Skip to content

viewers.interactive_handle_subspace_viewer

interactive_handle_subspace_viewer

Viewer for interactive affine handle app. This viewer updates the draw call by sending the full space mesh and rig parameters to the GPU, and then does the full space projection

Parameters:

Name Type Description Default
V (n, 3) float numpy array

vertex positions

required
T (f, 3) int numpy array

triangle indices

required
guizmo_callback function

callback function for guizmo widget

required
pre_draw_callback function

callback function for pre-draw step

required
texture_png str

path to texture png file

None
texture_obj str

path to texture obj file

None
t0 (3,) float numpy array

initial mesh translation (to align the textured mesh with)

None
s0 float

initial mesh scale (to align the textured mesh with)

None
init_guizmo bool

whether to initialize the guizmo widget

True
max_fps int

maximum fps for the viewer

60

Examples:

Simple script for using the viewer in a CD App

import fast_cody as fcd
import numpy as np
num_modes = 16
num_clusters = 120
[V, F, T] = fcd.read_msh(fcd.get_data("cd_fish.msh"))
Wp = np.ones((V.shape[0], 1))
J = fcd.lbs_jacobian(V, Wp)
C = fcd.complementary_constraint_matrix(V, T, J, dt=1e-3)
Cd = fcd.lbs_weight_space_constraint(V, C)
[B, l, Ws] =fcd.skinning_subspace(V, T, num_modes,  num_clusters, C=Cd)
sim = fcd.fast_cd_sim(V, T, B, l, J, mu=1e4)
z0 = np.zeros((num_modes*12, 1))
T0 = np.identity(4);
p0 = T0[0:3, :].reshape((-1, 1))
st = fcd.fast_cd_state(z0, p0)
step = 0
def pre_draw_callback():
     global step
     p = viewer.T0[0:3, :].reshape((-1, 1))
     z = sim.step( p, st)
     st.update(z, p)
     viewer.update_subspace_coefficients(z, p)
     step += 1
