void VisibleEntity::onPose(Array<shared_ptr<Surface> >& surfaceArray) { // We have to pose in order to compute bounds that are used for selection in the editor // and collisions in simulation, so pose anyway if not visible, // but then roll back. debugAssert(isFinite(m_frame.translation.x)); debugAssert(! isNaN(m_frame.rotation[0][0])); const int oldLen = surfaceArray.size(); const bool boundsChangedSincePreviousFrame = poseModel(surfaceArray); // Compute bounds for objects that moved if (m_lastAABoxBounds.isEmpty() || boundsChangedSincePreviousFrame || (m_lastChangeTime > m_lastBoundsTime)) { m_lastSphereBounds = Sphere(m_frame.translation, 0); const CFrame& myFrameInverse = frame().inverse(); m_lastObjectSpaceAABoxBounds = AABox::empty(); m_lastBoxBoundArray.fastClear(); // Look at all surfaces produced for (int i = oldLen; i < surfaceArray.size(); ++i) { AABox b; Sphere s; const shared_ptr<Surface>& surf = surfaceArray[i]; // body to world transformation for the surface CoordinateFrame cframe; surf->getCoordinateFrame(cframe, false); debugAssertM(cframe.translation.x == cframe.translation.x, "NaN translation"); surf->getObjectSpaceBoundingSphere(s); s = cframe.toWorldSpace(s); m_lastSphereBounds.radius = max(m_lastSphereBounds.radius, (s.center - m_lastSphereBounds.center).length() + s.radius); // Take the entity's frame out of consideration, so that we get tight AA bounds // in the Entity's frame CFrame osFrame = myFrameInverse * cframe; surf->getObjectSpaceBoundingBox(b); m_lastBoxBoundArray.append(cframe.toWorldSpace(b)); const Box& temp = osFrame.toWorldSpace(b); m_lastObjectSpaceAABoxBounds.merge(temp); } // Box can't represent an empty box, so we make empty boxes into real boxes with zero volume here if (m_lastObjectSpaceAABoxBounds.isEmpty()) { m_lastObjectSpaceAABoxBounds = AABox(Point3::zero()); m_lastAABoxBounds = AABox(frame().translation); } m_lastBoxBounds = frame().toWorldSpace(m_lastObjectSpaceAABoxBounds); m_lastBoxBounds.getBounds(m_lastAABoxBounds); m_lastBoundsTime = System::time(); } if (! m_visible) { // Discard my surfaces if I'm invisible; they were only needed for bounds surfaceArray.resize(oldLen, false); } }
void vHavokBehaviorComponent::OnAfterHavokUpdate() { if( m_character == HK_NULL || m_entityOwner == HK_NULL ) { return; } // Update WFM of skin or override it if ( m_useBehaviorWorldFromModel ) { const hkQsTransform& worldFromModel = m_character->getWorldFromModel(); // Copy the Behavior result into vision hkvMat3 visionRotation; hkvVec3 visionTranslation; vHavokConversionUtils::HkQuatToVisMatrix( worldFromModel.getRotation(), visionRotation ); vHavokConversionUtils::PhysVecToVisVecWorld( worldFromModel.getTranslation(), visionTranslation ); m_entityOwner->SetPosition( visionTranslation ); m_entityOwner->SetRotationMatrix( visionRotation ); } else { // Override Behavior results. // This will currently cause Behavior data being sent to HBT during remote debug to be one frame off. // However, the only other solution with the current APIs is to disable motion accumulation on *all* // Characters. A slight delay/offset in the special case of remote debugging in HBT isn't worth that change. UpdateHavokTransformFromVision(); } if( m_entityOwner->GetMesh() == HK_NULL || m_entityOwner->GetMesh()->GetSkeleton() == HK_NULL ) { return; } VisAnimConfig_cl* animConfig = m_entityOwner->GetAnimConfig(); if( !animConfig ) { return; } VisAnimFinalSkeletalResult_cl* skeletalResult = animConfig->GetFinalResult(); if( !skeletalResult ) { return; } // Try updating the bone index list in case a mesh was added if( m_boneIndexList.getSize() == 0 ) { UpdateAnimationAndBoneIndexList(); // Exit early if there's no bone index list if( m_boneIndexList.getSize() == 0 ) { return; } } // Convert pose to Havok model space const hkQsTransform* poseLocal = m_character->getPoseLocal(); hkArray<hkQsTransform> poseModel( m_character->getNumPoseLocal() ); hkaSkeletonUtils::transformLocalPoseToModelPose( m_character->getNumPoseLocal(), m_character->getSetup()->m_animationSkeleton->m_parentIndices.begin(), poseLocal, poseModel.begin() ); const hkQsTransform* pose = poseModel.begin(); float const inverseCharacterScale = 1.0f / m_character->getSetup()->getData()->m_scale; // Convert pose to vision units VisSkeleton_cl* visionSkeleton = m_entityOwner->GetMesh()->GetSkeleton(); for( int havokBoneIndex = 0; havokBoneIndex < m_character->getNumPoseLocal(); havokBoneIndex++ ) { // Find the bone index int visionBoneIndex = m_boneIndexList[havokBoneIndex]; if( visionBoneIndex != -1 ) { HK_ON_DEBUG( VisSkeletalBone_cl* bone = visionSkeleton->GetBone(visionBoneIndex) ); HK_ASSERT2(0x68b6649, hkString::strCmp( bone->m_sBoneName.AsChar(), m_character->getSetup()->m_animationSkeleton->m_bones[havokBoneIndex].m_name.cString() ) == 0, "" ); const hkQsTransform& transform = pose[havokBoneIndex]; // Convert Havok pose to Vision pose hkvQuat quat; quat.setIdentity(); vHavokConversionUtils::HkQuatToVisQuat( transform.getRotation(), quat ); hkvVec3 scale; vHavokConversionUtils::PhysVecToVisVec_noscale( transform.getScale(), scale ); hkvVec3 translation; vHavokConversionUtils::PhysVecToVisVecWorld( transform.getTranslation(), translation ); // Behavior propagates character scale through the skeleton; need to compensate when scaling to Vision scale *= inverseCharacterScale; // Set the skeletal result skeletalResult->SetCustomBoneScaling( visionBoneIndex, scale, VIS_REPLACE_BONE | VIS_OBJECT_SPACE ); skeletalResult->SetCustomBoneRotation( visionBoneIndex, quat, VIS_REPLACE_BONE | VIS_OBJECT_SPACE ); skeletalResult->SetCustomBoneTranslation( visionBoneIndex, translation, VIS_REPLACE_BONE | VIS_OBJECT_SPACE ); } } #if 0 // Draw skeleton for( int i = 0; i < visionSkeleton->GetBoneCount(); i++ ) { VisSkeletalBone_cl* bone = visionSkeleton->GetBone(i); if( bone->m_iParentIndex != -1 ) { hkvVec3 translation, subTranslation; hkvQuat rotation, subRotation; m_entityOwner->GetBoneCurrentWorldSpaceTransformation( i, translation, rotation ); m_entityOwner->GetBoneCurrentWorldSpaceTransformation( bone->m_iParentIndex, subTranslation, subRotation ); Vision::Game.DrawSingleLine( translation.x, translation.y, translation.z, subTranslation.x, subTranslation.y, subTranslation.z ); } } #endif }