A New UI system: GGUI
caution
GGUI is currently supported on the x64 and CUDA backends for the system Windows and Linux.
You also need to install the Vulkan environment: https://vulkan.lunarg.com/sdk/home.
A new UI system has been added to Taichi in version v0.8.0
. The new GUI system uses GPU for rendering, enabling it to be much faster and to render 3d scenes. For these reasons, this new system is sometimes referred to as GGUI. This doc describes the APIs provided.
Apart from this doc, a good way of getting familiarized with GGUI is to look at the examples. Please checkout the examples provided in examples/ggui_examples
.
Creating a window
ti.ui.Window(name, res)
creates a window.
window = ti.ui.Window('Window Title', (640, 360))
There are three types of objects that can be displayed on a ti.ui.Window
:
- 2D Canvas, which can be used to draw simple 2D geometries such as circles, triangles, etc.
- 3D Scene, which can be used to render 3D meshes and particles, with a configurable camera and light sources.
- Immediate mode GUI components, e.g., buttons, textboxes, etc.
2D Canvas
Creating a canvas
canvas = window.get_canvas()
this retrieves a Canvas
object that covers the entire window.
Drawing on the canvas
canvas.set_background_color(color)
canvas.triangles(vertices, color, indices, per_vertex_color)
canvas.circles(vertices, radius, color, per_vertex_color)
canvas.lines(vertices, width, indices, color, per_vertex_color)
canvas.set_image(image)
The arguments vertices
, indices
, per_vertex_color
, and image
are all expected to be Taichi fields. If per_vertex_color
is provided, color
will be ignored.
The positions/centers of geometries will be represented as floats between 0 and 1, which indicate relative positions on the canvas. For circles
and lines
, the radius
and width
arguments are relative to the height of the window.
The canvas will be cleared after every frame. You should call these methods within the render loop.
3D Scene
Creating a scene
scene = ti.ui.Scene()
Configuring camera
camera = ti.ui.make_camera()
camera.position(pos)
camera.lookat(pos)
camera.up(dir)
camera.projection_mode(mode)
scene.set_camera(camera)
Configuring light sources
Adding a point light
scene.point_light(pos, color)
Note that point_light
method needs to be called every frame. Similar to the canvas
methods, you should call this within your render loop.
3d Geometries
scene.mesh(vertices, indices, normals, color, per_vertex_color)
scene.particles(vertices, radius, color, per_vertex_color)
The arguments vertices
, indices
, per_vertex_color
, and image
are all expected to be Taichi fields. If per_vertex_color
is provided, color
will be ignored.
The positions/centers of geometries should be in the world-space coordinates.
note
If a mesh has num
triangles, the indices
should be a 1D scalar field with a shape of num * 3
instead of a vector field.
The normals
parameter for scene.mesh
is optional.
Rendering the scene
A scene can be rendered on a canvas.
canvas.scene(scene)
GUI components
The support for GUI components will closely follow the Dear IMGUI APIs:
window.GUI.begin(name, x, y, width, height)
window.GUI.text(text)
is_clicked = window.GUI.button(name)
new_value = window.GUI.slider_float(name, old_value, min_value, max_value)
new_color = window.GUI.color_edit_3(name, old_color)
window.GUI.end()
Showing a window
...
window.show()
Call this method at the very end of the render loop for each frame.
User Input Processing
To obtain the events that have occurred since the previous poll:
events = window.get_events()
Each event
in events
is an instance of ti.ui.Event
. It has the following properties:
event.action
, which could beti.ui.PRESS
,ti.ui.RELEASE
, ...event.key
, which indicates the key related to this event
To obtain the mouse position:
window.get_cursor_pos()
To check if a specific key is currently pressed:
window.is_pressed(key)
Here is an input processing example in GGUI version mpm128
:
while window.running:
# keyboard event processing
if window.get_event(ti.ui.PRESS):
if window.event.key == 'r': reset()
elif window.event.key in [ti.ui.ESCAPE]: break
if window.event is not None: gravity[None] = [0, 0] # if had any event
if window.is_pressed(ti.ui.LEFT, 'a'): gravity[None][0] = -1
if window.is_pressed(ti.ui.RIGHT, 'd'): gravity[None][0] = 1
if window.is_pressed(ti.ui.UP, 'w'): gravity[None][1] = 1
if window.is_pressed(ti.ui.DOWN, 's'): gravity[None][1] = -1
# mouse event processing
mouse = window.get_cursor_pos()
# ...
if window.is_pressed(ti.ui.LMB):
attractor_strength[None] = 1
if window.is_pressed(ti.ui.RMB):
attractor_strength[None] = -1
Image I/O
To write the current screen content into an image file:
window.write_image(filename)
Notice that, when the window is showing, you have to call window.write_image()
before the window.show()
call.
Off-screen rendering
GGUI supports rendering contents off-screen, that is, writing the results into image files without showing the window at all. This is sometimes referred to as "headless" rendering. To enable this mode, initialize the window with the argument show_window=False
:
window = ti.ui.Window('Window Title', (640, 360), show_window = False)
Then, you can use window.write_image()
as normal, and remove the window.show()
call at the end.