//----------------------------------------------------------------------------- //! Returns an aiBone ptr if name exists, or null if it doesn't const SLMat4f SLAssimpImporter::getOffsetMat(const SLstring& name) { if(_jointOffsets.find(name) != _jointOffsets.end()) return _jointOffsets[name]; return SLMat4f(); }
/*! Applies the view transform to the modelview matrix depending on the eye: eye=-1 for left, eye=1 for right */ void SLCamera::setView(const SLEye eye) { SLSceneView* sv = SLScene::current->activeSV(); if (eye == centerEye) { stateGL->modelViewMatrix.identity(); stateGL->modelViewMatrix.multiply(_vm); } else // stereo viewing { if (_projection == stereoSideBySideD) { // half interpupilar distance _eyeSeparation = sv->oculus()->eyeSeparation(); SLfloat halfIPD = (SLfloat)eye * _eyeSeparation * -0.5f; // get the oculus orientation SLMat4f rotation(sv->oculus()->orientation().toMat4()); SLMat4f vmEye(SLMat4f(halfIPD, 0.0f, 0.f) * rotation * _vm); stateGL->modelViewMatrix = vmEye; stateGL->viewMatrix = vmEye; } else { // Get central camera vectors eye, lookAt, lookUp out of the view matrix vm SLVec3f EYE, LA, LU, LR; _vm.lookAt(&EYE, &LA, &LU, &LR); // Shorten LR to half of the eye dist (eye=-1 for left, eye=1 for right) LR *= _eyeSeparation * 0.5f * (SLfloat)eye; // Set the OpenGL view matrix for the left eye SLMat4f vmEye; vmEye.lookAt(EYE+LR, EYE + _focalDist*LA+LR, LU); stateGL->modelViewMatrix = vmEye; stateGL->viewMatrix = vmEye; } } }
/*! Applies the view transform to the modelview matrix depending on the eye: eye=-1 for left, eye=1 for right */ void SLCamera::setView(SLSceneView* sv, const SLEye eye) { SLScene* s = SLScene::current; SLMat4f vm = updateAndGetWMI(); if (eye == centerEye) { _stateGL->modelViewMatrix.identity(); _stateGL->viewMatrix.setMatrix(vm); } else // stereo viewing { if (_projection == stereoSideBySideD) { // half interpupilar disqtance //_eyeSeparation = s->oculus()->interpupillaryDistance(); update old rift code SLfloat halfIPD = (SLfloat)eye * _eyeSeparation * -0.5f; SLMat4f trackingPos; if (_useDeviceRot) { // get the oculus or mobile device orientation SLQuat4f rotation; if (s->oculus()->isConnected()) { rotation = s->oculus()->orientation(eye); trackingPos.translate(-s->oculus()->position(eye)); } else rotation = sv->deviceRotation(); SLfloat rotX, rotY, rotZ; rotation.toMat4().toEulerAnglesZYX(rotZ, rotY, rotX); //SL_LOG("rotx : %3.1f, roty: %3.1f, rotz: %3.1f\n", rotX*SL_RAD2DEG, rotY*SL_RAD2DEG, rotZ*SL_RAD2DEG); SLVec3f viewAdjust = s->oculus()->viewAdjust(eye) * _unitScaling; SLMat4f vmEye(SLMat4f(viewAdjust.x, viewAdjust.y, viewAdjust.z) * rotation.inverted().toMat4() * trackingPos * vm); _stateGL->modelViewMatrix = vmEye; _stateGL->viewMatrix = vmEye; } else { SLMat4f vmEye(SLMat4f(halfIPD, 0.0f, 0.f) * vm); _stateGL->modelViewMatrix = vmEye; _stateGL->viewMatrix = vmEye; } } else { // Get central camera vectors eye, lookAt, lookUp out of the view matrix vm SLVec3f EYE, LA, LU, LR; vm.lookAt(&EYE, &LA, &LU, &LR); // Shorten LR to half of the eye dist (eye=-1 for left, eye=1 for right) LR *= _eyeSeparation * 0.5f * (SLfloat)eye; // Set the OpenGL view matrix for the left eye SLMat4f vmEye; vmEye.lookAt(EYE+LR, EYE + _focalDist*LA+LR, LU); _stateGL->modelViewMatrix = vmEye; _stateGL->viewMatrix = vmEye; } } }
/*! SLAssimpImporter::loadAnimation loads the scene graph node tree recursively. */ SLAnimation* SLAssimpImporter::loadAnimation(aiAnimation* anim) { int animCount = 0; if (_skeleton) animCount = _skeleton->numAnimations(); ostringstream oss; oss << "unnamed_anim_" << animCount; SLstring animName = oss.str(); SLfloat animTicksPerSec = (anim->mTicksPerSecond == 0) ? 30.0f : (SLfloat)anim->mTicksPerSecond; SLfloat animDuration = (SLfloat)anim->mDuration / animTicksPerSec; if (anim->mName.length > 0) animName = anim->mName.C_Str(); // log logMessage(LV_minimal, "\nLoading animation %s\n", animName.c_str()); logMessage(LV_normal, " Duration(seconds): %f \n", animDuration); logMessage(LV_normal, " Duration(ticks): %f \n", anim->mDuration); logMessage(LV_normal, " Ticks per second: %f \n", animTicksPerSec); logMessage(LV_normal, " Num channels: %d\n", anim->mNumChannels); // exit if we didn't load a skeleton but have animations for one if (_skinnedMeshes.size() > 0) assert(_skeleton != nullptr && "The skeleton wasn't impoted correctly."); // create the animation SLAnimation* result; if (_skeleton) result = _skeleton->createAnimation(animName, animDuration); else { result = SLScene::current->animManager().createNodeAnimation(animName, animDuration); _nodeAnimations.push_back(result); } SLbool isSkeletonAnim = false; for (SLuint i = 0; i < anim->mNumChannels; i++) { aiNodeAnim* channel = anim->mChannels[i]; // find the node that is animated by this channel SLstring nodeName = channel->mNodeName.C_Str(); SLNode* affectedNode = _sceneRoot->find<SLNode>(nodeName); SLuint id = 0; SLbool isJointNode = (affectedNode == nullptr); // @todo: this is currently a work around but it can happen that we receive normal node animationtracks and joint animationtracks // we don't allow node animation tracks in a skeleton animation, so we should split an animation in two seperate // animations if this happens. for now we just ignore node animation tracks if we already have joint tracks // ofc this will crash if the first track is a node anim but its just temporary if (!isJointNode && isSkeletonAnim) continue; // is there a skeleton and is this animation channel not affecting a normal node? if (_skeletonRoot && !affectedNode) { isSkeletonAnim = true; SLJoint* affectedJoint = _skeleton->getJoint(nodeName); if (affectedJoint == nullptr) break; id = affectedJoint->id(); // @todo warn if we find an animation with some node channels and some joint channels // this shouldn't happen! /// @todo [high priority!] Address the problem of some bones not containing an animation channel /// when importing. Current workaround is to set their reset position to their bind pose. /// This will however fail if we have multiple animations affecting a single model and fading /// some of them out or in. This will require us to provide animations that have a channel /// for all bones even if they're just positional. // What does this next line do? // // The testimportfile we used (Astroboy.dae) has the following properties: // > It has joints in the skeleton that aren't animated by any channel. // > The joints need a reset position of (0, 0, 0) to work properly // because the joint position is contained in a single keyframe for every joint // // Since some of the joints don't have a channel that animates them, they also lack // the joint position that the other joints get from their animation channel. // So we need to set the initial state for all joints that have a channel // to identity. // All joints that arent in a channel will receive their local joint bind pose as // reset position. // // The problem stems from the design desicion to reset a whole skeleton before applying // animations to it. If we were to reset each joint just before applying a channel to it // we wouldn't have this problem. But we coulnd't blend animations as easily. // SLMat4f prevOM = affectedJoint->om(); affectedJoint->om(SLMat4f()); affectedJoint->setInitialState(); affectedJoint->om(prevOM); } // log logMessage(LV_normal, "\n Channel %d %s", i, (isJointNode) ? "(joint animation)\n" : "\n"); logMessage(LV_normal, " Affected node: %s\n", channel->mNodeName.C_Str()); logMessage(LV_detailed, " Num position keys: %d\n", channel->mNumPositionKeys); logMessage(LV_detailed, " Num rotation keys: %d\n", channel->mNumRotationKeys); logMessage(LV_detailed, " Num scaling keys: %d\n", channel->mNumScalingKeys); // joint animation channels should receive the correct node id, normal node animations just get 0 SLNodeAnimTrack* track = result->createNodeAnimationTrack(id); // this is a node animation only, so we add a reference to the affected node to the track if (affectedNode && !isSkeletonAnim) { track->animatedNode(affectedNode); } KeyframeMap keyframes; // add position keys for (SLuint i = 0; i < channel->mNumPositionKeys; i++) { SLfloat time = (SLfloat)channel->mPositionKeys[i].mTime; keyframes[time] = SLImportKeyframe(&channel->mPositionKeys[i], nullptr, nullptr); } // add rotation keys for (SLuint i = 0; i < channel->mNumRotationKeys; i++) { SLfloat time = (SLfloat)channel->mRotationKeys[i].mTime; if (keyframes.find(time) == keyframes.end()) keyframes[time] = SLImportKeyframe(nullptr, &channel->mRotationKeys[i], nullptr); else { // @todo this shouldn't abort but just throw an exception assert(keyframes[time].rotation == nullptr && "There were two rotation keys assigned to the same timestamp."); keyframes[time].rotation = &channel->mRotationKeys[i]; } } // add scaleing keys for (SLuint i = 0; i < channel->mNumScalingKeys; i++) { SLfloat time = (SLfloat)channel->mScalingKeys[i].mTime; if (keyframes.find(time) == keyframes.end()) keyframes[time] = SLImportKeyframe(nullptr, nullptr, &channel->mScalingKeys[i]); else { // @todo this shouldn't abort but just throw an exception assert(keyframes[time].scaling == nullptr && "There were two scaling keys assigned to the same timestamp."); keyframes[time].scaling = &channel->mScalingKeys[i]; } } logMessage(LV_normal, " Found %d distinct keyframe timestamp(s).\n", keyframes.size()); for (auto it : keyframes) { SLTransformKeyframe* kf = track->createNodeKeyframe(it.first); kf->translation(getTranslation(it.first, keyframes)); kf->rotation(getRotation(it.first, keyframes)); kf->scale(getScaling(it.first, keyframes)); // log logMessage(LV_detailed, "\n Generating keyframe at time '%.2f'\n", it.first); logMessage(LV_detailed, " Translation: (%.2f, %.2f, %.2f) %s\n", kf->translation().x, kf->translation().y, kf->translation().z, (it.second.translation != nullptr) ? "imported" : "generated"); logMessage(LV_detailed, " Rotation: (%.2f, %.2f, %.2f, %.2f) %s\n", kf->rotation().x(), kf->rotation().y(), kf->rotation().z(), kf->rotation().w(), (it.second.rotation != nullptr) ? "imported" : "generated"); logMessage(LV_detailed, " Scale: (%.2f, %.2f, %.2f) %s\n", kf->scale().x, kf->scale().y, kf->scale().z, (it.second.scaling != nullptr) ? "imported" : "generated"); } } return result; }