viewer = fcd.viewers.interactive_handle_subspace_viewer(V, T, Wp, Ws,  pre_draw_callback)
viewer.launch()
Source code in src\fast_cody\viewers\interactive_handle_subspace_viewer.py
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
class interactive_handle_subspace_viewer():
    """
    Viewer for interactive affine handle app. This viewer updates the draw call by sending
    the full space mesh and rig parameters to the GPU, and then does the full space projection

    Parameters
    ----------
    V : (n, 3) float numpy array
        vertex positions
    T : (f, 3) int numpy array
        triangle indices
    guizmo_callback : function
        callback function for guizmo widget
    pre_draw_callback : function
        callback function for pre-draw step
    texture_png : str
        path to texture png file
    texture_obj : str
        path to texture obj file
    t0 : (3,) float numpy array
        initial mesh translation (to align the textured mesh with)
    s0 : float
        initial mesh scale (to align the textured mesh with)
    init_guizmo : bool
        whether to initialize the guizmo widget
    max_fps : int
        maximum fps for the viewer



    Examples
    --------
    Simple script for using the viewer in a CD App
    ```
    import fast_cody as fcd
    import numpy as np
    num_modes = 16
    num_clusters = 120
    [V, F, T] = fcd.read_msh(fcd.get_data("cd_fish.msh"))
    Wp = np.ones((V.shape[0], 1))
    J = fcd.lbs_jacobian(V, Wp)
    C = fcd.complementary_constraint_matrix(V, T, J, dt=1e-3)
    Cd = fcd.lbs_weight_space_constraint(V, C)
    [B, l, Ws] =fcd.skinning_subspace(V, T, num_modes,  num_clusters, C=Cd)
    sim = fcd.fast_cd_sim(V, T, B, l, J, mu=1e4)
    z0 = np.zeros((num_modes*12, 1))
    T0 = np.identity(4);
    p0 = T0[0:3, :].reshape((-1, 1))
    st = fcd.fast_cd_state(z0, p0)
    step = 0
    def pre_draw_callback():
         global step
         p = viewer.T0[0:3, :].reshape((-1, 1))
         z = sim.step( p, st)
         st.update(z, p)
         viewer.update_subspace_coefficients(z, p)
         step += 1
    viewer = fcd.viewers.interactive_handle_subspace_viewer(V, T, Wp, Ws,  pre_draw_callback)
    viewer.launch()
    ```
    """

    def __init__(self, V, T, Wp, Ws,  pre_draw_callback,T0=None,
                 texture_png=None, texture_obj=None, t0=None, s0=None, init_guizmo=True, max_fps=60):
        vertex_shader_path = fast_cody.get_shader("./vertex_shader_16.glsl")
        fragment_shader_path = fast_cody.get_shader("./fragment_shader.glsl")

        viewer = fcd.fast_cd_viewer_custom_shader(vertex_shader_path,
                                                  fragment_shader_path, 16, 16)
        print("  c        Toggle Secondary Motion")

        print("  g        Toggle Guizmo Widget Transform")

        # TODO q for quit
        F = igl.boundary_facets(T)
        vis_texture = False
        if texture_png is not None and texture_obj is not None:
            vis_texture = True
        self.vis_texture = vis_texture

        self.max_fps = max_fps
        if not vis_texture:
            viewer.set_mesh(V, F, 0)
            viewer.invert_normals(True, 0)
            color = np.array([144, 210, 236]) / 255.0
            viewer.set_color(color, 0)
            self.V = V
            self.Wp =  Wp;  # primary rig weights
            self.Ws =  Ws;  # secondary subspace weights
            viewer.set_weights(self.Wp, self.Ws, 0)

        if vis_texture:
            [Vf, TC, N, Ff, FTC, FN] = fcd.readOBJ_tex(texture_obj)
            if s0 is not None:
                Vf = Vf * s0
            if t0 is not None:
                Vf = Vf - t0

            P = fcd.prolongation(Vf, V, T)
            Pe = sp.sparse.kron(sp.sparse.identity(3), P )
            viewer.set_mesh(Vf, Ff, 0)
            viewer.set_texture(texture_png, TC, FTC, 0)
            self.Wp_tex = P @ Wp;  # primary rig weights
            self.Ws_tex = P @ Ws;  # secondary subspace weights
            viewer.set_weights(self.Wp_tex, self.Ws_tex, 0)

            viewer.set_show_lines(False, 0)
            viewer.set_face_based(False, 0)
            self.V = Vf
            self.Pe = Pe


        self.init_guizmo = init_guizmo
        if init_guizmo:
            if T0 is None:
                T0 = np.identity(4).astype( dtype=np.float32, order="F");
            self.T0 = T0
            self.transform = "translate"
            viewer.init_guizmo(True, T0, self.guizmo_callback, self.transform)

        viewer.set_pre_draw_callback(pre_draw_callback)

        viewer.set_key_callback(self.callback_key_pressed)

        self.viewer = viewer

        self.vis_cd = True

    def guizmo_callback(self, A):
        self.T0 = A

    def callback_key_pressed(s, key, modifier):
        if (key == ord('g') or key == ord('G')):
            if (s.init_guizmo):
                if (s.transform == "translate"):
                    s.transform = "rotate"
                elif (s.transform == "rotate"):
                    s.transform = "scale"
                elif (s.transform == "scale"):
                    s.transform = "translate"

                s.viewer.change_guizmo_op(s.transform)
            else:
                print("Guizmo not initialized, pass init_guizmo=True to the viewer constructor")
        if (key == ord('c') or key==ord('C') ):
            s.vis_cd = not s.vis_cd
        return False
    def launch(self):
        self.viewer.launch(self.max_fps, True)

    def change_guizmo_op(self, op):
        self.viewer.change_guizmo_op(op)
    def update_subspace_coefficients(self, z, p):
        self.viewer.set_bone_transforms(p, z * self.vis_cd, 0);
        self.viewer.updateGL(0)