OUTPUT BUFFER:
#----------------------------------------------------------------------------- # Name: ogl_cg_skinning.cpp ogl_glslang_skinning.cpp # Author: Kevin Harris (kevin@codesampler.com) # Last Modified: 04/28/05 # Description: This sample demonstrates how to skin a mesh on the hardware # using a Cg or GLSL shader. To keep things simple, the skeletal # system used in this sample is very simple and only consists # of two bones or bone matrices. # # Special thanks go out to Cyril Zeller, and Matthias Wloka # of nVIDIA for their help in straightening out a few oddities # that my sample was suffering from. In short, Cg works fine # and I'm occasionally a big dummy! ;) # # Control Keys: Left Mouse Button - Spin the matrix for bone0. # Right Mouse Button - Spin the matrix for bone1. # # F1 - Toggle test geometry between a cylinder and a simple # grouping of 3 quads. # F2 - Toggle wire-frame mode #----------------------------------------------------------------------------- # # Original C++ code by Kevin Harris (kevin@codesampler.com) # See www.codesampler.com for the original files # OpenGL samples page 11: Matrix Palette Skinning on the Hardware # # Modified for Tcl3D by Paul Obermeier 2005/11/05 # See www.tcl3d.org for the Tcl3D extension. # # This sample integrates Cg and GLSL code into one file. # If called with no command line arguments, it uses the Cg shader. # Use "glsl" as parameter to use the GLSL shader. package require Tk package require Img package require tcl3d 0.5.0 # Font to be used in the Tk listbox. set g_listFont {-family {Courier} -size 10} set g_WinWidth 640 set g_WinHeight 480 set g_LastMousePosX(1) 0 set g_LastMousePosY(1) 0 set g_LastMousePosX(2) 0 set g_LastMousePosY(2) 0 set g_bRenderInWireFrame 0 set g_bRenderQuads 0 set g_bUseCgShader 1 set g_bAnimStarted 0 set g_fDistance -12.0 set g_fSpinX(1) 0.0 set g_fSpinY(1) 0.0 set g_fSpinX(2) 0.0 set g_fSpinY(2) 0.0 set g_boneMatrix0 [tcl3dVector GLfloat 16] set g_boneMatrix1 [tcl3dVector GLfloat 16] set g_matrixToRenderBone0 [tcl3dVector GLfloat 16] set g_matrixToRenderBone1 [tcl3dVector GLfloat 16] set R 0 set G 1 set B 2 set A 3 set NX 4 set NY 5 set NZ 6 set X 7 set Y 8 set Z 9 set W0 10 set W1 11 set MI0 12 set MI1 13 set NB 14 set SIZE 15 # Cylinder geometry set CYLINDER_RESOLUTION 12 set NUM_CYLINDER_SECTIONS 6 set NUM_VERTICES_PER_SECTION [expr $CYLINDER_RESOLUTION * 2] set NUM_VERTICES [expr $NUM_VERTICES_PER_SECTION * $NUM_CYLINDER_SECTIONS] set g_cylinderVertices [tcl3dVector GLfloat [expr $NUM_VERTICES * $SIZE]] # Quad geometry # wx: Weights of bones # mix: Matrix indices # nb: Number of bones # r g b a nx ny nz x y z w0 w1 mi0 mi1 nb set g_quadVertices [tcl3dVectorFromArgs GLfloat \ 1.0 1.0 0.0 1.0 0.0 0.0 1.0 -1.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0 \ 1.0 1.0 0.0 1.0 0.0 0.0 1.0 1.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0 \ 1.0 1.0 0.0 1.0 0.0 0.0 1.0 1.0 2.0 0.0 0.5 0.5 0.0 1.0 2.0 \ 1.0 1.0 0.0 1.0 0.0 0.0 1.0 -1.0 2.0 0.0 0.5 0.5 0.0 1.0 2.0 \ 0.0 1.0 0.0 1.0 0.0 0.0 1.0 -1.0 2.0 0.0 0.5 0.5 0.0 1.0 2.0 \ 0.0 1.0 0.0 1.0 0.0 0.0 1.0 1.0 2.0 0.0 0.5 0.5 0.0 1.0 2.0 \ 0.0 1.0 0.0 1.0 0.0 0.0 1.0 1.0 4.0 0.0 0.5 0.5 0.0 1.0 2.0 \ 0.0 1.0 0.0 1.0 0.0 0.0 1.0 -1.0 4.0 0.0 0.5 0.5 0.0 1.0 2.0 \ 0.0 0.0 1.0 1.0 0.0 0.0 1.0 -1.0 4.0 0.0 0.5 0.5 0.0 1.0 2.0 \ 0.0 0.0 1.0 1.0 0.0 0.0 1.0 1.0 4.0 0.0 0.5 0.5 0.0 1.0 2.0 \ 0.0 0.0 1.0 1.0 0.0 0.0 1.0 1.0 6.0 0.0 1.0 0.0 1.0 0.0 1.0 \ 0.0 0.0 1.0 1.0 0.0 0.0 1.0 -1.0 6.0 0.0 1.0 0.0 1.0 0.0 1.0] set g_StopWatch [tcl3dNewSwatch] tcl3dStartSwatch $g_StopWatch # Determine the directory of this script. set g_scriptDir [file dirname [info script]] # Show errors occuring in the Togl callbacks. proc bgerror { msg } { tk_messageBox -icon error -type ok -message "Error: $msg\n\n$::errorInfo" exit } # Print info message into widget a the bottom of the window. proc PrintInfo { msg } { if { [winfo exists .fr.info] } { .fr.info configure -text $msg } } proc ToggleWireframe { toglwin } { set ::g_bRenderInWireFrame [expr 1 - $::g_bRenderInWireFrame] if { $::g_bRenderInWireFrame } { glPolygonMode GL_FRONT_AND_BACK GL_LINE } else { glPolygonMode GL_FRONT_AND_BACK GL_FILL } $toglwin postredisplay } proc ToggleRenderQuads { toglwin } { set ::g_bRenderQuads [expr 1 - $::g_bRenderQuads] $toglwin postredisplay } proc SetDistance { toglwin off } { set ::g_fDistance [expr $::g_fDistance + $off] $toglwin postredisplay } proc SetMouseInput { btn x y } { set ::g_LastMousePosX($btn) $x set ::g_LastMousePosY($btn) $y } proc GetMouseInput { btn x y } { set nXDiff [expr ($x - $::g_LastMousePosX($btn))] set nYDiff [expr ($y - $::g_LastMousePosY($btn))] set ::g_fSpinX($btn) [expr $::g_fSpinX($btn) - $nXDiff] set ::g_fSpinY($btn) [expr $::g_fSpinY($btn) - $nYDiff] set ::g_LastMousePosX($btn) $x set ::g_LastMousePosY($btn) $y .fr.toglwin postredisplay } proc ReshapeCallback { toglwin { w -1 } { h -1 } } { set w [$toglwin width] set h [$toglwin height] glViewport 0 0 $w $h glMatrixMode GL_PROJECTION glLoadIdentity gluPerspective 45.0 [expr double($w)/double($h)] 0.1 1000.0 } proc CreateCallback { toglwin } { glEnable GL_DEPTH_TEST glEnable GL_LIGHTING glClearColor 0.35 0.53 0.7 1.0 # Set up a material set ambient_mtrl { 0.2 0.2 0.2 1.0 } set diffuse_mtrl { 1.0 1.0 1.0 1.0 } glMaterialfv GL_FRONT GL_AMBIENT $ambient_mtrl glMaterialfv GL_FRONT GL_DIFFUSE $diffuse_mtrl # Set light 0 to be a pure white directional light set diffuse_light0 { 1.0 1.0 1.0 1.0 } set ambient_light0 { 0.0 0.0 0.0 1.0 } set position_light0 { 0.0 0.0 0.0 1.0 } glLightfv GL_LIGHT0 GL_DIFFUSE $diffuse_light0 glLightfv GL_LIGHT0 GL_AMBIENT $ambient_light0 glLightfv GL_LIGHT0 GL_POSITION $position_light0 glEnable GL_LIGHT0 # Enable some dim, grey ambient lighting so objects that are not lit # by the other lights are not completely black. set ambient_lightModel { 0.2 0.2 0.2 0.2 } glLightModelfv GL_LIGHT_MODEL_AMBIENT $ambient_lightModel glMatrixMode GL_PROJECTION glLoadIdentity gluPerspective 45.0 [expr double($::g_WinWidth)/double($::g_WinHeight)] \ 0.1 100.0 # Create a piece of geometry to represent a single bone, which is 3.0f # units in length... set ::g_boneDisplayList [glGenLists 1] glNewList $::g_boneDisplayList GL_COMPILE glBegin GL_LINE_STRIP glVertex3f 0.0 0.0 0.0 glVertex3f -0.2 0.2 -0.2 glVertex3f 0.2 0.2 -0.2 glVertex3f 0.0 3.0 0.0 ; # Bone length = 3.0f glVertex3f -0.2 0.2 -0.2 glVertex3f -0.2 0.2 0.2 glVertex3f 0.0 0.0 0.0 glVertex3f 0.2 0.2 -0.2 glVertex3f 0.2 0.2 0.2 glVertex3f 0.0 0.0 0.0 glVertex3f -0.2 0.2 0.2 glVertex3f 0.0 3.0 0.0 ; # Bone length = 3.0f glVertex3f 0.2 0.2 0.2 glVertex3f -0.2 0.2 0.2 glEnd glEndList # Create the geometry for a cylinder... createMeshCylinderWithWeightedVertices if { $::g_bUseCgShader } { InitCgShader } else { InitGLSLShader } } proc createMeshCylinderWithWeightedVertices {} { set fTwoTimesPI [expr 2.0 * 3.141592654] # Create the geometry for a cylinder mesh... for {set j 0 } { $j < $::NUM_CYLINDER_SECTIONS } { incr j } { set nIndexOffset [expr $j * $::NUM_VERTICES_PER_SECTION] for {set i 0 } { $i < $::CYLINDER_RESOLUTION } { incr i } { set fTheta [expr ($fTwoTimesPI * $i) / ($::CYLINDER_RESOLUTION - 1)] # Top of cylinder section... set nIndex [expr (2*$i+0) + $nIndexOffset] $::g_cylinderVertices set [expr $nIndex*$::SIZE + $::X] [expr sin($fTheta)] $::g_cylinderVertices set [expr $nIndex*$::SIZE + $::Y] [expr $j + 1.0] $::g_cylinderVertices set [expr $nIndex*$::SIZE + $::Z] [expr cos($fTheta)] $::g_cylinderVertices set [expr $nIndex*$::SIZE + $::NX] [expr sin($fTheta)] $::g_cylinderVertices set [expr $nIndex*$::SIZE + $::NY] 0.0 $::g_cylinderVertices set [expr $nIndex*$::SIZE + $::NZ] [expr cos($fTheta)] # Bottom of cylinder section... set nIndex [expr (2*$i+1) + $nIndexOffset] $::g_cylinderVertices set [expr $nIndex*$::SIZE + $::X] [expr sin($fTheta)] $::g_cylinderVertices set [expr $nIndex*$::SIZE + $::Y] $j $::g_cylinderVertices set [expr $nIndex*$::SIZE + $::Z] [expr cos($fTheta)] $::g_cylinderVertices set [expr $nIndex*$::SIZE + $::NX] [expr sin($fTheta)] $::g_cylinderVertices set [expr $nIndex*$::SIZE + $::NY] 0.0 $::g_cylinderVertices set [expr $nIndex*$::SIZE + $::NZ] [expr cos($fTheta)] } } # Next, assign skinning information to the cylinder's vertex data... # # The cylinder was built up along the Y axis, so if we vary the # vertex weights based on its Y axis value, we can make it bend like # a plastic pipe. In a real-life application, the weights would most # likely be assigned to each vertex through a modeling tool like Maya or # 3D Studio Max, which is far more intuitive to work with. for {set i 0 } { $i < $::NUM_VERTICES } { incr i } { # Zero out these to prevent bad data from sneaking in... $::g_cylinderVertices set [expr $i*$::SIZE + $::NB] 0.0 $::g_cylinderVertices set [expr $i*$::SIZE + $::MI0] 0.0 $::g_cylinderVertices set [expr $i*$::SIZE + $::MI1] 0.0 $::g_cylinderVertices set [expr $i*$::SIZE + $::W0] 0.0 $::g_cylinderVertices set [expr $i*$::SIZE + $::W1] 0.0 # Now, vary the weights for the current vertex based on its # Y axis value... if { [$::g_cylinderVertices get [expr $i*$::SIZE + $::Y]] < 3.0 } { # The vertex is in the lower half, which is closet to bone0. # Number of bones that effect this vertex = 1 # Index of bone matrix that effects it = 0 # Influence from that bone matrix = 100% $::g_cylinderVertices set [expr $i*$::SIZE + $::NB] 1.0 $::g_cylinderVertices set [expr $i*$::SIZE + $::MI0] 0.0 $::g_cylinderVertices set [expr $i*$::SIZE + $::W0] 1.0 # Make the vertex yellow as a visual reference... $::g_cylinderVertices set [expr $i*$::SIZE + $::R] 1.0 $::g_cylinderVertices set [expr $i*$::SIZE + $::G] 1.0 $::g_cylinderVertices set [expr $i*$::SIZE + $::B] 0.0 $::g_cylinderVertices set [expr $i*$::SIZE + $::A] 1.0 } elseif { [$::g_cylinderVertices get [expr $i*$::SIZE + $::Y]] == 3.0 } { # The vertex is in the middle, which is equal distance from # bone0 and bone1. # Number of bones that effect this vertex = 2 # Index of the first bone matrix that effects it = 0 # Index of the second bone matrix that effects it = 1 # Influence from the first bone matrix = 50% # Influence from the second bone matrix = 50% $::g_cylinderVertices set [expr $i*$::SIZE + $::NB] 2.0 $::g_cylinderVertices set [expr $i*$::SIZE + $::MI0] 0.0 $::g_cylinderVertices set [expr $i*$::SIZE + $::MI1] 1.0 $::g_cylinderVertices set [expr $i*$::SIZE + $::W0] 0.5 $::g_cylinderVertices set [expr $i*$::SIZE + $::W1] 0.5 # Make the vertex green as a visual reference... $::g_cylinderVertices set [expr $i*$::SIZE + $::R] 0.0 $::g_cylinderVertices set [expr $i*$::SIZE + $::G] 1.0 $::g_cylinderVertices set [expr $i*$::SIZE + $::B] 0.0 $::g_cylinderVertices set [expr $i*$::SIZE + $::A] 1.0 } elseif { [$::g_cylinderVertices get [expr $i*$::SIZE + $::Y]] > 3.0 } { # The vertex is in the upper half, which is closet to bone1. # Number of bones that effect this vertex = 1 # Index of bone matrix that effects it = 1 # Influence from that bone matrix = 100% $::g_cylinderVertices set [expr $i*$::SIZE + $::NB] 2.0 $::g_cylinderVertices set [expr $i*$::SIZE + $::MI0] 0.0 $::g_cylinderVertices set [expr $i*$::SIZE + $::MI1] 1.0 $::g_cylinderVertices set [expr $i*$::SIZE + $::W0] 0.0 $::g_cylinderVertices set [expr $i*$::SIZE + $::W1] 1.0 # Make the vertex blue as a visual reference... $::g_cylinderVertices set [expr $i*$::SIZE + $::R] 0.0 $::g_cylinderVertices set [expr $i*$::SIZE + $::G] 0.0 $::g_cylinderVertices set [expr $i*$::SIZE + $::B] 1.0 $::g_cylinderVertices set [expr $i*$::SIZE + $::A] 1.0 } } } proc ReadShaderFile { fileName } { set pathName [file join $::g_scriptDir $fileName] set realPath [tcl3dGetExtFile $pathName] set retVal [catch {open $realPath r} fp] if { $retVal == 0 } { set buffer [read $fp] close $fp } else { error "Cannot open shader file $realPath" } return $buffer } proc InitGLSLShader {} { if { ![tcl3dOglHaveExtension "GL_ARB_shading_language_100"] } { error "Extension GL_ARB_shading_language_100 missing" } if { ![tcl3dOglHaveExtension "GL_ARB_shader_objects"] } { error "Extension GL_ARB_shader_objects missing" } if { ![tcl3dOglHaveExtension "GL_ARB_vertex_shader"] } { error "Extension GL_ARB_vertex_shader missing" } set bLinked [tcl3dVector GLint 1] set bVertCompiled [tcl3dVector GLint 1] # Create the vertex shader... set ::g_vertexShader [glCreateShaderObjectARB GL_VERTEX_SHADER_ARB] set vertexShaderAssembly [ReadShaderFile "ogl_glslang_skinning_nvidia.vert"] #set vertexShaderAssembly [ReadShaderFile "ogl_glslang_skinning_ati.vert"] tcl3dOglShaderSource $::g_vertexShader $vertexShaderAssembly glCompileShaderARB $::g_vertexShader glGetObjectParameterivARB $::g_vertexShader GL_OBJECT_COMPILE_STATUS_ARB \ $bVertCompiled if { [$bVertCompiled get 0] == 0 } { set msg [tcl3dOglGetInfoLogARB $::g_vertexShader] error "Vertex Shader Compile Error: $msg" } # Create a program object and attach our skinning shader to it... set ::g_programObj [glCreateProgramObjectARB] glAttachObjectARB $::g_programObj $::g_vertexShader # Link the program object and print out the info log... glLinkProgramARB $::g_programObj glGetObjectParameterivARB $::g_programObj GL_OBJECT_LINK_STATUS_ARB $bLinked if { [$bLinked get 0] == 0 } { set msg [tcl3dOglGetInfoLogARB $::g_programObj] error "Linking Error: $msg" } # Locate some parameters by name so we can set them later... set ::g_param_inverseModelView [glGetUniformLocationARB $::g_programObj "inverseModelView"] set ::g_param_eyePosition [glGetUniformLocationARB $::g_programObj "eyePosition"] set ::g_param_lightVector [glGetUniformLocationARB $::g_programObj "lightVector"] set ::g_param_boneMatrices_0 [glGetUniformLocationARB $::g_programObj "boneMatrices\[0\]"] set ::g_param_boneMatrices_1 [glGetUniformLocationARB $::g_programObj "boneMatrices\[1\]"] set ::g_param_weights [glGetAttribLocationARB $::g_programObj "weights"] set ::g_param_matrixIndices [glGetAttribLocationARB $::g_programObj "matrixIndices"] set ::g_param_numBones [glGetAttribLocationARB $::g_programObj "numBones"] $bLinked delete $bVertCompiled delete } proc InitCgShader {} { # Search for a valid vertex shader profile in this order: # # CG_PROFILE_VP30 - GL_NV_vertex_program2 (support for loops) # CG_PROFILE_ARBVP1 - GL_ARB_vertex_program (no support for loops) # CG_PROFILE_VP20 - GL_NV_vertex_program set ::g_CGprofile [tcl3dCgFindProfile $::CG_PROFILE_VP30 \ $::CG_PROFILE_ARBVP1 \ $::CG_PROFILE_VP20] if { $::g_CGprofile == "" } { error "Failed to initialize vertex shader. No VP30, ARBVP1 or VP20 profile available." } tcl3dCgResetError # Create a CG context set ::g_CGcontext [cgCreateContext] # Add the Vertex shader to the context set cgFile [tcl3dGetExtFile [file join $::g_scriptDir "ogl_cg_skinning.cg"]] set ::g_CGprogram [cgCreateProgramFromFile $::g_CGcontext \ CG_SOURCE $cgFile \ $::g_CGprofile "main" "NULL"] set retVal [tcl3dCgGetError $::g_CGcontext] if { $retVal ne "" } { error "cgCreateProgramFromFile: $retVal" } # Load the program using Cg's expanded interface... cgGLLoadProgram $::g_CGprogram set retVal [tcl3dCgGetError $::g_CGcontext] if { $retVal ne "" } { error "cgGLLoadProgram: $retVal" } # Bind some parameters by name so we can set them later... set ::g_param_modelViewProjection [cgGetNamedParameter $::g_CGprogram "modelViewProjection"] set ::g_param_modelViewInverse [cgGetNamedParameter $::g_CGprogram "modelViewInverse"] set ::g_param_boneMatrices [cgGetNamedParameter $::g_CGprogram "boneMatrices"] set ::g_param_eyePosition [cgGetNamedParameter $::g_CGprogram "eyePosition"] set ::g_param_lightVector [cgGetNamedParameter $::g_CGprogram "lightVector"] # These vary on a per-vertex basis... set ::g_param_weights [cgGetNamedParameter $::g_CGprogram "IN.weights"] set ::g_param_matrixIndices [cgGetNamedParameter $::g_CGprogram "IN.matrixIndices"] set ::g_param_numBones [cgGetNamedParameter $::g_CGprogram "IN.numBones"] } proc SetShaderConstants {} { if { $::g_bUseCgShader } { glMatrixMode GL_MODELVIEW glLoadIdentity glTranslatef 0.0 -3.0 $::g_fDistance # Track the combined model-view-projection matrix cgGLSetStateMatrixParameter $::g_param_modelViewProjection \ CG_GL_MODELVIEW_PROJECTION_MATRIX \ CG_GL_MATRIX_IDENTITY # This matrix will be used to transform the normals from model-space to view-space cgGLSetStateMatrixParameter $::g_param_modelViewInverse \ CG_GL_MODELVIEW_MATRIX \ CG_GL_MATRIX_INVERSE_TRANSPOSE } # Set the matrix for bone0... # The matrix for bone0 is fairly simple since it's the first bone in the # hierarchy. To liven things up a bit, we'll have bone0 slowly translate # left and right. This will help to demonstrate how bone1, which is attached # to the end of bone0, moves in relation to it. tcl3dMatfIdentity $::g_boneMatrix0 tcl3dMatfIdentity $::g_matrixToRenderBone0 set tmpMat [tcl3dVector GLfloat 16] set translationMatrixX [tcl3dVector GLfloat 16] set rotationMatrixY [tcl3dVector GLfloat 16] set rotationMatrixZ [tcl3dVector GLfloat 16] set boneRotationMatrix [tcl3dVector GLfloat 16] set transposeOfBoneMatrix0 [tcl3dVector GLfloat 16] # As a demonstration, translate bone0 left and right... set fTime [tcl3dLookupSwatch $::g_StopWatch] tcl3dMatfTranslate [expr sin($fTime)] 0 0 $translationMatrixX # Build up a rotation matrix for bone0... tcl3dMatfRotateY $::g_fSpinY(1) $rotationMatrixY tcl3dMatfRotateZ [expr -1.0 * $::g_fSpinX(1)] $rotationMatrixZ tcl3dMatfMult $rotationMatrixY $rotationMatrixZ $boneRotationMatrix tcl3dMatfMult $boneRotationMatrix $translationMatrixX $::g_boneMatrix0 # Cache what we have so far so we can render the graphical representation # of bone0 correctly. This is just for the demo and is not required for # mesh skinning. tcl3dMatfCopy $::g_boneMatrix0 $::g_matrixToRenderBone0 tcl3dMatfTranspose $::g_boneMatrix0 $transposeOfBoneMatrix0 if { $::g_bUseCgShader } { set transposeMatrixAsList [tcl3dVectorToList $transposeOfBoneMatrix0 16] set param_currentMatrix [cgGetArrayParameter $::g_param_boneMatrices 0] cgGLSetMatrixParameterfr $param_currentMatrix $transposeMatrixAsList } else { if { $::g_param_boneMatrices_0 != -1 } { set transposeMatrixAsList [tcl3dVectorToList $::g_boneMatrix0 16] glUniformMatrix4fvARB $::g_param_boneMatrices_0 1 0 $transposeMatrixAsList } } # Set the matrix for bone1... # # The setup for bone1 is more complicated than bone0. This is for two # reasons. # # First, bone0 and bone1 are supposed to be connected at a joint, # so we need to make sure that bone1 follows along with any translation # and/or rotation that bone0 performs. If we don't, the mesh will be # stretched out as the bones move apart. # # Secondly, bone1 performs its rotation at the tip-end of bone0, so we # need to take into account the length of bone0 (3.0 units along Y) when # rotating bone1, or the vertices weighted to respond to bone1 will get # rotated strangely. When it comes to mesh skinning, this is probably the # hardest part to understand. tcl3dMatfIdentity $::g_boneMatrix1 tcl3dMatfIdentity $::g_matrixToRenderBone1 set offsetMatrix_toBoneEnd [tcl3dVector GLfloat 16] set offsetMatrix_backFromBoneEnd [tcl3dVector GLfloat 16] set transposeOfBoneMatrix1 [tcl3dVector GLfloat 16] # Build up two offset matrices for bone1... tcl3dMatfTranslate 0.0 3.0 0.0 $offsetMatrix_toBoneEnd tcl3dMatfTranslate 0.0 -3.0 0.0 $offsetMatrix_backFromBoneEnd # Build up a rotation matrix for bone1... tcl3dMatfRotateY $::g_fSpinY(2) $rotationMatrixY tcl3dMatfRotateZ [expr -1.0 * $::g_fSpinX(2)] $rotationMatrixZ tcl3dMatfMult $rotationMatrixY $rotationMatrixZ $boneRotationMatrix # g_boneMatrix0 = Move in relation to bone0 # offsetMatrix_toBoneEnd = Offset to the end of bone0 # boneRotationMatrix = Apply rotation of bone1 tcl3dMatfMult $::g_boneMatrix0 $offsetMatrix_toBoneEnd $tmpMat tcl3dMatfMult $tmpMat $boneRotationMatrix $::g_boneMatrix1 # Cache what we have so far so we can render the graphical representation # of bone1 correctly. This is just for the demo and is not required for mesh skinning. tcl3dMatfCopy $::g_boneMatrix1 $::g_matrixToRenderBone1 # Once rotated, undo the translation offset so it doesn't translate the # actual vertices... tcl3dMatfMult $::g_matrixToRenderBone1 $offsetMatrix_backFromBoneEnd $::g_boneMatrix1 tcl3dMatfTranspose $::g_boneMatrix1 $transposeOfBoneMatrix1 if { $::g_bUseCgShader } { set param_currentMatrix [cgGetArrayParameter $::g_param_boneMatrices 1] set transposeMatrixAsList [tcl3dVectorToList $transposeOfBoneMatrix1 16] cgGLSetMatrixParameterfr $param_currentMatrix $transposeMatrixAsList } else { if { $::g_param_boneMatrices_1 != -1 } { set transposeMatrixAsList [tcl3dVectorToList $::g_boneMatrix1 16] glUniformMatrix4fvARB $::g_param_boneMatrices_1 1 0 $transposeMatrixAsList } } # Set up some lighting... set fEyePosition { 0.0 0.0 0.0 0.0 } set fLightVector { 0.0 0.0 -1.0 0.0 } # Normalize light vector set fLength [expr sqrt( [lindex $fLightVector 0]*[lindex $fLightVector 0] + \ [lindex $fLightVector 1]*[lindex $fLightVector 1] + \ [lindex $fLightVector 2]*[lindex $fLightVector 2])] lset fLightVector 0 [expr [lindex $fLightVector 0] / $fLength] lset fLightVector 1 [expr [lindex $fLightVector 1] / $fLength] lset fLightVector 2 [expr [lindex $fLightVector 2] / $fLength] if { $::g_bUseCgShader } { cgGLSetParameter4fv $::g_param_eyePosition $fEyePosition cgGLSetParameter4fv $::g_param_lightVector $fLightVector } else { # Set the light's directional vector if { $::g_param_lightVector != -1 } { glUniform4fARB $::g_param_lightVector [lindex $fLightVector 0] \ [lindex $fLightVector 1] [lindex $fLightVector 2] [lindex $fLightVector 3] } # Set the viewer's eye position if { $::g_param_eyePosition != -1 } { glUniform4fARB $::g_param_eyePosition [lindex $fEyePosition 0] \ [lindex $fEyePosition 1] [lindex $fEyePosition 2] [lindex $fEyePosition 3] } # Set the inverse of the current model-view matrix set modelView [tcl3dVector GLfloat 16] set inverseModelView [tcl3dVector GLfloat 16] glGetFloatv GL_MODELVIEW_MATRIX $modelView tcl3dMatfInvert $modelView $inverseModelView if { $::g_param_inverseModelView != -1 } { set inverseMatrixAsList [tcl3dVectorToList $inverseModelView 16] glUniformMatrix4fvARB $::g_param_inverseModelView 1 0 $inverseMatrixAsList } } $tmpMat delete $translationMatrixX delete $rotationMatrixY delete $rotationMatrixZ delete $boneRotationMatrix delete $transposeOfBoneMatrix0 delete $offsetMatrix_toBoneEnd delete $offsetMatrix_backFromBoneEnd delete $transposeOfBoneMatrix1 delete } proc DisplayCallback { toglwin } { glClear [expr $::GL_COLOR_BUFFER_BIT | $::GL_DEPTH_BUFFER_BIT] # Viewport command is not really needed, but has been inserted for # Mac OSX. Presentation framework (Tk) does not send a reshape event, # when switching from one demo to another. glViewport 0 0 [$toglwin width] [$toglwin height] if { $::g_bUseCgShader } { SetShaderConstants cgGLBindProgram $::g_CGprogram cgGLEnableProfile $::g_CGprofile } else { glMatrixMode GL_MODELVIEW glLoadIdentity glTranslatef 0.0 -3.0 $::g_fDistance ; # Move the view back some... glUseProgramObjectARB $::g_programObj SetShaderConstants } set fWeights { 0.0 0.0 0.0 0.0 } set fNumBones { 0.0 0.0 0.0 0.0 } set fMatrixIndices { 0.0 0.0 0.0 0.0 } if { $::g_bRenderQuads } { # Render three quads with weighted vertices and skin them using a Cg or GLSL shader # For each quad... for {set j 0 } { $j < 3 } { incr j } { set nIndexOffset [expr {$j * 4}] glBegin GL_QUADS # Render the quad's four vertices... for {set i 0 } { $i < 4 } { incr i } { set nIndex [expr {($i + $nIndexOffset) * $::SIZE}] lset fWeights 0 [$::g_quadVertices get [expr {$nIndex + $::W0}]] lset fWeights 1 [$::g_quadVertices get [expr {$nIndex + $::W1}]] if { $::g_bUseCgShader } { cgGLSetParameter4fv $::g_param_weights $fWeights } else { glVertexAttrib4fvARB $::g_param_weights $fWeights } lset fMatrixIndices 0 [$::g_quadVertices get [expr {$nIndex + $::MI0}]] lset fMatrixIndices 1 [$::g_quadVertices get [expr {$nIndex + $::MI1}]] if { $::g_bUseCgShader } { cgGLSetParameter4fv $::g_param_matrixIndices $fMatrixIndices } else { glVertexAttrib4fvARB $::g_param_matrixIndices $fMatrixIndices } # Store the number of bones in the x component of a 4 float array lset fNumBones 0 [$::g_quadVertices get [expr {$nIndex + $::NB}]] if { $::g_bUseCgShader } { cgGLSetParameter4fv $::g_param_numBones $fNumBones } else { glVertexAttrib4fvARB $::g_param_numBones $fNumBones } glColor4f \ [$::g_quadVertices get [expr {$nIndex + $::R}]] \ [$::g_quadVertices get [expr {$nIndex + $::G}]] \ [$::g_quadVertices get [expr {$nIndex + $::B}]] \ [$::g_quadVertices get [expr {$nIndex + $::A}]] glNormal3f \ [$::g_quadVertices get [expr {$nIndex + $::NX}]] \ [$::g_quadVertices get [expr {$nIndex + $::NY}]] \ [$::g_quadVertices get [expr {$nIndex + $::NZ}]] glVertex3f \ [$::g_quadVertices get [expr {$nIndex + $::X}]] \ [$::g_quadVertices get [expr {$nIndex + $::Y}]] \ [$::g_quadVertices get [expr {$nIndex + $::Z}]] } glEnd } } else { # Render a cylinder with weighted vertices and skin it using a Cg or GLSL shader # For each cylinder section... for {set j 0 } { $j < $::NUM_CYLINDER_SECTIONS } { incr j } { set nIndexOffset [expr {$j * $::NUM_VERTICES_PER_SECTION}] glBegin GL_TRIANGLE_STRIP # Render the section as a tri-strip... for {set i 0 } { $i < $::NUM_VERTICES_PER_SECTION } { incr i } { set nIndex [expr {($i + $nIndexOffset) * $::SIZE}] lset fWeights 0 [$::g_cylinderVertices get [expr {$nIndex + $::W0}]] lset fWeights 1 [$::g_cylinderVertices get [expr {$nIndex + $::W1}]] if { $::g_bUseCgShader } { cgGLSetParameter4fv $::g_param_weights $fWeights } else { glVertexAttrib4fvARB $::g_param_weights $fWeights } lset fMatrixIndices 0 [$::g_cylinderVertices get [expr {$nIndex + $::MI0}]] lset fMatrixIndices 1 [$::g_cylinderVertices get [expr {$nIndex + $::MI1}]] if { $::g_bUseCgShader } { cgGLSetParameter4fv $::g_param_matrixIndices $fMatrixIndices } else { glVertexAttrib4fvARB $::g_param_matrixIndices $fMatrixIndices } # Store the number of bones in the x component of a 4 float array lset fNumBones 0 [$::g_cylinderVertices get [expr {$nIndex + $::NB}]] if { $::g_bUseCgShader } { cgGLSetParameter4fv $::g_param_numBones $fNumBones } else { glVertexAttrib4fvARB $::g_param_numBones $fNumBones } glColor4f \ [$::g_cylinderVertices get [expr {$nIndex + $::R}]] \ [$::g_cylinderVertices get [expr {$nIndex + $::G}]] \ [$::g_cylinderVertices get [expr {$nIndex + $::B}]] \ [$::g_cylinderVertices get [expr {$nIndex + $::A}]] glNormal3f \ [$::g_cylinderVertices get [expr {$nIndex + $::NX}]] \ [$::g_cylinderVertices get [expr {$nIndex + $::NY}]] \ [$::g_cylinderVertices get [expr {$nIndex + $::NZ}]] glVertex3f \ [$::g_cylinderVertices get [expr {$nIndex + $::X}]] \ [$::g_cylinderVertices get [expr {$nIndex + $::Y}]] \ [$::g_cylinderVertices get [expr {$nIndex + $::Z}]] } glEnd } } if { $::g_bUseCgShader } { cgGLDisableProfile $::g_CGprofile } else { glUseProgramObjectARB 0 } # Render the graphical representations of our two bones for reference... glDisable GL_LIGHTING glPushMatrix set matrixAsList [tcl3dVectorToList $::g_matrixToRenderBone0 16] glMultMatrixf $matrixAsList glColor3f 1.0 1.0 0.0 ; # Bone0 will be green glCallList $::g_boneDisplayList glPopMatrix glPushMatrix set matrixAsList [tcl3dVectorToList $::g_matrixToRenderBone1 16] glMultMatrixf $matrixAsList glColor3f 0.0 0.0 1.0 ; # Bone1 will be blue glCallList $::g_boneDisplayList glPopMatrix glEnable GL_LIGHTING $toglwin swapbuffers } proc StartStopAnimation {} { if { $::g_bAnimStarted == 0 } { StartAnimation } else { StopAnimation } } proc StartAnimation {} { .fr.toglwin postredisplay set ::animId [tcl3dAfterIdle StartAnimation] set ::g_bAnimStarted 1 } proc StopAnimation {} { if { [info exists ::animId] } { after cancel $::animId unset ::animId } set ::g_bAnimStarted 0 } proc Cleanup {} { if { $::g_bUseCgShader } { if { [info exists ::g_CGprogram] } { cgDestroyProgram $::g_CGprogram } if { [info exists ::g_CGcontext] } { cgDestroyContext $::g_CGcontext } } else { if { [info exists ::g_vertexShader] } { glDeleteObjectARB $::g_vertexShader } if { [info exists ::g_programObj] } { glDeleteObjectARB $::g_programObj } } if { [info exists ::g_boneDisplayList] } { glDeleteLists $::g_boneDisplayList 0 } $::g_boneMatrix0 delete $::g_boneMatrix1 delete $::g_matrixToRenderBone0 delete $::g_matrixToRenderBone1 delete $::g_cylinderVertices delete $::g_quadVertices delete tcl3dDeleteSwatch $::g_StopWatch foreach var [info globals g_*] { uplevel #0 unset $var } } proc ExitProg {} { exit } if { $argc >= 1 } { if { [string compare -nocase [lindex $argv 0] "cg"] == 0 } { set ::g_bUseCgShader 1 } elseif { [string compare -nocase [lindex $argv 0] "glsl"] == 0 } { set ::g_bUseCgShader 0 } } if { $::g_bUseCgShader } { if { [info procs tcl3dHaveCg] ne "" } { if { ![tcl3dHaveCg] } { tk_messageBox -icon error -type ok -title "Error" \ -message "You do not have Cg installed." exit } } } frame .fr pack .fr -expand 1 -fill both togl .fr.toglwin -width $g_WinWidth -height $g_WinHeight \ -double true -depth true \ -createproc CreateCallback \ -reshapeproc ReshapeCallback \ -displayproc DisplayCallback listbox .fr.usage -font $::g_listFont -height 7 label .fr.info grid .fr.toglwin -row 0 -column 0 -sticky news grid .fr.usage -row 1 -column 0 -sticky news grid .fr.info -row 2 -column 0 -sticky news grid rowconfigure .fr 0 -weight 1 grid columnconfigure .fr 0 -weight 1 set shaderType "GLSL" if { $::g_bUseCgShader } { set shaderType "Cg" } set appTitle "Tcl3D demo: CodeSampler's Matrix Palette Skinning on the Hardware using a $shaderType shader" wm title . $appTitle # Watch For ESC Key And Quit Messages wm protocol . WM_DELETE_WINDOW "ExitProg" bind .