// NeL - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "stdafx.h" #include "export_nel.h" #include "export_appdata.h" #include "calc_lm.h" #include "nel/misc/path.h" #include "nel/3d/texture_file.h" #include // class IDerivedObject #include // class IDerivedObject #include using namespace NLMISC; using namespace NL3D; // *************************************************************************** // -------------------------------------------------- const char* CExportNel::ErrorMessage[CodeCount]= { "No error.", "Some vertices of the skin are not assigned.", "The skin doesn't use the same skeleton.", }; // -------------------------------------------------- CExportNel::CExportNel (bool errorInDialog, bool view, bool absolutePath, Interface *ip, std::string errorTitle, CExportNelOptions *opt) { _Ip = ip; _AbsolutePath = absolutePath; _View = view; _ErrorInDialog = errorInDialog; _ErrorTitle = errorTitle; // No options ? if (opt) _Options = *opt; } // -------------------------------------------------- /// Transforme a 3dsmax view matrix to camera matrix. Matrix3 CExportNel::viewMatrix2CameraMatrix (const Matrix3& viewMatrix) { // Init the return matrix Matrix3 mat; mat.Invert(); // Get invert viewMatrix // We invert view matrix to get camera matrix in viewport space Matrix3 invertViewMatrix=viewMatrix; invertViewMatrix.Invert(); // This matrix is used to compute the coordinates to the viewport space. Take ths invert. Matrix3 viewportInvert; viewportInvert.SetRow (0, Point3(1.f, 0.f, 0.f)); viewportInvert.SetRow (1, Point3(0.f, 0.f, 1.f)); viewportInvert.SetRow (2, Point3(0.f, -1.f, 0.f)); viewportInvert.SetRow (3, Point3(0.f, 0.f, 0.f)); viewportInvert.Invert(); // Compose with invert viewport matrix mat=viewportInvert*invertViewMatrix; // return the computed matrix return mat; } // -------------------------------------------------- // ConvertMatrix a 3dsmax vector in NeL matrix void CExportNel::convertVector (CVector& nelVector, const Point3& maxVector) { nelVector.x = maxVector.x; nelVector.y = maxVector.y; nelVector.z = maxVector.z; } // -------------------------------------------------- // ConvertMatrix a 3dsmax matrix in NeL matrix void CExportNel::convertMatrix (CMatrix& nelMatrix, const Matrix3& maxMatrix) { // Basis vector CVector I, J, K, P; // Build the rot matrix I.x= maxMatrix.GetRow(0).x; I.y= maxMatrix.GetRow(0).y; I.z= maxMatrix.GetRow(0).z; J.x= maxMatrix.GetRow(1).x; J.y= maxMatrix.GetRow(1).y; J.z= maxMatrix.GetRow(1).z; K.x= maxMatrix.GetRow(2).x; K.y= maxMatrix.GetRow(2).y; K.z= maxMatrix.GetRow(2).z; // Build the translation vector P.x= maxMatrix.GetTrans().x; P.y= maxMatrix.GetTrans().y; P.z= maxMatrix.GetTrans().z; // *** Build the NeL matrix // Set it to identity to have the good flags in it nelMatrix.identity(); // Set the rotation part nelMatrix.setRot(I, J, K); // Set the position part nelMatrix.setPos(P); } // -------------------------------------------------- // ConvertMatrix a 3dsmax matrix in NeL matrix void CExportNel::convertMatrix (Matrix3& maxMatrix, const CMatrix& nelMatrix) { // Basis vector Point3 I, J, K, P; // Build the rot matrix I.x= nelMatrix.getI().x; I.y= nelMatrix.getI().y; I.z= nelMatrix.getI().z; J.x= nelMatrix.getJ().x; J.y= nelMatrix.getJ().y; J.z= nelMatrix.getJ().z; K.x= nelMatrix.getK().x; K.y= nelMatrix.getK().y; K.z= nelMatrix.getK().z; // Build the translation vector P.x= nelMatrix.getPos().x; P.y= nelMatrix.getPos().y; P.z= nelMatrix.getPos().z; // *** Build the NeL matrix // Set it to identity to have the good flags in it maxMatrix.IdentityMatrix(); // Set the rotation part maxMatrix.SetRow (0, I); maxMatrix.SetRow (1, J); maxMatrix.SetRow (2, K); // Set the position part maxMatrix.SetTrans (P); } // -------------------------------------------------- // Convert a 3dsmax color in NeL color void CExportNel::convertColor (CRGBA& nelColor, const Color& maxColor) { // Convert from max to Nel float fR=maxColor.r*255.f+0.5f; float fG=maxColor.g*255.f+0.5f; float fB=maxColor.b*255.f+0.5f; clamp (fR, 0.f, 255.f); clamp (fG, 0.f, 255.f); clamp (fB, 0.f, 255.f); nelColor.R=(uint8)fR; nelColor.G=(uint8)fG; nelColor.B=(uint8)fB; // Alpha nelColor.A=255; } // -------------------------------------------------- // Return true if the texmap is a texture file else false bool CExportNel::isClassIdCompatible (Animatable& anim, Class_ID& classId) { // Check if it is a file texmap if ( (anim.ClassID() == classId) || ((anim.SuperClassID() == classId.PartA())&&(classId.PartB()==0)) ) { return true; } return false; } // -------------------------------------------------- // Return the pointer on the subanim with the name sName of the node. If it doesn't exist, return NULL. Animatable* CExportNel::getSubAnimByName (Animatable& node, const char* sName) { // Search for the subanim for (int nSub=0; nSubNumParams(); nSub++) { // Index of the parameter ParamID id=param->IndextoID(nSub); // Get the paramDef ParamDef& paramDef=param->GetParamDef(id); // Good name? if (strcmp (paramDef.int_name, sName)==0) { // ok, return this subanim return param->GetController(id); } } } // Get num sub anim uint numSubAnim=node.NumSubs(); // Visit sub anim for (uint s=0; sNumParams(); nSub++) { // Index of the parameter ParamID id=param->IndextoID(nSub); // Get the paramDef ParamDef& paramDef=param->GetParamDef(id); // Good name? if (strcmp (paramDef.int_name, sName)==0) { // Check this value is good type ParamType2 paramType = param->GetParameterType(id); bool typeOk; switch (type) { case TYPE_FILENAME: case TYPE_STRING: typeOk = (paramType == TYPE_FILENAME) || (paramType == TYPE_STRING); break; case TYPE_FILENAME_TAB: case TYPE_STRING_TAB: typeOk = (paramType == TYPE_FILENAME_TAB) || (paramType == TYPE_STRING_TAB); break; default: typeOk = (paramType == type); break; } if (typeOk) { // Get the value Interval ivalid=FOREVER; BOOL bRes=FALSE; switch (type) { case TYPE_PCNT_FRAC: case TYPE_FLOAT: bRes=param->GetValue(id, tvTime, *(float*)pValue, ivalid); break; case TYPE_BOOL: case TYPE_INT: bRes=param->GetValue(id, tvTime, *(int*)pValue, ivalid); break; case TYPE_RGBA: case TYPE_POINT3: bRes=param->GetValue(id, tvTime, *(Point3*)pValue, ivalid); break; case TYPE_FILENAME: case TYPE_STRING: *(std::string*)pValue = param->GetStr (id, tvTime); bRes = TRUE; break; case TYPE_FILENAME_TAB: case TYPE_STRING_TAB: { std::vector &rTab = *(std::vector*)pValue; uint total = param->Count (id); rTab.resize(total); for( uint i = 0; i < total; ++i ) rTab[i] = param->GetStr (id, tvTime, i); bRes = TRUE; } break; case TYPE_BOOL_TAB: { std::vector &rTab = *(std::vector*)pValue; uint total = param->Count (id); rTab.resize(total); for( uint i = 0; i < total; ++i ) rTab[i] = param->GetInt(id, tvTime, i) ? true : false; bRes = TRUE; } break; case TYPE_TEXMAP: { bRes=param->GetValue(id, tvTime, *(Texmap**)pValue, ivalid); } break; } // Get successful ? if (bRes) return true; } else { nldebug("Invalid type specified for pblock2 value with name '%s', given type '%u', found '%u'", sName, (uint32)type, (uint32)paramType); } } } } // Get num sub anim uint numSubAnim=node.NumSubs(); // Visit sub anim for (uint s=0; sGetObjectRef(); if (!pObj) return NULL; ObjectState os = pNode->EvalWorldState(0); if (os.obj && (os.obj->SuperClassID() != GEOMOBJECT_CLASS_ID) && (os.obj->SuperClassID() != LIGHT_CLASS_ID) ) { return NULL; } // For all derived objects (can be > 1) while (pObj && (pObj->SuperClassID() == GEN_DERIVOB_CLASS_ID)) { IDerivedObject* pDObj = (IDerivedObject*)pObj; int m; int nNumMods = pDObj->NumModifiers(); // Step through all modififers and verify the class id for (m=0; mGetModifier(m); if (pMod) { if (pMod->ClassID() == modCID) { // Match! Return it return pMod; } } } pObj = pDObj->GetObjRef(); } return NULL; } // -------------------------------------------------- // Get the material name std::string CExportNel::getName (MtlBase& mtl) { // Return its name TSTR name; name=mtl.GetName(); return std::string (name); } // -------------------------------------------------- // Get the node name std::string CExportNel::getName (INode& mtl) { // Return its name TCHAR* name=mtl.GetName(); return std::string (name); } // -------------------------------------------------- // Get the NEL node name std::string CExportNel::getNelObjectName (INode& node) { // Workaround for FX (don't know why, but the AppData are not copied when FX are duplicated, so try to get the name in another way) // If this is a particle system, try to get the name of the shape.from the param blocks Object *obj = node.EvalWorldState(0).obj; // Check if there is an object if (obj) { Class_ID clid = obj->ClassID(); // is the object a particle system ? (we do this defore meshs, because for now there is a mesh in max scenes to say where a particle system is...) if (clid.PartA() == NEL_PARTICLE_SYSTEM_CLASS_ID) { std::string shapeName; if (CExportNel::getValueByNameUsingParamBlock2(node, "ps_file_name", (ParamType2) TYPE_STRING, &shapeName, 0)) { return NLMISC::CFile::getFilename(shapeName); } } } // Try to get an APPDATA for the name of the object AppDataChunk *ad = node.GetAppDataChunk (MAXSCRIPT_UTILITY_CLASS_ID, UTILITY_CLASS_ID, NEL3D_APPDATA_INSTANCE_SHAPE); if (ad&&ad->data) { if (::strlen((const char *) ad->data) != 0) { // Get the name of the object in the APP data return (const char*)ad->data; } else { return node.GetName(); } } else { Object *obj = node.EvalWorldState(0).obj; if (obj) { ad = obj->GetAppDataChunk (MAXSCRIPT_UTILITY_CLASS_ID, UTILITY_CLASS_ID, NEL3D_APPDATA_INSTANCE_SHAPE); if (ad&&ad->data) { if (::strlen((const char *) ad->data) != 0) { // get file name only char fName[_MAX_FNAME]; char ext[_MAX_FNAME]; ::_splitpath((const char*)ad->data, NULL, NULL, fName, ext) ; return std::string(fName + std::string(ext)); } else { return node.GetName(); } } else { // Extract the node name return node.GetName(); } } else { // Extract the node name return node.GetName(); } } } // -------------------------------------------------- void CExportNel::decompMatrix (NLMISC::CVector& nelScale, NLMISC::CQuat& nelRot, NLMISC::CVector& nelPos, const Matrix3& maxMatrix) { // Use decomp part of the max SDK AffineParts parts; decomp_affine(maxMatrix, &parts); // Check Matrix3 srtm, rtm, ptm, stm, ftm; ptm.IdentityMatrix(); ptm.SetTrans(parts.t); parts.q.MakeMatrix(rtm); parts.u.MakeMatrix(srtm); stm = ScaleMatrix(parts.k); ftm = ScaleMatrix(Point3(parts.f,parts.f,parts.f)); Matrix3 mat = Inverse(srtm) * stm * srtm * rtm * ftm * ptm; // Set the translation nelPos.x=parts.t.x; nelPos.y=parts.t.y; nelPos.z=parts.t.z; // Set the rotation nelRot.x=parts.q.x; nelRot.y=parts.q.y; nelRot.z=parts.q.z; nelRot.w=-parts.q.w; // Make a scale matrix parts.u.MakeMatrix(srtm); stm = ScaleMatrix(parts.k); mat = Inverse(srtm) * stm * srtm; // Get a NeL matrix CMatrix scaleMatrix; convertMatrix (scaleMatrix, mat); // Take the scale of this matrix, not very smart but.. nelScale.x=parts.f*scaleMatrix.getI().x; nelScale.y=parts.f*scaleMatrix.getJ().y; nelScale.z=parts.f*scaleMatrix.getK().z; } // -------------------------------------------------- void CExportNel::getLocalMatrix (Matrix3& localMatrix, INode& node, TimeValue time) { INode *parent; Matrix3 parentTM, nodeTM; nodeTM = node.GetNodeTM (time); parent = node.GetParentNode (); if (parent) { parentTM = parent->GetNodeTM (time); localMatrix = nodeTM*Inverse (parentTM); } else localMatrix = nodeTM; } // -------------------------------------------------- bool CExportNel::isMesh (INode& node, TimeValue time, bool excludeCollision) { // Result false by default bool bRet=false; // Eval the object a time ObjectState os = node.EvalWorldState(time); // Object exist ? if (os.obj) { // Object can convert itself to NeL patchmesh ? if (os.obj->CanConvertToType(Class_ID(TRIOBJ_CLASS_ID, 0))) bRet=true; } // Want to exclude collision Mesh?? if(excludeCollision) { // Object is flagged as a collision? int bCol= getScriptAppData(&node, NEL3D_APPDATA_COLLISION, BST_UNCHECKED); if(bCol == BST_CHECKED) bRet= false; } // Return result return bRet; } // -------------------------------------------------- bool CExportNel::isCamera (INode& node, TimeValue time) { // Result false by default bool bRet=false; // Eval the object a time ObjectState os = node.EvalWorldState(time); // Object exist ? if (os.obj) { // Object can convert itself to NeL patchmesh ? if (os.obj->CanConvertToType(Class_ID(LOOKAT_CAM_CLASS_ID, 0))) bRet=true; } // Return result return bRet; } // -------------------------------------------------- bool CExportNel::isDummy (INode& node, TimeValue time) { ObjectState os = node.EvalWorldState(time); return os.obj->ClassID().PartA() == DUMMY_CLASS_ID; } // -------------------------------------------------- bool CExportNel::hasLightMap (INode& node, TimeValue time) { if( !isMesh(node,time) ) return false; int nMaterialCount=0; // Get primary material pointer of the node Mtl* pNodeMat=node.GetMtl(); // If NULL, no material at all at this node if (pNodeMat==NULL) return false; // Number of sub material at in this material nMaterialCount=pNodeMat->NumSubMtls(); // If it is a multisub object, export all its sub materials if (nMaterialCount>0) { // Check all the sub materials for (int nSub=0; nSubGetSubMtl(nSub); // Should not be NULL nlassert (pSub); // Is there a lightmap handling wanted int bLightMap = 0; // false CExportNel::getValueByNameUsingParamBlock2 (*pSub, "bLightMap", (ParamType2)TYPE_BOOL, &bLightMap, 0); if (bLightMap) return true; } } // Else check only this material else { int bLightMap = 0; // false CExportNel::getValueByNameUsingParamBlock2 (*pNodeMat, "bLightMap", (ParamType2)TYPE_BOOL, &bLightMap, 0); if (bLightMap) return true; } return false; } // -------------------------------------------------- void CExportNel::outputErrorMessage (const char *message) { if (_ErrorInDialog) { MessageBox (_Ip->GetMAXHWnd(), message, _ErrorTitle.c_str(), MB_OK|MB_ICONEXCLAMATION); } mprintf (message); mprintf ("\n"); nlwarning ("Error in max file %s : ", _Ip->GetCurFilePath()); nlwarning (message); } // -------------------------------------------------- void CExportNel::outputWarningMessage (const char *message) { if (_ErrorInDialog) { MessageBox (_Ip->GetMAXHWnd(), message, _ErrorTitle.c_str(), MB_OK|MB_ICONEXCLAMATION); } mprintf (message); mprintf ("\n"); nlwarning ("Warning in max file %s : ", _Ip->GetCurFilePath()); nlwarning (message); } // -------------------------------------------------- bool CExportNel::isVegetable (INode& node, TimeValue time) { return CExportNel::getScriptAppData (&node, NEL3D_APPDATA_VEGETABLE, BST_UNCHECKED) != BST_UNCHECKED; } // -------------------------------------------------- bool CExportNel::isLodCharacter (INode& node, TimeValue time) { return CExportNel::getScriptAppData (&node, NEL3D_APPDATA_CHARACTER_LOD, BST_UNCHECKED) != BST_UNCHECKED; } // -------------------------------------------------- void CExportNel::addChildLodNode (std::set &lodListToExclude, INode *current) { // First node ? if (current == NULL) current = _Ip->GetRootNode(); // Get child count uint lodCount = getScriptAppData (current, NEL3D_APPDATA_LOD_NAME_COUNT, 0); for (uint lod=0; lodGetINodeByName (lodName.c_str()); if (lodNode) { // Insert it in the set lodListToExclude.insert (lodNode); } } } // Scan child nodes for ( uint i = 0; i < (uint)current->NumberOfChildren(); ++i ) addChildLodNode ( lodListToExclude, current->GetChildNode(i) ); } // -------------------------------------------------- void CExportNel::addParentLodNode (INode &child, std::set &lodListToExclude, INode *parent) { // First node ? if (parent == NULL) parent = _Ip->GetRootNode(); // Get its child lod uint lodCount = getScriptAppData (parent, NEL3D_APPDATA_LOD_NAME_COUNT, 0); for (uint lod=0; lodGetINodeByName (lodName.c_str()); if (lodNode == &child) { // Insert it in the set lodListToExclude.insert (parent); break; } } } // Scan child nodes for ( uint i = 0; i < (uint)parent->NumberOfChildren(); ++i ) addParentLodNode ( child, lodListToExclude, parent->GetChildNode(i) ); } // -------------------------------------------------- /// Transform a 3dsmax uv matrix to a nel uv matrix void CExportNel::uvMatrix2NelUVMatrix (const Matrix3& maxMatrix, NLMISC::CMatrix &dest) { // Basis vector CVector I, J, K, P; // Build the rot matrix I.x= maxMatrix.GetRow(0).x; I.y= maxMatrix.GetRow(0).y; I.z= maxMatrix.GetRow(0).z; J.x= maxMatrix.GetRow(1).x; J.y= maxMatrix.GetRow(1).y; J.z= maxMatrix.GetRow(1).z; K.x= maxMatrix.GetRow(2).x; K.y= maxMatrix.GetRow(2).y; K.z= maxMatrix.GetRow(2).z; // Build the translation vector P.x= maxMatrix.GetTrans().x; P.y= maxMatrix.GetTrans().y; P.z= maxMatrix.GetTrans().z; // *** Build the NeL matrix // Set it to identity to have the good flags in it dest.identity(); // Set the rotation part dest.setRot(I, J, K); // Set the position part dest.setPos(P); // transfo matrix CMatrix convert; convert.setRot(CVector::I, -CVector::J, CVector::K); convert.setPos(CVector::J); dest = convert * dest * convert; // exported v are already inverted therefore the conversion } // -------------------------------------------------- void CExportNel::getObjectNodes (std::vector& vectNode, TimeValue time, INode* node) { // Get the root node if (node==NULL) node=_Ip->GetRootNode(); // Get a pointer on the object's node Object *obj = node->EvalWorldState(time).obj; // Check if there is an object if (obj) { // Append this node. vectNode.push_back(node); } // Recurse sub node for (int i=0; iNumberOfChildren(); i++) getObjectNodes (vectNode, time, node->GetChildNode(i)); } // -------------------------------------------------- INode *CExportNel::getRootNode() const { return _Ip->GetRootNode(); } // *********************************************************************************************** std::string CExportNel::getAnimatedLight (INode *node) { std::string ret = CExportNel::getScriptAppData (node, NEL3D_APPDATA_LM_ANIMATED_LIGHT, NEL3D_APPDATA_LM_ANIMATED_LIGHT_DEFAULT); if (ret == "Sun") ret = ""; if (ret == "GlobalLight") ret = ""; if (ret == "(Use NelLight Modifier)") ret = ""; return ret; } // *********************************************************************************************** uint CExportNel::getLightGroup (INode *node) { return CExportNel::getScriptAppData (node, NEL3D_APPDATA_LM_LIGHT_GROUP, NEL3D_APPDATA_LM_LIGHT_GROUP_DEFAULT); } // *********************************************************************************************** // a segment (used by maxPolygonMeshToOrderedPoly) struct CMaxMeshSeg { uint V0, V1; // for map insertion bool operator<(const CMaxMeshSeg &other) const { uint lv0 = std::min(V0, V1); uint lv1 = std::max(V0, V1); uint rv0 = std::min(other.V0, other.V1); uint rv1 = std::max(other.V0, other.V1); if (lv0 != rv0) return lv0 < rv0; return lv1 < rv1; } bool operator==(const CMaxMeshSeg &other) const { return !(*this < other) && !(other < *this); } CMaxMeshSeg(uint v0, uint v1) : V0(v0), V1(v1) { } }; // private : predicate to ordonate segments (used by maxPolygonMeshToOrderedPoly) struct CPredNextSegOf { uint Prev; CPredNextSegOf(uint prev) : Prev(prev) {} bool operator()(const CMaxMeshSeg &value) const { return value.V0 == Prev || value.V1 == Prev; } }; /// Get normal of a max triangle in nel format static NLMISC::CVector getMaxFaceNormal(const Mesh &m, const NLMISC::CMatrix &basis, uint faceIndex) { CVector corner[3]; for(uint k = 0; k < 3; ++k) { CExportNel::convertVector(corner[k], m.verts[m.faces[faceIndex].v[k]]); corner[k]= basis * corner[k]; } CVector normal = (corner[1] - corner[0]) ^ (corner[2] - corner[1]); normal.normalize(); return normal; } // -------------------------------------------------- // This convert a polygon expressed as a max mesh into a list of ordered vectors void CExportNel::maxPolygonMeshToOrderedPoly(Mesh &mesh, std::vector &dest, const NLMISC::CMatrix &basis, NLMISC::CVector &avgNormal) { /// We use a very simple (but slow) algo : examine for each segment how many tris share it. If it is one then it is a border seg /// Then, just order segments typedef std::map TSegMap; avgNormal.set(0, 0, 0); ///////////////////////////////////////////////////////////// // count the number of ref by a triangle for each segment // ///////////////////////////////////////////////////////////// TSegMap segs; uint k; for(k = 0; k < (uint) mesh.getNumFaces(); ++k) { avgNormal += getMaxFaceNormal(mesh, basis, k); for(uint l = 0; l < 3; ++l) { CMaxMeshSeg seg(mesh.faces[k].v[l], mesh.faces[k].v[(l + 1) % 3]); TSegMap::iterator it = segs.find(seg); if (it != segs.end()) { ++ it->second; } else { // create a new entry segs[seg] = 1; } } } avgNormal.normalize(); //////////////////////////////////////// // keep segments for which nbref is 1 // //////////////////////////////////////// typedef std::list TSegList; TSegList borderSegs; for(TSegMap::const_iterator it = segs.begin(); it != segs.end(); ++it) { if (it->second == 1) borderSegs.push_back(it->first); } dest.clear(); if (borderSegs.empty()) return; /////////////////////// // ordonate segments // /////////////////////// NLMISC::CVector pos; CExportNel::convertVector(pos, mesh.verts[borderSegs.begin()->V0]); dest.push_back(basis * pos); uint nextToFind = borderSegs.begin()->V1; borderSegs.pop_front(); for(;;) { TSegList::iterator nextSeg = std::find_if(borderSegs.begin(), borderSegs.end(), CPredNextSegOf(nextToFind)); if (nextSeg == borderSegs.end()) return; CExportNel::convertVector(pos, mesh.verts[nextSeg->V0 == nextToFind ? nextSeg->V0 : nextSeg->V1]); dest.push_back(basis * pos); nextToFind = (nextSeg->V0 == nextToFind) ? nextSeg->V1 : nextSeg->V0; borderSegs.erase(nextSeg); } } static std::string OldDecimalSeparatorLocale; static void setDecimalSeparatorAsPoint() { OldDecimalSeparatorLocale = ::setlocale(LC_NUMERIC, NULL); ::setlocale(LC_NUMERIC, "English"); } static void restoreDecimalSeparator() { ::setlocale(LC_NUMERIC, OldDecimalSeparatorLocale.c_str()); } ///======================================================================= float toFloatMax(const char *src) { float result = 0.f; if (toFloatMax(src, result)) return result; return 0.f; } bool toFloatMax(const char *src, float &dest) { setDecimalSeparatorAsPoint(); std::string str(src); std::string::size_type pointPos = str.find_first_of(","); if (pointPos != std::string::npos) { str[pointPos] ='.'; } if (::sscanf(str.c_str(), "%g", &dest) == 1) { restoreDecimalSeparator(); return true; } restoreDecimalSeparator(); return false; /* if (!src || *src == '\0') return false; float result = 0.f; float sgn = 1.f; while (*src == ' ') ++src; if (*src == '-') { sgn = -1.f; ++src; } while (*src == ' ') ++src; bool numberFound = false; while (::isdigit(*src)) { numberFound = true; result *= 10.f; result += (float) (*src - '0'); ++src; } if (!(*src == '.' || *src == ',')) { if (numberFound) { dest = sgn * result; return true; } else return false; } ++src; if (!::isdigit(*src)) { if (!numberFound) { return false; } } float addValue = 0.1f; while (::isdigit(*src)) { result += addValue * (float) (*src - '0'); addValue *= 0.1f; ++ src; } dest = sgn * result; return true;*/ } ///======================================================================= std::string toStringMax(float value) { char result[256]; setDecimalSeparatorAsPoint(); ::sprintf(result, "%g", value); restoreDecimalSeparator(); return result; /* char result[256]; sprintf(result, "%g", value); char *point = strchr(result, ','); if (point) { *point = '.'; } return std::string(result); */ /* double intPart; value = (float) ::modf(value, &intPart); bool positive = value >= 0.f && intPart >= 0.f; value = ::fabsf(value); intPart = ::fabs(intPart); std::string result = toString(intPart); if (value == 0.f) return positive ? result : "-" + result; result += "."; float frac; do { value *= 10.f; frac = (float) ::modf((double) value, &intPart); result += toString((int) intPart % 10); } while (frac != 0.f); return positive ? result : "-" + result;*/ } ///======================================================================= std::string toStringMax(int value) { char result[256]; setDecimalSeparatorAsPoint(); ::sprintf(result, "%d", value); restoreDecimalSeparator(); return result; /*char str[256]; ::sprintf(str, "%d", value); return str;*/ } ///======================================================================= void CExportNel::buildCamera(NL3D::CCameraInfo &cameraInfo, INode& node, TimeValue time) { // Eval the object a time ObjectState os = node.EvalWorldState(time); // Object exist ? if (os.obj) { // Object can convert itself to NeL patchmesh ? GenCamera *genCamera = (GenCamera *)os.obj->ConvertToType(time, Class_ID (LOOKAT_CAM_CLASS_ID, 0)); if (genCamera) { INode *target = node.GetTarget(); if (target) { cameraInfo.TargetMode = true; cameraInfo.UseFov = true; bool deleteIt = genCamera != os.obj; // Camera position Point3 pos=node.GetNodeTM(time).GetTrans (); CVector position; position.x=pos.x; position.y=pos.y; position.z=pos.z; cameraInfo.Pos = position; // Target position pos=target->GetNodeTM(time).GetTrans (); position.x=pos.x; position.y=pos.y; position.z=pos.z; cameraInfo.TargetPos = position; // Set the roll parameter cameraInfo.Roll = 0; // Set the fov cameraInfo.Fov = genCamera->GetFOV(time); if (deleteIt) genCamera->DeleteMe(); } } } } bool CExportNel::isErrorInDialog () const { return _ErrorInDialog; }