void SimpleSkeletalAnimatedObject_cl::ThinkFunction() { // Check whether the object is active. if (GetAnimConfig() == NULL) return; const float dtime = Vision::GetTimer()->GetTimeDifference(); // highlight the bone after animation event if (m_fBoneHighlightDuration > 0.0f && !m_sHighlightedBoneName.IsEmpty()) { int iColorVal = static_cast<int>(m_fBoneHighlightDuration / BONE_HIGHLIGHT_FADEOUTTIME * 255.0f); DrawBoneBoundingBox(m_sHighlightedBoneName, VColorRef(iColorVal,iColorVal, 0), 2.f); m_fBoneHighlightDuration -= Vision::GetTimer()->GetTimeDifference(); } // update the LookAt if (m_eSampleMode == MODE_LOOKAT) { VASSERT(m_pLookAtTarget); // rotate the object around the character m_fLookAtRotationPhase = hkvMath::mod(m_fLookAtRotationPhase + dtime*0.7f, hkvMath::pi() * 2.0f); hkvVec3 vNewPos(100.f*hkvMath::sinRad(m_fLookAtRotationPhase), 100.0f*hkvMath::cosRad(m_fLookAtRotationPhase), 160.0f); vNewPos += GetPosition(); m_pLookAtTarget->SetPosition(vNewPos); // update the LookAt orientation UpdateLookatHeadRotation(dtime); } }
void SpinningWorldModel::UpdateOn( const LTFLOAT &fCurTime ) { LTVector vNewPos(0.0f, 0.0f, 0.0f); LTRotation rNewRot; LTFLOAT fDeltaTm = fCurTime - m_fLastTime; LTFLOAT fPercent = 0.0f; LTVector vOldAngles( m_fPitch, m_fYaw, m_fRoll ); m_bUpdateSpin = LTTRUE; if( m_vVelocity.x ) { m_fPitch += m_vVelocity.x * fDeltaTm; } if( m_vVelocity.y ) { m_fYaw += m_vVelocity.y * fDeltaTm; } if( m_vVelocity.z ) { m_fRoll += m_vVelocity.z * fDeltaTm; } LTFLOAT fDifLeft = (m_vOnAngles - LTVector( m_fPitch, m_fYaw, m_fRoll )).Mag(); LTFLOAT fFinalDif = (m_vOnAngles - m_vOffAngles).Mag(); // Get the percent of our rotation to use for the light ani... fPercent = ( fFinalDif > MATH_EPSILON ? 1 - fDifLeft / fFinalDif : 1.0f ); if( !CalculateNewPosRot( vNewPos, rNewRot, m_vOnPos, m_fPowerOnTime, fPercent, LTTRUE ) && !(m_dwPropFlags & AWM_PROP_FORCEMOVE) ) { // Restore our angles... m_fPitch = vOldAngles.x; m_fYaw = vOldAngles.y; m_fRoll = vOldAngles.z; m_fMoveStartTm += g_pLTServer->GetFrameTime(); m_fLastTime = fCurTime; m_bUpdateSpin = LTFALSE; return; } g_pLTServer->MoveObject( m_hObject, &vNewPos ); // Check to see if we actually moved anywhere... LTVector vPos; g_pLTServer->GetObjectPos( m_hObject, &vPos ); if( !vPos.NearlyEquals( vNewPos, MATH_EPSILON ) ) { // Restore our angles... m_fPitch = vOldAngles.x; m_fYaw = vOldAngles.y; m_fRoll = vOldAngles.z; m_fMoveStartTm += g_pLTServer->GetFrameTime(); m_fLastTime = fCurTime; m_bUpdateSpin = LTFALSE; return; } g_pLTServer->RotateObject( m_hObject, &rNewRot ); // Keep the Pitch, Yaw and Roll within 2PI so they will never over flow... if( m_fPitch > MATH_CIRCLE ) { m_fPitch = -(MATH_CIRCLE - m_fPitch); } else if( m_fPitch < -MATH_CIRCLE ) { m_fPitch += MATH_CIRCLE; } if( m_fYaw > MATH_CIRCLE ) { m_fYaw = -(MATH_CIRCLE - m_fYaw); } else if( m_fYaw < -MATH_CIRCLE ) { m_fYaw += MATH_CIRCLE; } if( m_fRoll > MATH_CIRCLE ) { m_fRoll = -(MATH_CIRCLE - m_fRoll); } else if( m_fRoll < -MATH_CIRCLE ) { m_fRoll += MATH_CIRCLE; } m_fLastTime = fCurTime; }
void SpinningWorldModel::UpdateOn( const double &fCurTime ) { LTVector vNewPos(0.0f, 0.0f, 0.0f); LTRotation rNewRot; float fDeltaTm = (float)(fCurTime - m_fLastTime); float fPercent = 0.0f; LTVector vOldAngles( m_fPitch, m_fYaw, m_fRoll ); m_bUpdateSpin = true; if( m_vVelocity.x ) { m_fPitch += m_vVelocity.x * fDeltaTm; } if( m_vVelocity.y ) { m_fYaw += m_vVelocity.y * fDeltaTm; } if( m_vVelocity.z ) { m_fRoll += m_vVelocity.z * fDeltaTm; } float fDifLeft = (m_vInitOnAngles - LTVector( m_fPitch, m_fYaw, m_fRoll )).Mag(); float fFinalDif = (m_vInitOnAngles - m_vInitOffAngles).Mag(); // Get the percent of our rotation to use for the light ani... fPercent = ( fFinalDif > MATH_EPSILON ? 1 - fDifLeft / fFinalDif : 1.0f ); uint32 nFlags; g_pCommonLT->GetObjectFlags( m_hObject, OFT_Flags, nFlags ); bool bTestCollisions = !!( nFlags & FLAG_SOLID ); if( !CalculateNewPosRot( vNewPos, rNewRot, m_vOnPos, m_fPowerOnTime, fPercent, bTestCollisions ) && !(m_dwPropFlags & AWM_PROP_FORCEMOVE) ) { // Restore our angles... m_fPitch = vOldAngles.x; m_fYaw = vOldAngles.y; m_fRoll = vOldAngles.z; m_fMoveStartTm += g_pLTServer->GetFrameTime(); m_fLastTime = fCurTime; m_bUpdateSpin = false; return; } g_pLTServer->Physics()->MoveObject( m_hObject, vNewPos, 0 ); // Check to see if we actually moved anywhere... LTVector vPos; g_pLTServer->GetObjectPos( m_hObject, &vPos ); if( !vPos.NearlyEquals( vNewPos, MATH_EPSILON ) ) { // Restore our angles... m_fPitch = vOldAngles.x; m_fYaw = vOldAngles.y; m_fRoll = vOldAngles.z; m_fMoveStartTm += g_pLTServer->GetFrameTime(); m_fLastTime = fCurTime; m_bUpdateSpin = false; return; } g_pLTServer->RotateObject( m_hObject, rNewRot ); // Keep the Pitch, Yaw and Roll within 2PI so they will never over flow... if( m_fPitch > MATH_CIRCLE ) { m_fPitch = -(MATH_CIRCLE - m_fPitch); } else if( m_fPitch < -MATH_CIRCLE ) { m_fPitch += MATH_CIRCLE; } if( m_fYaw > MATH_CIRCLE ) { m_fYaw = -(MATH_CIRCLE - m_fYaw); } else if( m_fYaw < -MATH_CIRCLE ) { m_fYaw += MATH_CIRCLE; } if( m_fRoll > MATH_CIRCLE ) { m_fRoll = -(MATH_CIRCLE - m_fRoll); } else if( m_fRoll < -MATH_CIRCLE ) { m_fRoll += MATH_CIRCLE; } m_fLastTime = fCurTime; }
void CModelGTF::RenderModel() { // Go through all of the objects stored in this model for(int i = 0; i < m_Model.numOfObjects; i++) { // Get the current object that we are displaying t3DObject *pObject = &m_Model.pObject[i]; // Check if there are materials assigned to this object and // enable texture mapping if necessary, otherwise turn it off. if(pObject->numOfMaterials >= 1) glEnable(GL_TEXTURE_2D); else glDisable(GL_TEXTURE_2D); // Go through all of the faces (polygons) of the object and draw them for(int j = 0; j < pObject->numOfFaces; j++) { // Now let's see if there are any materials that need binding if(pObject->numOfMaterials >= 1) { // Create a static int to hold the last texture ID static int lastTexID = -1; // Extract the current textureID for this face from our face data int textureID = m_Model.pMaterials[pObject->pFaces[j].textureID].texureId; // If the current texture ID isn't the same as last time, bind the texture. if(textureID != lastTexID && textureID >= 0 && textureID < (int)strTextures.size()) glBindTexture(GL_TEXTURE_2D, m_Textures[textureID]); // Store the current textureID in our static int for next time lastTexID = textureID; } // Start drawing our model triangles glBegin(GL_TRIANGLES); // Go through each vertex of the triangle and draw it. for(int whichVertex = 0; whichVertex < 3; whichVertex++) { // Make sure there are texture coordinates for this if(pObject->pTexVerts) { // Here we grab the texture index from our face information int texIndex = pObject->pFaces[j].coordIndex[whichVertex]; // Assign the texture coordinate to this vertex glTexCoord2f(pObject->pTexVerts[ texIndex ].x, pObject->pTexVerts[ texIndex ].y); } // Grab the current vertex index from our face information int vertIndex = pObject->pFaces[j].vertIndex[whichVertex]; //////////// *** NEW *** ////////// *** NEW *** ///////////// *** NEW *** //////////////////// // Now we get to the beef of the tutorial. Below is where we // handle the calculations for our animation. Basically what // happens is we grab the current and the next frame of animation // data. With that data (the weights and transformations) we // do spherical interpolation (quaternion rotations) and linear // interpolation (the translations) to get a smooth transformation // between each frame of animation. There isn't anything to // difficult about the concepts, but the math is the challenging // part. The only math that is new to this tutorial is the // calculations of multiplying a quaternion by a 3D vector. // Extract the current vertex and draw it CVector3 vPos = pObject->pVerts[vertIndex]; // If there is no animation, just render the original // vertex and continue (skip the animation calculations). if(!pObject->bAnimated) { glVertex3f(vPos.x, vPos.y, vPos.z); continue; } // This will be our final vertex position to render CVector3 vNewPos(0, 0, 0); // Just to specify the index, we store our vertex index as a blendIndex. // This will index into our weight list. int blendIndex = vertIndex; // Now comes the crazy code :) We now will go through every single // weight influence and add up all the influences and transformations // to our current vertex. for(int b=0; b < pObject->vWeightInfo[blendIndex].numBlendLinks; b++) { // Grab the current bone index from our weight list. Remember that // the X variable stores the bone index and the Y stores the weight. int boneIndex = (int)pObject->vWeightInfo[blendIndex].pWeightInfo[b].x; // Make sure that the index we got isn't out of range of our bones if(boneIndex > pObject->numBones) return; // Now we want to grab the translation and rotation data for both the // current and the next frame of animation. That way we can interpolate // between the two frames to make the animation look smoother. So here // we grab pointers to the current and next matrix information for the // current bone we are working with. tBoneMatrix *pBoneMatrix1 = &(pObject->vBoneInfo[m_Model.currentFrame].pBoneMatrices[boneIndex]); tBoneMatrix *pBoneMatrix2 = &(pObject->vBoneInfo[m_Model.nextFrame].pBoneMatrices[boneIndex]); // First we want to perform the spherical-linear interpolation between // the two quaternions (rotations) for the current and next frame. CQuaternion qPos(pBoneMatrix1->qRotation); CQuaternion qPos2(pBoneMatrix2->qRotation); CQuaternion qNew = qPos.Slerp(qPos, qPos2, m_Model.t); // To apply the interpolated rotation we multiply our rotation by // the original position of the vertex. This will then use our // function that we created at the top of this file which multiplies a // quaternion by a vector. Once we do that, we store the new 3D vector. CVector3 vTempPos = qNew * vPos; // Next we grab the translations for the current and next frame CVector3 vPosition = pBoneMatrix1->vTranslation; CVector3 vNextPosition = pBoneMatrix2->vTranslation; // Using a simple linear-interpolation equation, we use our time "t" // value to interpolate between the current and next translation. This // gives us a smooth transition between each frame of animation. vTempPos.x += vPosition.x + m_Model.t * (vNextPosition.x - vPosition.x); vTempPos.y += vPosition.y + m_Model.t * (vNextPosition.y - vPosition.y); vTempPos.z += vPosition.z + m_Model.t * (vNextPosition.z - vPosition.z); // If there is a special weight for this vertex we want to extract // that and apply it to our current vertex. If the model isn't using // weighted animation then the weight will just be 1.0, which will do // nothing to the vertex when applied. Remember that the Y value of // our weight-info list is the actual weight value to be applied. float weight = pObject->vWeightInfo[blendIndex].pWeightInfo[b].y; // Apply the weight value to our current vertex and add it to our // final transformed vertex position. vNewPos += vTempPos * weight; } // vNewPos has our final position that was transformed, so render it. glVertex3f(vNewPos.x, vNewPos.y, vNewPos.z); //////////// *** NEW *** ////////// *** NEW *** ///////////// *** NEW *** //////////////////// } // Stop drawing polygons glEnd(); } } }