//[-------------------------------------------------------] //[ Private virtual PLSceneNode functions ] //[-------------------------------------------------------] void PLSceneCellPortal::WriteToFile(XmlElement &cSceneElement, const String &sApplicationDrive, const String &sApplicationDir) { // Add scene node XmlElement *pNodeElement = new XmlElement("Node"); pNodeElement->SetAttribute("Class", GetClassName()); pNodeElement->SetAttribute("Name", GetName()); // Write position, rotation, scale, bounding box and flags WriteToFilePosRotScaleBoxFlags(*pNodeElement); // Target cell pNodeElement->SetAttribute("TargetCell", m_sTargetCell); // [TODO] Check this again, within the Dungeon-Demo scene, this test fails for one portal... // Check cell-portal direction and write a warning into the log if the direction looks wrong float fDot = 0.0f; PLSceneContainer *pContainer = GetContainer(); if (pContainer) { // Get world space direction vector from cell-portal to owner cell Point3 vDir = pContainer->GetWorldSpaceCenter() - m_vCenter; vDir.Unify(); // Check whether the cell-portal looks into the owner cell fDot = DotProd(m_vNormal, vDir); if (fDot < 0.0f) g_pLog->LogFLine(PLLog::Warning, "%s: The cell-portal doesn't look INTO the cell (%s) it is in!!", GetIGameNode()->GetName(), pContainer->GetName().GetASCII()); } // Vertices std::vector<Point3>::size_type nNumOfVertices = m_lstVertices.size(); String sVertices; if (fDot < 0.0f && g_SEOptions.bCorrectPortals) { // Invert vertex order for (std::vector<Point3>::size_type i=nNumOfVertices; i>0; i--) { const Point3 &vVertex = m_lstVertices[i-1]; if (i) sVertices += String::Format("%f %f %f ", vVertex.x, vVertex.y, vVertex.z); else sVertices += String::Format("%f %f %f", vVertex.x, vVertex.y, vVertex.z); } } else { for (std::vector<Point3>::size_type i=0; i<nNumOfVertices; i++) { const Point3 &vVertex = m_lstVertices[i]; if (i < nNumOfVertices-1) sVertices += String::Format("%f %f %f ", vVertex.x, vVertex.y, vVertex.z); else sVertices += String::Format("%f %f %f", vVertex.x, vVertex.y, vVertex.z); } } pNodeElement->SetAttribute("Vertices", sVertices); // Write flexible variables WriteVariables(*pNodeElement); // Write modifiers WriteModifiers(*pNodeElement, sApplicationDrive, sApplicationDir); // Link node element cSceneElement.LinkEndChild(*pNodeElement); }
/** * @brief * Saves the material */ bool PLSceneMaterial::Save(const String &sApplicationDrive, const String &sApplicationDir) { // Create XML document XmlDocument cDocument; // Add declaration XmlDeclaration *pDeclaration = new XmlDeclaration("1.0", "ISO-8859-1", ""); cDocument.LinkEndChild(*pDeclaration); // Add material XmlElement *pMaterialElement = new XmlElement("Material"); pMaterialElement->SetAttribute("Version", "1"); // Save the parameters SaveParameters(*pMaterialElement); // Link material element cDocument.LinkEndChild(*pMaterialElement); // Get the absolute material filename const String sFilename = sApplicationDrive + sApplicationDir + PLTools::GetResourceFilename(PLTools::ResourceMaterial, m_sName); { // Before we safe, we need to ensure that the target directory is there, else 'Save()' will fail! Url cUrl = sFilename; Directory cDirectory(cUrl.CutFilename()); cDirectory.CreateRecursive(); } // Save settings cDocument.Save(sFilename); // Done return true; }
/** * @brief * Writes the scene node position, rotation, scale, bounding box and flags into a file */ void PLSceneNode::WriteToFilePosRotScaleBoxFlags(XmlElement &cNodeElement) const { // 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); // Write down the position PLTools::XmlElementSetAttributeWithDefault(cNodeElement, "Position", (GetType() != TypeScene && GetType() != TypeCell) ? m_vPos-vParentWorldSpaceCenter : static_cast<const PLSceneContainer*>(this)->GetWorldSpaceCenter(), Point3(0.0f, 0.0f, 0.0f)); // Write down the rotation PLTools::XmlElementSetAttributeWithDefault(cNodeElement, "Rotation", m_vRot, Point3(0.0f, 0.0f, 0.0f)); // Write down the scale PLTools::XmlElementSetAttributeWithDefault(cNodeElement, "Scale", m_vScale, Point3(1.0f, 1.0f, 1.0f)); // Are there any flags? if (m_sFlags.GetLength()) { String sFlags = cNodeElement.GetAttribute("Flags"); if (sFlags.GetLength()) sFlags += '|' + m_sFlags; else sFlags = m_sFlags; sFlags.Trim(); cNodeElement.SetAttribute("Flags", sFlags); } }
/** * @brief * Writes the flexible scene node variables */ void PLSceneNode::WriteVariables(XmlElement &cNodeElement) const { // Export variables? if (g_SEOptions.bUserPropVariables) { // Is there a 3ds Max node (No 3ds Max node, no properties) INode *pMaxNode = GetMaxNode(); if (pMaxNode) { // Check for variables TSTR s3dsMaxString; if (pMaxNode->GetUserPropString(_T("Vars"), s3dsMaxString)) { // 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); // Flags variable? if (sName == "Flags") { // Update attribute String sFlags = cNodeElement.GetAttribute(sName); if (sFlags.GetLength()) sFlags += '|' + sValue; else sFlags = sValue; sFlags.Trim(); cNodeElement.SetAttribute(sName, sFlags); } else { // Set attribute cNodeElement.SetAttribute(sName, sValue); } } } } } } }
/** * @brief * Appends the parsed information about a plugin to the given XML document */ void PLPluginInfo::AppendInformation(XmlDocument &cDocument) const { // Add declaration XmlDeclaration *pDeclaration = new XmlDeclaration("1.0", "", ""); cDocument.LinkEndChild(*pDeclaration); XmlElement *pRootElement = new XmlElement("Plugin"); pRootElement->SetAttribute("Version", m_sPluginFileVersion); pRootElement->SetAttribute("PixelLightVersion", m_sPLVersion); AddTextXmlElement(*pRootElement, "Active", m_bActive ? "1" : "0"); AddTextXmlElement(*pRootElement, "Delayed", m_bDelayed ? "1" : "0"); AddTextXmlElement(*pRootElement, "Name", m_sPluginName); if (m_sModuleVersion.GetLength()) AddTextXmlElement(*pRootElement, "Version", m_sModuleVersion); if (m_sVendor.GetLength()) AddTextXmlElement(*pRootElement, "Vendor", m_sVendor); if (m_sLicense.GetLength()) AddTextXmlElement(*pRootElement, "License", m_sLicense); if (m_sDescription.GetLength()) AddTextXmlElement(*pRootElement, "Description", m_sDescription); cDocument.LinkEndChild(*pRootElement); // Append the parsed information to the given XML element m_cPluginPlatformInfo.Save(*pRootElement); // Are there any classes? if (!m_lstClasses.IsEmpty()) { // Create the classes XML element instance XmlElement *pClassesElement = new XmlElement("Classes"); // Iterate through all classes ConstIterator<PLPluginClassInfo*> cIterator = m_lstClasses.GetConstIterator(); while (cIterator.HasNext()) cIterator.Next()->Save(*pClassesElement); // Link the classes XML element instance to the XML root element pRootElement->LinkEndChild(*pClassesElement); } }
/** * @brief * Writes a target rotation scene node modifier */ void PLSceneNode::WriteTargetRotationModifier(XmlElement &cSceneElement, INode &cTarget, bool bFlip) const { // Get the PL scene node String sTarget; PLSceneNode *pSceneNode = GetScene().GetPLSceneNode(cTarget); if (pSceneNode) { // Same container? if (pSceneNode->GetContainer() == GetContainer()) { // Yeah, that was easy! sTarget = pSceneNode->GetName(); } else { // The exported supports only 'one' cell level, else this would be a bit more complex... if (pSceneNode->GetContainer() == &GetScene()) { sTarget += "Parent."; sTarget += pSceneNode->GetName(); } else { sTarget += "Parent."; sTarget += pSceneNode->GetContainer()->GetName(); sTarget += '.'; sTarget += pSceneNode->GetName(); } } // Fallback: Use the 3ds Max node name } else { sTarget = cTarget.GetName(); g_pLog->LogFLine(PLLog::Warning, "'%s' target node: There's no PL scene node with the name '%s'", m_sName.GetASCII(), sTarget.GetASCII()); } // Add scene node modifier XmlElement *pModifierElement = new XmlElement("Modifier"); pModifierElement->SetAttribute("Class", "PLScene::SNMRotationTarget"); pModifierElement->SetAttribute("Target", sTarget); if (bFlip) pModifierElement->SetAttribute("Offset", "0 180 0"); // Link modifier element cSceneElement.LinkEndChild(*pModifierElement); }
/** * @brief * Writes the scene container into a file */ void PLSceneContainer::WriteToFile(XmlElement &cSceneElement, const String &sApplicationDrive, const String &sApplicationDir, const String &sClass) { // Add scene node XmlElement *pNodeElement = new XmlElement("Container"); pNodeElement->SetAttribute("Class", sClass); pNodeElement->SetAttribute("Name", GetName()); // [TODO] Currently the bounding boxes are not correct /* if (!PLTools::IsEmpty(m_cBoundingBox)) { const Point3 vMin = m_cBoundingBox.pmin-m_vCenter; const Point3 vMax = m_cBoundingBox.pmax-m_vCenter; pNodeElement->SetAttribute("AABBMin", String::Format("%f %f %f", vMin.x, vMin.y, vMin.z)); pNodeElement->SetAttribute("AABBMax", String::Format("%f %f %f", vMax.x, vMax.y, vMax.z)); } */ // Write position, rotation, scale, bounding box and flags WriteToFilePosRotScaleBoxFlags(*pNodeElement); // Write flexible variables WriteVariables(*pNodeElement); // Write modifiers WriteModifiers(*pNodeElement, sApplicationDrive, sApplicationDir); // Are there any modifiers or scene nodes? if (m_lstSceneNodes.size()) { // Write down the child nodes for (std::vector<PLSceneNode*>::size_type i=0; i<m_lstSceneNodes.size(); i++) { PLSceneNode *pSceneNode = m_lstSceneNodes[i]; if (pSceneNode) pSceneNode->WriteToFile(*pNodeElement, sApplicationDrive, sApplicationDir); } } // Link node element cSceneElement.LinkEndChild(*pNodeElement); }
void PLTools::XmlElementSetAttributeWithDefault(XmlElement &cXmlElement, const String &sName, float fValue, float fDefaultValue) { // Is the given value equal to the given default value? if (fValue != fDefaultValue) { // Value to string const String sValue = String::Format("%f", fValue); // Due to rounding errors the string may now or may not contain the given default value, check for this if (sValue.GetFloat() != fDefaultValue) cXmlElement.SetAttribute(sName, sValue); } else { // The given value is equal to the default value -> nothing to do } }
void PLTools::XmlElementSetAttributeWithDefault(XmlElement &cXmlElement, const String &sName, const Point3 &cPoint, const Point3 &cDefault) { // Is the given value equal to the given default value? if (cPoint.x != cDefault.x || cPoint.y != cDefault.y || cPoint.z != cDefault.z) { // Value to string const String sValue = String::Format("%f %f %f", cPoint.x, cPoint.y, cPoint.z); // Due to rounding errors the string may now or may not contain the given default value, check for this float fValues[3] = { 0.0f, 0.0f, 0.0f }; ParseTools::ParseFloatArray(sValue, fValues, 3); if (fValues[0] != cDefault.x || fValues[1] != cDefault.y || fValues[2] != cDefault.z) cXmlElement.SetAttribute(sName, sValue); } else { // The given value is equal to the default value -> nothing to do } }
/** * @brief * Saves a texture */ void PLSceneMaterial::SaveTexture(XmlElement &cMaterialElement, const String &sFilename, const String &sSemantic, bool bNormalMap_xGxR) { // Get known semantic String sPLSemantic = sSemantic; if (sPLSemantic.CompareNoCase("Ambient Color", 0, 13) || sPLSemantic.CompareNoCase("EmissiveMap", 0, 11)) sPLSemantic = "EmissiveMap"; else if (sPLSemantic.CompareNoCase("Diffuse Color", 0, 13) || sPLSemantic.CompareNoCase("DiffuseMap", 0, 10)) sPLSemantic = "DiffuseMap"; else if (sPLSemantic.CompareNoCase("Specular Color", 0, 14) || sPLSemantic.CompareNoCase("SpecularMap", 0, 11)) sPLSemantic = "SpecularMap"; else if (sPLSemantic.CompareNoCase("Self-Illumination", 0, 17) || sPLSemantic.CompareNoCase("LightMap", 0, 8)) sPLSemantic = "LightMap"; else if (sPLSemantic.CompareNoCase("Filter Color", 0, 12) || sPLSemantic.CompareNoCase("DetailNormalMap", 0, 15)) sPLSemantic = "DetailNormalMap"; else if (sPLSemantic.CompareNoCase("Bump", 0, 4) || sPLSemantic.CompareNoCase("NormalMap", 0, 9)) sPLSemantic = "NormalMap"; else if (sPLSemantic.CompareNoCase("Reflection", 0, 10) || sPLSemantic.CompareNoCase("ReflectionMap", 0, 13)) sPLSemantic = "ReflectionMap"; else if (sPLSemantic.CompareNoCase("Refraction", 0, 10) || sPLSemantic.CompareNoCase("ReflectivityMap", 0, 15)) sPLSemantic = "ReflectivityMap"; else if (sPLSemantic.CompareNoCase("Displacement", 0, 13) ||sPLSemantic.CompareNoCase("HeightMap", 0, 9)) sPLSemantic = "HeightMap"; else { // Unknown to the exporter - just export it anyway so we don't loose important information } // Copy texture if there's a known semantic if (sPLSemantic.GetLength()) { PLSceneTexture *pTexture = m_pScene->CopyTexture(sFilename, bNormalMap_xGxR); if (pTexture) { // Add to material XmlElement *pTextureElement = new XmlElement("Texture"); pTextureElement->SetAttribute("Name", sPLSemantic); // Add value XmlText *pValue = new XmlText(pTexture->GetName()); pTextureElement->LinkEndChild(*pValue); // Link texture element cMaterialElement.LinkEndChild(*pTextureElement); } } }
/** * @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++; } } } }
/** * @brief * Constructor */ PLSceneTexture::PLSceneTexture(PLScene &cScene, const String &sName, bool bNormalMap_xGxR) : m_pScene(&cScene), m_sName(sName), m_nReferenceCount(0) { // Cut of the path of the map name - if there's one String sAbsBitmapFilename = m_sName; // Get the texture name m_sName = sName; // Check options if (g_SEOptions.bCopyTextures) { // Can we use the given absolute filename? HANDLE hFile = CreateFileW(sAbsBitmapFilename.GetUnicode(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); if (hFile == INVALID_HANDLE_VALUE) { // Get the current path of the loaded 3ds Max scene String sCurFilePath = Url(GetCOREInterface()->GetCurFilePath().data()).CutFilename(); if (sCurFilePath.GetLength()) { // Compose absolute filename by just concatenating the two filenames (for relative filenames) String sBitmapFilename = sCurFilePath + sAbsBitmapFilename; hFile = CreateFileW(sBitmapFilename.GetUnicode(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); if (hFile == INVALID_HANDLE_VALUE) { // Get the filename without any path information String sFilenameOnly = Url(sName).GetFilename().GetASCII(); // Compose absolute filename if (sFilenameOnly.GetLength()) { char nLastCharacter = sCurFilePath[sCurFilePath.GetLength()-1]; if (nLastCharacter == '\\' || nLastCharacter == '/') sBitmapFilename = sCurFilePath + sFilenameOnly; else sBitmapFilename = sCurFilePath + "\\" + sFilenameOnly; hFile = CreateFileW(sBitmapFilename.GetUnicode(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); if (hFile == INVALID_HANDLE_VALUE) { // Check map directories int nMapDirCount = TheManager->GetMapDirCount(); for (int nMapDir=0; nMapDir<nMapDirCount; nMapDir++) { const String sMapDir = TheManager->GetMapDir(nMapDir); const uint32 nLength = sMapDir.GetLength(); if (nLength) { nLastCharacter = sMapDir[nLength-1]; if (nLastCharacter == '\\' || nLastCharacter == '/') sBitmapFilename = sMapDir + sFilenameOnly; else sBitmapFilename = sMapDir + '\\' + sFilenameOnly; hFile = CreateFileW(sBitmapFilename.GetUnicode(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); if (hFile != INVALID_HANDLE_VALUE) break; else sAbsBitmapFilename = sBitmapFilename; } } } else { sAbsBitmapFilename = sBitmapFilename; } } } else { sAbsBitmapFilename = sBitmapFilename; } } } if (hFile != INVALID_HANDLE_VALUE) { // Get source file time and close it FILETIME sSourceCreationTime; FILETIME sSourceLastAccessTime; FILETIME sSourceLastWriteTime; GetFileTime(hFile, &sSourceCreationTime, &sSourceLastAccessTime, &sSourceLastWriteTime); CloseHandle(hFile); // Cut of the filename String sFilename = Url(g_SEOptions.sFilename).CutFilename(); // Construct the absolute target filename uint32 nLength = sFilename.GetLength(); if (nLength) { sFilename = sFilename + m_sName; // Is there already such a file? If yes, check the file times... hFile = CreateFileW(sFilename.GetUnicode(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); if (hFile != INVALID_HANDLE_VALUE) { // Get target file time and close it FILETIME sTargetCreationTime; FILETIME sTargetLastAccessTime; FILETIME sTargetLastWriteTime; GetFileTime(hFile, &sTargetCreationTime, &sTargetLastAccessTime, &sTargetLastWriteTime); CloseHandle(hFile); // Compare file time long nResult = CompareFileTime(&sTargetLastWriteTime, &sSourceLastWriteTime); if (nResult >= 0) return; // Nothing to do :) } { // Before we copy, we need to ensure that the target directory is there, else 'CopyFile()' will fail! Directory cDirectory(Url(sFilename).CutFilename()); cDirectory.CreateRecursive(); } // Copy the texture (bitmap) CopyFileW(sAbsBitmapFilename.GetUnicode(), sFilename.GetUnicode(), false); // If there's a 'plt'-file for the texture, copy it, too int nIndex = sFilename.LastIndexOf("."); if (nIndex >= 0) { sFilename.Delete(nIndex); sFilename += ".plt"; nIndex = sAbsBitmapFilename.LastIndexOf("."); if (nIndex >= 0) { sAbsBitmapFilename.Delete(nIndex); sAbsBitmapFilename += ".plt"; if (!CopyFileW(sAbsBitmapFilename.GetUnicode(), sFilename.GetUnicode(), false)) { // Failed to copy the 'plt'-file... if (bNormalMap_xGxR) { // Create an automatic 'plt'-file... // Create XML document XmlDocument cDocument; // Add declaration XmlDeclaration *pDeclaration = new XmlDeclaration("1.0", "ISO-8859-1", ""); cDocument.LinkEndChild(*pDeclaration); // Add texture XmlElement *pTextureElement = new XmlElement("Texture"); // Setup attribute pTextureElement->SetAttribute("Version", "1"); // Add general XmlElement *pGeneralElement = new XmlElement("Node"); pGeneralElement->SetAttribute("Compression", "DXT5_xGxR"); // Link general element pTextureElement->LinkEndChild(*pGeneralElement); // Link material element cDocument.LinkEndChild(*pTextureElement); // Save settings if (cDocument.Save(sFilename)) g_pLog->LogFLine(PLLog::Hint, "Created '%s'", sFilename.GetASCII()); else g_pLog->LogFLine(PLLog::Error, "Can't create '%s'!", sFilename.GetASCII()); } } } } } } else { g_pLog->LogFLine(PLLog::Error, "Can't find texture (bitmap) '%s'!", m_sName.GetASCII()); } } }
//[-------------------------------------------------------] //[ Private virtual PLSceneNode functions ] //[-------------------------------------------------------] void PLSceneSpline::WriteToFile(XmlElement &cSceneElement, const String &sApplicationDrive, const String &sApplicationDir) { // Do NOT save it as scene node, it's just a 'resource' // Get path filename const String sFilename = sApplicationDrive + sApplicationDir + PLTools::GetResourceFilename(PLTools::ResourcePath, GetName() + ".path"); // Get the IGame spline object of the given IGame node IGameObject *pIGameObject = GetIGameNode()->GetIGameObject(); if (pIGameObject) { // Check the type of the IGame object if (pIGameObject->GetIGameType() == IGameObject::IGAME_SPLINE && pIGameObject->InitializeData()) { IGameSpline &cIGameSpline = *static_cast<IGameSpline*>(pIGameObject); if (cIGameSpline.GetNumberOfSplines () > 0) { // We only support spline 0 IGameSpline3D *pIGameSpline3D = cIGameSpline.GetIGameSpline3D(0); if (pIGameSpline3D) { // Get the local transform matrix GMatrix mTransform = cIGameSpline.GetIGameObjectTM(); // Get the 3ds Max shape object ShapeObject *pMaxShapeObject = static_cast<ShapeObject*>(pIGameObject->GetMaxObject()->ConvertToType(TIME_PosInfinity, Class_ID(GENERIC_SHAPE_CLASS_ID, 0))); if (pMaxShapeObject != nullptr && pMaxShapeObject->NumberOfCurves() == cIGameSpline.GetNumberOfSplines()) { // Create XML document XmlDocument cDocument; // Add declaration XmlDeclaration *pDeclaration = new XmlDeclaration("1.0", "ISO-8859-1", ""); cDocument.LinkEndChild(*pDeclaration); // Add path XmlElement *pPathElement = new XmlElement("Path"); // Setup attributes pPathElement->SetAttribute("Version", "1"); pPathElement->SetAttribute("Closed", String::Format("%d", pMaxShapeObject->CurveClosed(0, 0))); // Add all nodes for (int nKnot=0; nKnot<pIGameSpline3D->GetIGameKnotCount(); nKnot++) { IGameKnot *pIGameKnot = pIGameSpline3D->GetIGameKnot(nKnot); if (pIGameKnot) { // Get knot point in object space (although it is not documented it looks like object space...) Point3 cPoint = pIGameKnot->GetKnotPoint(); // We really need to flip the coordinates to OpenGL style, IGame is not doing this automatically... cPoint = PLTools::Convert3dsMaxVectorToOpenGLVector(cPoint); // Transform to world space cPoint = cPoint*mTransform; // If there's a parent container, make the position of this scene node relative to it PLSceneContainer *pContainer = GetContainer(); if (pContainer) cPoint -= pContainer->GetWorldSpaceCenter(); // Add node XmlElement *pNodeElement = new XmlElement("Node"); pNodeElement->SetAttribute("Name", String::Format("%d", nKnot)); pNodeElement->SetAttribute("Position", String::Format("%f %f %f", cPoint.x, cPoint.y, cPoint.z)); // Link general element pPathElement->LinkEndChild(*pNodeElement); } } // Link material element cDocument.LinkEndChild(*pPathElement); // Save settings if (cDocument.Save(sFilename)) g_pLog->LogFLine(PLLog::Hint, "Created '%s'", sFilename.GetASCII()); else g_pLog->LogFLine(PLLog::Error, "Can't create '%s'!", sFilename.GetASCII()); } } } } else { g_pLog->LogFLine(PLLog::Error, "%s: IGame object is no known spline object!", GetIGameNode()->GetName()); } // Release the IGame object GetIGameNode()->ReleaseIGameObject(); } else { g_pLog->LogFLine(PLLog::Error, "%s: IGame node has no IGame object!", GetIGameNode()->GetName()); } }