int FindDepNodeEnum::proc(ReferenceMaker *rmaker) { if (rmaker->SuperClassID()!=BASENODE_CLASS_ID) return 0; INode* node = (INode *)rmaker; if (node->GetTarget()==targ) { depNode = node; return 1; } return 0; }
//--------------------------------------------------------------- void LightExporter::exportLight( ExportNode* exportNode ) { if ( !exportNode->getIsInVisualScene() ) return; String lightId = getLightId(*exportNode); INode * iNode = exportNode->getINode(); LightObject* lightObject = (LightObject*) (iNode->GetObjectRef()); if ( !lightObject ) return; if ( mDocumentExporter->isExportedObject(ObjectIdentifier(lightObject)) ) return; mDocumentExporter->insertExportedObject(ObjectIdentifier(lightObject), exportNode); // Retrieve the target node, if we are not baking matrices. // Baked matrices must always sample the transform! ULONG ClassId = lightObject->ClassID().PartA(); bool isTargeted = !mDocumentExporter->getOptions().getBakeMatrices() && (ClassId == SPOT_LIGHT_CLASS_ID || ClassId == TDIR_LIGHT_CLASS_ID); INode* targetNode = isTargeted ? iNode->GetTarget() : 0; // some lights are not supported at all switch (ClassId) { case FSPOT_LIGHT_CLASS_ID: case SPOT_LIGHT_CLASS_ID: case DIR_LIGHT_CLASS_ID: case TDIR_LIGHT_CLASS_ID: case SKY_LIGHT_CLASS_ID_PART_A: case OMNI_LIGHT_CLASS_ID: break; default: return; } // Determine the light's type bool isSpot = false; bool isDirectional = false; bool isPoint = false; bool isSky = false; COLLADASW::Light::LightType lightType; switch (ClassId) { case FSPOT_LIGHT_CLASS_ID: case SPOT_LIGHT_CLASS_ID: lightType = COLLADASW::Light::SPOT; isSpot = true; break; case DIR_LIGHT_CLASS_ID: case TDIR_LIGHT_CLASS_ID: lightType = COLLADASW::Light::DIRECTIONAL; isDirectional = true; break; case SKY_LIGHT_CLASS_ID_PART_A: lightType = COLLADASW::Light::POINT; isSky = true; break; case OMNI_LIGHT_CLASS_ID: lightType = COLLADASW::Light::POINT; isPoint = true; break; } COLLADASW::Light * colladaLight = 0; switch ( lightType ) { case COLLADASW::Light::DIRECTIONAL: colladaLight = new COLLADASW::DirectionalLight(COLLADASW::LibraryLights::mSW, lightId, COLLADASW::Utils::checkNCName(NativeString(exportNode->getINode()->GetName()))); break; case COLLADASW::Light::POINT: colladaLight = new COLLADASW::PointLight(COLLADASW::LibraryLights::mSW, lightId, COLLADASW::Utils::checkNCName(NativeString(exportNode->getINode()->GetName()))); break; case COLLADASW::Light::SPOT: colladaLight = new COLLADASW::SpotLight(COLLADASW::LibraryLights::mSW, lightId, COLLADASW::Utils::checkNCName(NativeString(exportNode->getINode()->GetName()))); break; } // Retrieve the parameter block IParamBlock* parameters = 0; IParamBlock2* parametersSky = 0; if (isSky) parametersSky = (IParamBlock2*) lightObject->GetReference(MaxLight::PBLOCK_REF_SKY); else parameters = (IParamBlock*) lightObject->GetReference(MaxLight::PBLOCK_REF); if (!parameters && !parametersSky) { delete colladaLight; return; } if (parameters) { bool hasAnimatedColor = mAnimationExporter->addAnimatedParameter(parameters, MaxLight::PB_COLOR, lightId, colladaLight->getColorDefaultSid(), 0 ); colladaLight->setColor(EffectExporter::maxColor2Color(parameters->GetColor(MaxLight::PB_COLOR)), hasAnimatedColor); } else if (parametersSky ) { bool hasAnimatedColor = mAnimationExporter->addAnimatedParameter(parametersSky, MaxLight::PB_SKY_COLOR, lightId, colladaLight->getColorDefaultSid(), 0 ); colladaLight->setColor(EffectExporter::maxColor2Color(parametersSky->GetColor(MaxLight::PB_SKY_COLOR)), hasAnimatedColor); } if (isSpot || isPoint) { int decayFunction = parameters->GetInt(isPoint ? MaxLight::PB_DECAY : MaxLight::PB_OMNIDECAY, mDocumentExporter->getOptions().getAnimationStart()); switch (decayFunction) { case 1: colladaLight->setConstantAttenuation(0.0f); colladaLight->setLinearAttenuation(1.0f); break; case 2: colladaLight->setConstantAttenuation(0.0f); colladaLight->setQuadraticAttenuation(1.0f); break; case 0: default: colladaLight->setConstantAttenuation(1.0f); break; } } else if (isSky) { colladaLight->setConstantAttenuation(1.0f); } setExtraTechnique(colladaLight); if ( parameters ) addParamBlockAnimatedExtraParameters(LIGHT_ELEMENT, LIGHT_PARAMETERS, LIGHT_PARAMETER_COUNT, parameters, lightId); else addParamBlockAnimatedExtraParameters(SKYLIGHT_ELEMENT, SKYLIGHT_PARAMETERS, SKYLIGHT_PARAMETER_COUNT, parametersSky, lightId); // add all the information to extra tag, that are not contained in IParamBlock if (isSpot || isDirectional || isPoint) { GenLight* light = (GenLight*)(lightObject); if (!light) { delete colladaLight; return; } // Export the overshoot flag for directional lights if (isDirectional || isSpot) { addExtraChildParameter(LIGHT_ELEMENT, OVERSHOOT_PARAMETER, light->GetOvershoot() != false); } addExtraChildParameter(LIGHT_ELEMENT, DECAY_TYPE_PARAMETER, (int)light->GetDecayType()); addExtraChildParameter(LIGHT_ELEMENT, USE_NEAR_ATTENUATION_PARAMETER, (light->GetUseAttenNear() != false)); addExtraChildParameter(LIGHT_ELEMENT, USE_FAR_ATTENUATION_PARAMETER, (light->GetUseAtten() != false)); exportShadowParameters(light); if (light->GetProjector()) { Texmap* projectorMap = light->GetProjMap(); if (projectorMap) { String imageId = exportTexMap(projectorMap); if ( !imageId.empty() ) { addExtraChildParameter(LIGHT_ELEMENT, LIGHT_MAP_ELEMENT, "#" + imageId); } } } } else // isSky { Texmap *colorMap = parametersSky->GetTexmap(MaxLight::PB_SKY_COLOR_MAP, mDocumentExporter->getOptions().getAnimationStart()); String imageId = exportTexMap(colorMap); if ( !imageId.empty()) { addExtraChildParameter(SKYLIGHT_ELEMENT, SKYLIGHT_COLORMAP_ELEMENT, "#" + imageId); } } addLight(*colladaLight); delete colladaLight; }
// Build a CLight bool CExportNel::buildLight (GenLight &maxLight, NL3D::CLight& nelLight, INode& node, TimeValue time) { // Eval the light state fot this time Interval valid=NEVER; LightState ls; if (maxLight.EvalLightState(time, valid, &ls)==REF_SUCCEED) { // Set the light mode switch (maxLight.Type()) { case OMNI_LIGHT: nelLight.setMode (CLight::PointLight); break; case TSPOT_LIGHT: case FSPOT_LIGHT: nelLight.setMode (CLight::SpotLight); break; case DIR_LIGHT: case TDIR_LIGHT: nelLight.setMode (CLight::DirectionalLight); break; default: // Not initialized return false; } // *** Set the light color // Get the color CRGBA nelColor; Point3 maxColor=maxLight.GetRGBColor(time); // Mul by multiply CRGBAF nelFColor; nelFColor.R=maxColor.x; nelFColor.G=maxColor.y; nelFColor.B=maxColor.z; nelFColor.A=1.f; nelFColor*=maxLight.GetIntensity(time); nelColor=nelFColor; // Affect the ambiant color ? if (maxLight.GetAmbientOnly()) { nelLight.setAmbiant (nelColor); nelLight.setDiffuse (CRGBA (0,0,0)); nelLight.setSpecular (CRGBA (0,0,0)); } else { nelLight.setAmbiant (CRGBA (0,0,0)); // Affect the diffuse color ? if (maxLight.GetAffectDiffuse()) nelLight.setDiffuse (nelColor); else nelLight.setDiffuse (CRGBA (0,0,0)); // Affect the specular color ? if (maxLight.GetAffectSpecular()) nelLight.setSpecular (nelColor); else nelLight.setSpecular (CRGBA (0,0,0)); } // Set the light position Point3 pos=node.GetNodeTM(time).GetTrans (); CVector position; position.x=pos.x; position.y=pos.y; position.z=pos.z; // Set the position nelLight.setPosition (position); // Set the light direction CVector direction; INode* target=node.GetTarget (); if (target) { // Get the position of the target Point3 posTarget=target->GetNodeTM (time).GetTrans (); CVector positionTarget; positionTarget.x=posTarget.x; positionTarget.y=posTarget.y; positionTarget.z=posTarget.z; // Direction direction=positionTarget-position; direction.normalize (); } else // No target { // Get orientation of the source as direction CMatrix nelMatrix; convertMatrix (nelMatrix, node.GetNodeTM(time)); // Direction is -Z direction=-nelMatrix.getK(); direction.normalize (); } // Set the direction nelLight.setDirection (direction); // Set spot light information nelLight.setCutoff ((float)(NLMISC::Pi*maxLight.GetFallsize(time)/180.f/2.f)); // Compute the exponent value float angle=(float)(NLMISC::Pi*maxLight.GetHotspot(time)/(2.0*180.0)); nelLight.setupSpotExponent (angle); // *** Set attenuation if (maxLight.GetUseAtten()) { float nearAtten = maxLight.GetAtten (time, ATTEN_START); if (nearAtten == 0) nearAtten = 0.1f; nelLight.setupAttenuation (nearAtten, maxLight.GetAtten (time, ATTEN_END)); } else nelLight.setNoAttenuation (); // Initialized return true; } // Not initialized return false; }
//--------------------------------------------------------------- void CameraExporter::exportCamera( ExportNode* exportNode ) { if ( !exportNode->getIsInVisualScene() ) return; String cameraId = getCameraId(*exportNode); INode* iNode = exportNode->getINode(); CameraObject* camera = (CameraObject*)iNode->GetObjectRef(); INode* targetNode = ( camera->ClassID().PartA() == LOOKAT_CAM_CLASS_ID) ? iNode->GetTarget() : 0; if ( camera ) { if ( mDocumentExporter->isExportedObject(ObjectIdentifier(camera)) ) return; mDocumentExporter->insertExportedObject(ObjectIdentifier(camera), exportNode); // Retrieve the camera parameters block IParamBlock* parameters = (IParamBlock*) camera->GetReference(MaxCamera::PBLOCK_REF); COLLADASW::BaseOptic * optics = 0; if ( camera->IsOrtho() ) { optics = new COLLADASW::OrthographicOptic(COLLADASW::LibraryCameras::mSW); // Calculate the target distance for FOV calculations float targetDistance; if ( targetNode ) { Point3 targetTrans = targetNode->GetNodeTM(mDocumentExporter->getOptions().getAnimationStart()).GetTrans(); Point3 cameraTrans = iNode->GetNodeTM(mDocumentExporter->getOptions().getAnimationStart()).GetTrans(); targetDistance = (targetTrans - cameraTrans).Length(); } else { targetDistance = camera->GetTDist(mDocumentExporter->getOptions().getAnimationStart()); } ConversionInverseOrthoFOVFunctor conversionInverseOrthoFOVFunctor(targetDistance); if ( AnimationExporter::isAnimated(parameters, MaxCamera::FOV) ) { optics->setXMag(conversionInverseOrthoFOVFunctor(parameters->GetFloat(MaxCamera::FOV)), XMAG_SID); mAnimationExporter->addAnimatedParameter(parameters, MaxCamera::FOV, cameraId, XMAG_SID, 0, true, &conversionInverseOrthoFOVFunctor); } else { optics->setXMag(conversionInverseOrthoFOVFunctor(parameters->GetFloat(MaxCamera::FOV))); } } else { optics = new COLLADASW::PerspectiveOptic(COLLADASW::LibraryCameras::mSW); if ( AnimationExporter::isAnimated(parameters, MaxCamera::FOV) ) { optics->setXFov(COLLADASW::MathUtils::radToDegF(parameters->GetFloat(MaxCamera::FOV)), XFOV_SID); mAnimationExporter->addAnimatedParameter(parameters, MaxCamera::FOV, cameraId, XFOV_SID, 0, true, &ConversionFunctors::radToDeg); } else { optics->setXFov(COLLADASW::MathUtils::radToDegF(parameters->GetFloat(MaxCamera::FOV))); } } bool hasAnimatedZNear = mAnimationExporter->addAnimatedParameter(parameters, MaxCamera::NEAR_CLIP, cameraId, optics->getZNearDefaultSid(), 0); optics->setZNear(parameters->GetFloat(MaxCamera::NEAR_CLIP), hasAnimatedZNear); bool hasAnimatedZFar = mAnimationExporter->addAnimatedParameter(parameters, MaxCamera::FAR_CLIP, cameraId, optics->getZFarDefaultSid(), 0); optics->setZFar(parameters->GetFloat(MaxCamera::FAR_CLIP), hasAnimatedZFar); #ifdef UNICODE String exportNodeName = COLLADABU::StringUtils::wideString2utf8String(exportNode->getINode()->GetName()); COLLADASW::Camera colladaCamera(COLLADASW::LibraryCameras::mSW, optics, cameraId, COLLADASW::Utils::checkNCName(exportNodeName)); #else COLLADASW::Camera colladaCamera(COLLADASW::LibraryCameras::mSW, optics, cameraId, COLLADASW::Utils::checkNCName(exportNode->getINode()->GetName())); #endif setExtraTechnique(&colladaCamera); // Retrieve the camera target if ( targetNode ) { ExportNode* targetExportNode = mExportSceneGraph->getExportNode(targetNode); addExtraParameter(EXTRA_PARAMETER_TARGET, "#" + targetExportNode->getId()); } if (camera->GetMultiPassEffectEnabled(0, FOREVER)) { IMultiPassCameraEffect *multiPassCameraEffect = camera->GetIMultiPassCameraEffect(); if (multiPassCameraEffect) { Class_ID id = multiPassCameraEffect->ClassID(); // the camera could have both effects, but not in Max if (id == FMULTI_PASS_MOTION_BLUR_CLASS_ID) { IParamBlock2 *parameters = multiPassCameraEffect->GetParamBlock(0); if (parameters ) { addParamBlockAnimatedExtraParameters(MOTION_BLUR_ELEMENT, MOTION_BLUR_PARAMETERS, MOTION_BLUR_PARAMETER_COUNT, parameters, cameraId); } } else if (id == FMULTI_PASS_DOF_CLASS_ID) { IParamBlock2 *parameters = multiPassCameraEffect->GetParamBlock(0); if (parameters ) { addParamBlockAnimatedExtraParameters(DEPTH_OF_FIELD_ELEMENT, DEPTH_OF_FIELD_PARAMETERS, DEPTH_OF_FIELD_PARAMETER_COUNT, parameters, cameraId); addExtraParameter(TARGETDISTANCE_PARAMETER, camera->GetTDist(0)); } } } } addCamera(colladaCamera); delete optics; } }
/** * @brief * Writes the scene node modifiers */ void PLSceneNode::WriteModifiers(XmlElement &cSceneElement, const String &sApplicationDrive, const String &sApplicationDir) { // Is there a 3ds Max node? (no 3ds Max node, no properties) INode *pMaxNode = GetMaxNode(); if (pMaxNode) { // Has this 3ds Max node a target? if (pMaxNode->GetTarget()) { // Write down the scene node modifier WriteTargetRotationModifier(cSceneElement, *pMaxNode->GetTarget(), false); } // Are there any position, rotation, scale keyframes? bool bPositionKeyframes = false; bool bRotationKeyframes = false; bool bScaleKeyframes = false; // Check 3ds Max node controllers Control *pTMController = pMaxNode->GetTMController(); if (pTMController) { // Position controller Control *pController = pTMController->GetPositionController(); if (pController) { // Are there any position keyframes? bPositionKeyframes = PLTools::HasKeyControlInterface(*pController); if (!bPositionKeyframes) { // Is there a path controller? IPathPosition *pPathController = GetIPathConstInterface(pController); if (pPathController && pPathController->GetNumTargets() > 0) { INode *pTarget = pPathController->GetNode(0); if (pTarget) { // Get path filename const String sPathFilename = PLTools::GetResourceFilename(PLTools::ResourcePath, String(pTarget->GetName()) + ".path"); // Get the percentage along the path float fPercentageAlongPath = 0.0f; { IParamBlock2 *pIParamBlock2 = pPathController->GetParamBlock(path_params); int nRefNum = pIParamBlock2 ? pIParamBlock2->GetControllerRefNum(path_percent) : -1; RefTargetHandle cRefTargetHandle = (nRefNum >= 0) ? pIParamBlock2->GetReference(nRefNum) : nullptr; if (cRefTargetHandle) fPercentageAlongPath = pIParamBlock2->GetFloat(path_percent, 0); } { // Add scene node modifier XmlElement *pModifierElement = new XmlElement("Modifier"); pModifierElement->SetAttribute("Class", "PLScene::SNMPositionPath"); pModifierElement->SetAttribute("Filename", sPathFilename); pModifierElement->SetAttribute("Progress", String::Format("%f", fPercentageAlongPath)); // [TODO] Any change to setup speed inside 3ds Max? static const float fSpeed = 0.03f; // Automatic animation playback? if (g_SEOptions.bAnimationPlayback) pModifierElement->SetAttribute("Speed", String::Format("%f", (pPathController->GetFlip() ? -fSpeed : fSpeed))); else pModifierElement->SetAttribute("Speed", "0.0"); // Link modifier element cSceneElement.LinkEndChild(*pModifierElement); } // Follow? if (pPathController->GetFollow()) { // Add scene node modifier XmlElement *pModifierElement = new XmlElement("Modifier"); pModifierElement->SetAttribute("Class", "PLScene::SNMRotationMoveDirection"); // Link modifier element cSceneElement.LinkEndChild(*pModifierElement); } } } } } // Rotation controller pController = pTMController->GetRotationController(); if (pController) { // Are there any rotation keyframes? bRotationKeyframes = PLTools::HasKeyControlInterface(*pController); if (!bRotationKeyframes) { // Is there a look at controller? ILookAtConstRotation *pLookAtController = GetILookAtConstInterface(pController); if (pLookAtController && pLookAtController->GetNumTargets() > 0) { INode *pTarget = pLookAtController->GetNode(0); if (pTarget) { // Check look at controller bool bFlip = (pLookAtController->GetTargetAxisFlip() != 0); // Write down the scene node modifier WriteTargetRotationModifier(cSceneElement, *pTarget, bFlip); } } } } // Scale controller pController = pTMController->GetScaleController(); if (pController) { // Are there any scale keyframes? bScaleKeyframes = PLTools::HasKeyControlInterface(*pController); } } // Export keyframes? if (bPositionKeyframes || bRotationKeyframes || bScaleKeyframes) { // Get timing Interval cInterval = GetCOREInterface()->GetAnimRange(); int nTicksPerFrame = GetTicksPerFrame(); int nFrameCount = (cInterval.End() - cInterval.Start()) / nTicksPerFrame + 1; // Used to detect whether or not something is animated Point3 vFirstPos, vFirstScale; Quat qFirstRot; bool bUsePosition = false, bUseScale = false, bUseRotation = false; // Prepare the position chunk Chunk cPositionChunk; cPositionChunk.SetSemantic(Chunk::Position); cPositionChunk.Allocate(Chunk::Float, 3, nFrameCount); float *pfPositionData = reinterpret_cast<float*>(cPositionChunk.GetData()); // Prepare the rotation chunk Chunk cRotationChunk; cRotationChunk.SetSemantic(Chunk::Rotation); cRotationChunk.Allocate(Chunk::Float, 4, nFrameCount); float *pfRotationData = reinterpret_cast<float*>(cRotationChunk.GetData()); // Prepare the scale chunk Chunk cScaleChunk; cScaleChunk.SetSemantic(Chunk::Scale); cScaleChunk.Allocate(Chunk::Float, 3, nFrameCount); float *pfScaleData = reinterpret_cast<float*>(cScaleChunk.GetData()); // Loop through all frames int nTime = cInterval.Start(); for (int nFrame=0; nFrame<nFrameCount; nFrame++, nTime+=nTicksPerFrame) { // Get the position, rotation and scale Point3 vPos, vScale; Quat qRot; GetPosRotScale(vPos, qRot, vScale, nTime); // First frame? if (!nFrame) { vFirstPos = vPos; vFirstScale = vScale; qFirstRot = qRot; } else { if (!vFirstPos.Equals(vPos)) bUsePosition = true; if (!vFirstScale.Equals(vScale)) bUseScale = true; if (!qFirstRot.Equals(qRot)) bUseRotation = true; } // Position if (bPositionKeyframes && pfPositionData) { // Currently ONLY the center of the container the node is in use used to make it relative const Point3 vParentWorldSpaceCenter = GetContainer() ? GetContainer()->GetWorldSpaceCenter() : Point3(0.0f, 0.0f, 0.0f); // Get the position const Point3 vFinalPos = (GetType() != TypeScene && GetType() != TypeCell) ? vPos-vParentWorldSpaceCenter : static_cast<const PLSceneContainer*>(this)->GetWorldSpaceCenter(); // x *pfPositionData = vFinalPos.x; pfPositionData++; // y *pfPositionData = vFinalPos.y; pfPositionData++; // z *pfPositionData = vFinalPos.z; pfPositionData++; } // Rotation if (bRotationKeyframes && pfRotationData) { // [TODO] Check this (why do we need it?) qRot.Invert(); // w *pfRotationData = qRot.w; pfRotationData++; // x *pfRotationData = qRot.x; pfRotationData++; // y *pfRotationData = qRot.y; pfRotationData++; // z *pfRotationData = qRot.z; pfRotationData++; } // Scale if (bScaleKeyframes && pfScaleData) { // x *pfScaleData = vScale.x; pfScaleData++; // y *pfScaleData = vScale.y; pfScaleData++; // z *pfScaleData = vScale.z; pfScaleData++; } } // Create keyframe animation scene node modifiers if (bPositionKeyframes && bUsePosition) { // [TODO] Better (and safer) filename // Save chunk const String sPositionKeys = PLTools::GetResourceFilename(PLTools::ResourceKeyframes, String::Format("%s_PositionKeyframes.chunk", GetName().GetASCII()).GetASCII()); if (SaveChunk(cPositionChunk, sApplicationDrive + sApplicationDir + sPositionKeys)) { // Add the modifier XmlElement *pModifierElement = new XmlElement("Modifier"); // Set class attribute pModifierElement->SetAttribute("Class", "PLScene::SNMPositionKeyframeAnimation"); // [TODO] Currently the frame rate is by default always 24 // Set frames per second attribute // pModifierElement->SetAttribute("FramesPerSecond", GetFrameRate()); // Automatic animation playback? pModifierElement->SetAttribute("Speed", g_SEOptions.bAnimationPlayback ? "1.0" : "0.0"); // Set keys attribute pModifierElement->SetAttribute("Keys", sPositionKeys); // Link modifier elements cSceneElement.LinkEndChild(*pModifierElement); } } if (bRotationKeyframes && bUseRotation) { // [TODO] Better (and safer) filename // Save chunk const String sRotationKeys = PLTools::GetResourceFilename(PLTools::ResourceKeyframes, String::Format("%s_RotationKeyframes.chunk", GetName().GetASCII()).GetASCII()); if (SaveChunk(cRotationChunk, sApplicationDrive + sApplicationDir + sRotationKeys)) { // Add the modifier XmlElement *pModifierElement = new XmlElement("Modifier"); // Set class attribute pModifierElement->SetAttribute("Class", "PLScene::SNMRotationKeyframeAnimation"); // [TODO] Currently the frame rate is by default always 24 // Set frames per second attribute // pModifierElement->SetAttribute("FramesPerSecond", GetFrameRate()); // Automatic animation playback? pModifierElement->SetAttribute("Speed", g_SEOptions.bAnimationPlayback ? "1.0" : "0.0"); // Set keys attribute pModifierElement->SetAttribute("Keys", sRotationKeys); // Link modifier elements cSceneElement.LinkEndChild(*pModifierElement); } } if (bScaleKeyframes && bUseScale) { // [TODO] Better (and safer) filename // Save chunk const String sScaleKeys = PLTools::GetResourceFilename(PLTools::ResourceKeyframes, String::Format("%s_ScaleKeyframes.chunk", GetName().GetASCII()).GetASCII()); if (SaveChunk(cScaleChunk, sApplicationDrive + sApplicationDir + sScaleKeys)) { // Add the modifier XmlElement *pModifierElement = new XmlElement("Modifier"); // Set class attribute pModifierElement->SetAttribute("Class", "PLScene::SNMScaleKeyframeAnimation"); // [TODO] Currently the frame rate is by default always 24 // Set frames per second attribute // pModifierElement->SetAttribute("FramesPerSecond", GetFrameRate()); // Automatic animation playback? pModifierElement->SetAttribute("Speed", g_SEOptions.bAnimationPlayback ? "1.0" : "0.0"); // Set keys attribute pModifierElement->SetAttribute("Keys", sScaleKeys); // Link modifier elements cSceneElement.LinkEndChild(*pModifierElement); } } } // Use modifiers? if (g_SEOptions.bUserPropModifiers) { // Check for modifiers MSTR sModifier; int nIndex = 1; TSTR s3dsMaxString; sModifier = _T("Mod"); while (pMaxNode->GetUserPropString(sModifier, s3dsMaxString)) { // Add scene node modifier XmlElement *pModifierElement = new XmlElement("Modifier"); // We really NEED a 'Class' attribute! bool bClassFound = false; // Get all expressions static RegEx cExpressionRegEx("\\s*((\\w*\\s*=\\s*\"[^\"]*\")|(\\w*\\s*=\\s*[\\w|]*))"); const String sString = s3dsMaxString; uint32 nExpressionParsePos = 0; while (cExpressionRegEx.Match(sString, nExpressionParsePos)) { // Get expression nExpressionParsePos = cExpressionRegEx.GetPosition(); const String sExpression = cExpressionRegEx.GetResult(0); // Process the found expression static RegEx cRegEx("\\s*(\\w*)\\s*=\\s*\"?\\s*([^\"]*)\\s*\"?"); if (cRegEx.Match(sExpression)) { // Get name and value const String sName = cRegEx.GetResult(0); const String sValue = cRegEx.GetResult(1); // Set attribute pModifierElement->SetAttribute(sName, sValue); // Class variable already found? if (!bClassFound && sName == "Class") bClassFound = true; } } // Link modifier element? if (bClassFound) cSceneElement.LinkEndChild(*pModifierElement); else delete pModifierElement; // Get next modifier sModifier.printf(_T("Mod%d"), nIndex); nIndex++; } } } }