/* Copyright (c) 2002-2012 Croteam Ltd. This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "base.h" #include FILE *_fpOutput = NULL; EDStateRef _state; extern int msgbox_modeler( LWXPanelFuncs *xpanf, const char* msg ); extern LWItemID _objid; LWSurfaceID *_asurSurfaces; extern char *_strFileName; // arrays of all point and polygon ids extern int _ctPntIDs; extern int _iPnt; extern LWPntID *_aidPntIDs; extern int _ctPolIDs; extern int _iPol; extern LWPolID *_aidPolIDs; // point coordinates extern float (*_avPnts)[3]; // polygon normals extern float (*_avPolNormals)[3]; // point normals extern float (*_avPntNormals)[3]; // uvmapnames extern const char **_astrUVMapNames; extern int _ctUVMapNames; extern int _ctUsedUVMapNames; // weightmapnames extern const char **_astrWeightMapNames; extern int *_actWeightMapCounts; extern int _ctWeightMapNames; extern int _ctUsedWeightMapNames; // relmorphmapnames extern const char **_astrRelMorphMapNames; extern int *_actRelMorphMapCounts; extern int _ctRelMorphMapNames; extern int _ctUsedRelMorphMapNames; // absmorphmapnames extern const char **_astrAbsMorphMapNames; extern int *_actAbsMorphMapCounts; extern int _ctAbsMorphMapNames; extern int _ctUsedAbsMorphMapNames; // here we store ids for all point and polygon combinations in order by polygons and by points, effectively // having all info needed to handle the unwelded object and info needed to calculate normals extern int _ctPolPnts; extern PolPnt *_appPolPnts; extern PolPnt *_appPntPols; extern int _ctSurfs; int EnumPnts_modeler(void *, const EDPointInfo *poiPointInfo) { _aidPntIDs[_iPnt++] = poiPointInfo->pnt; return 0; } int EnumPols_modeler(void *, const EDPolygonInfo *pliPollyInfo) { _aidPolIDs[_iPol++] = pliPollyInfo->pol; if (pliPollyInfo->numPnts != 3) { _msg->error("All objects must be triangles!", NULL); } _ctPolPnts += pliPollyInfo->numPnts; return 0; } int __cdecl qsort_CompareIDs_modeler(const void *elem1, const void *elem2) { return *(int*)elem1-*(int*)elem2; } int __cdecl qsort_ComparePolPntsByPnt_modeler(const void *elem1, const void *elem2) { PolPnt *pp1 = (struct PolPnt *)elem1; PolPnt *pp2 = (struct PolPnt *)elem2; return (int)pp1->pp_idPnt-(int)pp2->pp_idPnt; } // fill base vertex coordinates (without morphing) void FillOriginalVertexCoords_modeler(void) { EDPointInfo * poiPoint; // for each vertex for(int iPnt=0; iPnt<_ctPntIDs; iPnt++) { // get the coords poiPoint = _meshEditOperations->pointInfo(_state, _aidPntIDs[iPnt]); _avPnts[iPnt][0] = (float) poiPoint->position[0]; _avPnts[iPnt][1] = (float) poiPoint->position[1]; _avPnts[iPnt][2] = (float) poiPoint->position[2]; } } // fill vertex coordinates with relative morphing void FillRelativeMorphVertexCoords_modeler(const char *strRelMorphMapName) { // fill base vertex coordinates (without morphing) FillOriginalVertexCoords_modeler(); void *pMap = _meshEditOperations->pointVSet(_state,NULL, LWVMAP_MORF, strRelMorphMapName); // for each vertex {for(int iPnt=0; iPnt<_ctPntIDs; iPnt++) { // if morphed float v[3]; if (_meshEditOperations->pointVGet(_state, _aidPntIDs[iPnt], v)) { // apply the morph _avPnts[iPnt][0]+=v[0]; _avPnts[iPnt][1]+=v[1]; _avPnts[iPnt][2]+=v[2]; } }} } // fill vertex coordinates with absolute morphing void FillAbsoluteMorphVertexCoords_modeler(const char *strAbsMorphMapName) { // fill base vertex coordinates (without morphing) FillOriginalVertexCoords_modeler(); void *pMap = _meshEditOperations->pointVSet(_state,NULL, LWVMAP_SPOT, strAbsMorphMapName); // for each vertex {for(int iPnt=0; iPnt<_ctPntIDs; iPnt++) { // if morphed float v[3]; if (_meshEditOperations->pointVGet(_state, _aidPntIDs[iPnt], v)) { // apply the morph _avPnts[iPnt][0] = v[0]; _avPnts[iPnt][1] = v[1]; _avPnts[iPnt][2] = v[2]; } }} } int GetPntIndex_modeler(LWPntID id) { LWPntID *p = (LWPntID *)bsearch(&id, _aidPntIDs, _ctPntIDs, sizeof(id), qsort_CompareIDs_modeler); if (p==NULL) { assert(false); return 0; } else { return p-_aidPntIDs; } } int GetPolIndex_modeler(LWPolID id) { LWPolID *p = (LWPolID *)bsearch(&id, _aidPolIDs, _ctPolIDs, sizeof(id), qsort_CompareIDs_modeler); if (p==NULL) { assert(false); return 0; } else { return p-_aidPolIDs; } } void MakeNormals_modeler(void) { // generate polygon normals for(int iPol=0; iPol<_ctPolIDs; iPol++) { LWPolID idPol = _aidPolIDs[iPol]; double dPolNormal[3]; _meshEditOperations->polyNormal(_state,idPol,dPolNormal); _avPolNormals[iPol][0] = (float) dPolNormal[0]; _avPolNormals[iPol][1] = (float) dPolNormal[1]; _avPolNormals[iPol][2] = (float) dPolNormal[2]; } // generate point normals memset(_avPntNormals, 0, _ctPntIDs*sizeof(float)*3); float vNormalSum[3]; LWPntID idLastPnt = NULL; // for each point polygon {for(int iPntPol=0; iPntPol<_ctPolPnts; iPntPol++) { LWPntID idThis = _appPntPols[iPntPol].pp_idPnt; // if new point if (idThis!=idLastPnt) { // store value for the last point (unless it was the first one) if (idLastPnt!=NULL) { int iLastPnt = GetPntIndex_modeler(idLastPnt); normalize(vNormalSum); _avPntNormals[iLastPnt][0] = vNormalSum[0]; _avPntNormals[iLastPnt][1] = vNormalSum[1]; _avPntNormals[iLastPnt][2] = vNormalSum[2]; } // reset averaging values vNormalSum[0] = 0.0f; vNormalSum[1] = 0.0f; vNormalSum[2] = 0.0f; } // add the polygon normal to the averaging values int iPol = GetPolIndex_modeler(_appPntPols[iPntPol].pp_idPol); vNormalSum[0] += _avPolNormals[iPol][0]; vNormalSum[1] += _avPolNormals[iPol][1]; vNormalSum[2] += _avPolNormals[iPol][2]; idLastPnt=idThis; }} int iLastPnt = GetPntIndex_modeler(idLastPnt); normalize(vNormalSum); _avPntNormals[iLastPnt][0] = vNormalSum[0]; _avPntNormals[iLastPnt][1] = vNormalSum[1]; _avPntNormals[iLastPnt][2] = vNormalSum[2]; } // extract polygon and point info void ExtractMeshData_modeler(void) { // extract point and poly ids, so we can walk them later _aidPntIDs = (LWPntID*)malloc(_ctPntIDs*sizeof(LWPntID)); _iPnt = 0; _meshEditOperations->pointScan(_state,EnumPnts_modeler,NULL,OPLYR_FG); qsort(_aidPntIDs, _ctPntIDs, sizeof(LWPntID), qsort_CompareIDs_modeler); _aidPolIDs = (LWPolID*)malloc(_ctPolIDs*sizeof(LWPolID)); _iPol = 0; _ctPolPnts = 0; _meshEditOperations->polyScan(_state,EnumPols_modeler,NULL,OPLYR_FG); qsort(_aidPolIDs, _ctPolIDs, sizeof(LWPolID), qsort_CompareIDs_modeler); _appPolPnts = (PolPnt *)malloc(_ctPolPnts*sizeof(PolPnt)); _appPntPols = (PolPnt *)malloc(_ctPolPnts*sizeof(PolPnt)); // fill in all point and polygon combinations {int iPolPnt = 0; EDPolygonInfo *pliPolly; for(int iPol=0; iPol<_ctPolIDs; iPol++) { LWPolID idPol = _aidPolIDs[iPol]; pliPolly = _meshEditOperations->polyInfo(_state,idPol); int ctInThisPol = pliPolly->numPnts; for (int iPnt=0; iPntpoints[iPnt]; iPolPnt++; } }} // copy to per-point array and sort by points memcpy(_appPntPols, _appPolPnts, _ctPolPnts*sizeof(PolPnt)); qsort(_appPntPols, _ctPolPnts, sizeof(PolPnt), qsort_ComparePolPntsByPnt_modeler); // allocate point coords and polygon and point normals _avPnts = (float(*)[3])malloc(_ctPntIDs*sizeof(float)*3); _avPolNormals = (float(*)[3])malloc(_ctPolIDs*sizeof(float)*3); _avPntNormals = (float(*)[3])malloc(_ctPntIDs*sizeof(float)*3); // calculate mesh normals using base vertex coordinates (without morphing) FillOriginalVertexCoords_modeler(); MakeNormals_modeler(); // list all the uvmaps used by this object _ctUVMapNames = _objfunc->numVMaps(LWVMAP_TXUV); _astrUVMapNames = (const char**) malloc(_ctUVMapNames*sizeof(char*)); memset(_astrUVMapNames, 0, _ctUVMapNames*sizeof(char*)); _ctUsedUVMapNames = 0; {for(int iUVMap=0; iUVMap<_ctUVMapNames; iUVMap++) { const char *strName = _objfunc->vmapName(LWVMAP_TXUV, iUVMap); void *pMap = _meshEditOperations->pointVSet(_state,NULL, LWVMAP_TXUV, strName); bool bExists = false; for(int iPnt=0; iPnt<_ctPntIDs; iPnt++) { float v[2]; if (_meshEditOperations->pointVGet(_state, _aidPntIDs[iPnt], v)) { bExists = true; break; } } if (bExists) { _astrUVMapNames[_ctUsedUVMapNames++] = strName; } }} // list all the weightmaps used by this object _ctWeightMapNames = _objfunc->numVMaps(LWVMAP_WGHT); _astrWeightMapNames = (const char**) malloc(_ctWeightMapNames*sizeof(char*)); _actWeightMapCounts = (int *) malloc(_ctWeightMapNames*sizeof(int)); memset(_astrWeightMapNames, 0, _ctWeightMapNames*sizeof(char*)); memset(_actWeightMapCounts, 0, _ctWeightMapNames*sizeof(int)); _ctUsedWeightMapNames = 0; {for(int iWeightMap=0; iWeightMap<_ctWeightMapNames; iWeightMap++) { const char *strName = _objfunc->vmapName(LWVMAP_WGHT, iWeightMap); void *pMap = _meshEditOperations->pointVSet(_state,NULL, LWVMAP_WGHT, strName); int ct = 0; // for each polygonvertex for(int iPolPnt=0; iPolPnt<_ctPolPnts; iPolPnt++) { // get the weight float v[1]; if (_meshEditOperations->pointVGet(_state, _appPolPnts[iPolPnt].pp_idPnt, v) && v[0]!=0.0f) { ct++; } } if (ct>0) { _astrWeightMapNames[_ctUsedWeightMapNames] = strName; _actWeightMapCounts[_ctUsedWeightMapNames] = ct; _ctUsedWeightMapNames++; } }} // list all the relmorphmaps used by this object _ctRelMorphMapNames = _objfunc->numVMaps(LWVMAP_MORF); _astrRelMorphMapNames = (const char**) malloc(_ctRelMorphMapNames*sizeof(char*)); _actRelMorphMapCounts = (int *) malloc(_ctRelMorphMapNames*sizeof(int)); memset(_astrRelMorphMapNames, 0, _ctRelMorphMapNames*sizeof(char*)); memset(_actRelMorphMapCounts, 0, _ctRelMorphMapNames*sizeof(int)); _ctUsedRelMorphMapNames = 0; {for(int iRelMorphMap=0; iRelMorphMap<_ctRelMorphMapNames; iRelMorphMap++) { const char *strName = _objfunc->vmapName(LWVMAP_MORF, iRelMorphMap); void *pMap = _meshEditOperations->pointVSet(_state,NULL, LWVMAP_MORF, strName); int ct = 0; // for each polygonvertex for(int iPolPnt=0; iPolPnt<_ctPolPnts; iPolPnt++) { // get the morphpos float v[3]; if (_meshEditOperations->pointVGet(_state, _appPolPnts[iPolPnt].pp_idPnt, v)) { ct++; } } if (ct>0) { _astrRelMorphMapNames[_ctUsedRelMorphMapNames] = strName; } _actRelMorphMapCounts[_ctUsedRelMorphMapNames] = ct; _ctUsedRelMorphMapNames++; }} // list all the absmorphmaps used by this object _ctAbsMorphMapNames = _objfunc->numVMaps(LWVMAP_SPOT); _astrAbsMorphMapNames = (const char**) malloc(_ctAbsMorphMapNames*sizeof(char*)); _actAbsMorphMapCounts = (int *) malloc(_ctAbsMorphMapNames*sizeof(int)); memset(_astrAbsMorphMapNames, 0, _ctAbsMorphMapNames*sizeof(char*)); memset(_actAbsMorphMapCounts, 0, _ctAbsMorphMapNames*sizeof(int)); _ctUsedAbsMorphMapNames = 0; {for(int iAbsMorphMap=0; iAbsMorphMap<_ctAbsMorphMapNames; iAbsMorphMap++) { const char *strName = _objfunc->vmapName(LWVMAP_SPOT, iAbsMorphMap); void *pMap = _meshEditOperations->pointVSet(_state,NULL, LWVMAP_SPOT, strName); int ct = 0; // for each polygonvertex for(int iPolPnt=0; iPolPnt<_ctPolPnts; iPolPnt++) { // get the morphpos float v[3]; if (_meshEditOperations->pointVGet(_state, _appPolPnts[iPolPnt].pp_idPnt, v)) { ct++; } } if (ct>0) { _astrAbsMorphMapNames[_ctUsedAbsMorphMapNames] = strName; } _actAbsMorphMapCounts[_ctUsedAbsMorphMapNames] = ct; _ctUsedAbsMorphMapNames++; }} } void ExportMesh_modeler(int iFaceForward) { char fnmOut[256]; char *strFileName; _state = _meshEditOperations->state; if (_meshEditOperations == NULL) { _msg->error("Error!","Error _meshEditOperations is NULL!"); return; } _ctPntIDs = _meshEditOperations->pointCount(_state,OPLYR_FG,EDCOUNT_ALL); _ctPolIDs = _meshEditOperations->polyCount(_state,OPLYR_FG,EDCOUNT_ALL); strFileName = strdup(_statequery->object()); strcpy(fnmOut, strFileName); char *pchDot = strrchr(fnmOut, '.'); if (pchDot!=NULL) { strcpy(pchDot, ".am"); } //msgbox(_xpanf,fnmOut); ExtractMeshData_modeler(); _fpOutput = fopen(fnmOut, "w"); if (_fpOutput==NULL) { msgbox_modeler(_xpanf, "Can't open file!"); } // write the mesh header fprintf(_fpOutput, "SE_MESH %s;\n\n",SE_ANIM_VER); // is mesh face forward if(iFaceForward==ML_HALF_FACE_FORWARD) { fprintf(_fpOutput, "HALF_FACE_FORWARD TRUE;\n\n"); } else if(iFaceForward==ML_FULL_FACE_FORWARD) { fprintf(_fpOutput, "FULL_FACE_FORWARD TRUE;\n\n"); } // write the vertex header fprintf(_fpOutput, "VERTICES %d\n", _ctPolPnts); fprintf(_fpOutput, "{\n"); // for each polygonvertex {for(int iPolPnt=0; iPolPnt<_ctPolPnts; iPolPnt++) { // get the coords EDPointInfo * poiPoint; double v[3]; poiPoint = _meshEditOperations->pointInfo(_state, _appPolPnts[iPolPnt].pp_idPnt); v[0] = (float) poiPoint->position[0]; v[1] = (float) poiPoint->position[1]; v[2] = (float) poiPoint->position[2]; fprintf(_fpOutput, " %g, %g, %g;\n", v[0], v[1], -v[2]); }} fprintf(_fpOutput, "}\n\n"); // write the normal header fprintf(_fpOutput, "NORMALS %d\n", _ctPolPnts); fprintf(_fpOutput, "{\n"); // for each polygonvertex {for(int iPolPnt=0; iPolPnt<_ctPolPnts; iPolPnt++) { // get the normal int iPnt = GetPntIndex_modeler(_appPolPnts[iPolPnt].pp_idPnt); fprintf(_fpOutput, " %g, %g, %g;\n", _avPntNormals[iPnt][0], _avPntNormals[iPnt][1], -_avPntNormals[iPnt][2]); }} fprintf(_fpOutput, "}\n\n"); // write the uvmaps header fprintf(_fpOutput, "UVMAPS %d\n", _ctUsedUVMapNames); fprintf(_fpOutput, "{\n"); if (_ctUsedUVMapNames == 0) { _msg->info("No UV maps in the scene!",NULL); } // for each uvmap {for(int iUVMap=0; iUVMap<_ctUsedUVMapNames; iUVMap++) { fprintf(_fpOutput, " {\n"); const char *strUVMap = _astrUVMapNames[iUVMap]; fprintf(_fpOutput, " NAME \"%s\";\n", strUVMap); void *pMap = _meshEditOperations->pointVSet(_state,NULL, LWVMAP_TXUV, strUVMap); fprintf(_fpOutput, " TEXCOORDS %d\n", _ctPolPnts); fprintf(_fpOutput, " {\n"); // for each polygonvertex {for(int iPolPnt=0; iPolPnt<_ctPolPnts; iPolPnt++) { // get the coords float v[2]; if (_meshEditOperations->pointVPGet(_state, _appPolPnts[iPolPnt].pp_idPnt, _appPolPnts[iPolPnt].pp_idPol, v)) { } else if (_meshEditOperations->pointVGet(_state, _appPolPnts[iPolPnt].pp_idPnt, v)) { } else { v[0] = 0.0f; v[1] = 0.0f; } fprintf(_fpOutput, " %g, %g;\n", v[0], 1.0f-v[1]); }} fprintf(_fpOutput, " }\n"); fprintf(_fpOutput, " }\n"); }} fprintf(_fpOutput, "}\n\n"); // get surfaces _asurSurfaces = _srf->byObject(strFileName); // count the surfaces _ctSurfs = 0; while(_asurSurfaces[_ctSurfs]!=NULL) { _ctSurfs++; } // write the surfaces header fprintf(_fpOutput, "SURFACES %d\n", _ctSurfs); fprintf(_fpOutput, "{\n"); // for each surface {for(int iSurf=0; iSurf<_ctSurfs; iSurf++) { fprintf(_fpOutput, " {\n"); const char *strSurf = _srf->name(_asurSurfaces[iSurf]); fprintf(_fpOutput, " NAME \"%s\";\n", strSurf); // count the polygons int iSurfPols = 0; {for(int i=0; i<_ctPolIDs; i++) { if (strcmp(_meshEditOperations->polyTag(_state, _aidPolIDs[i], LWPTAG_SURF), strSurf)==0) { iSurfPols++; } }} // write the polygon set header fprintf(_fpOutput, " TRIANGLE_SET %d\n", iSurfPols); fprintf(_fpOutput, " {\n"); // for each polygon for(int iTri=0; iTri<_ctPolIDs; iTri++) { LWPolID idPol = _aidPolIDs[iTri]; // if not in this surface if (strcmp(_meshEditOperations->polyTag(_state, idPol, LWPTAG_SURF), strSurf)!=0) { // skip it continue; } fprintf(_fpOutput, " %d, %d, %d;\n", iTri*3+2, iTri*3+1, iTri*3+0); } fprintf(_fpOutput, " }\n"); fprintf(_fpOutput, " }\n"); }} fprintf(_fpOutput, "}\n"); // write the weightmaps header fprintf(_fpOutput, "WEIGHTS %d\n", _ctUsedWeightMapNames); fprintf(_fpOutput, "{\n"); // for each weightmap {for(int iWeightMap=0; iWeightMap<_ctUsedWeightMapNames; iWeightMap++) { fprintf(_fpOutput, " {\n"); const char *strWeightMap = _astrWeightMapNames[iWeightMap]; fprintf(_fpOutput, " NAME \"%s\";\n", strWeightMap); void *pMap = _meshEditOperations->pointVSet(_state,NULL, LWVMAP_WGHT, strWeightMap); fprintf(_fpOutput, " WEIGHT_SET %d\n", _actWeightMapCounts[iWeightMap]); fprintf(_fpOutput, " {\n"); // for each polygonvertex {for(int iPolPnt=0; iPolPnt<_ctPolPnts; iPolPnt++) { // get the coords float v[1]; if (_meshEditOperations->pointVGet(_state, _appPolPnts[iPolPnt].pp_idPnt, v) && v[0]!=0.0f) { if (v[0] < 0) { _msg->error("Weight map value lesser than zero!",NULL); } else { fprintf(_fpOutput, " { %d; %g; }\n", iPolPnt, v[0]); } } }} fprintf(_fpOutput, " }\n"); fprintf(_fpOutput, " }\n"); }} fprintf(_fpOutput, "}\n\n"); // write the morphmaps header fprintf(_fpOutput, "MORPHS %d\n", _ctUsedRelMorphMapNames+_ctUsedAbsMorphMapNames); fprintf(_fpOutput, "{\n"); // for each relmorphmap {for(int iRelMorphMap=0; iRelMorphMap<_ctUsedRelMorphMapNames; iRelMorphMap++) { const char *strRelMorphMap = _astrRelMorphMapNames[iRelMorphMap]; // calculate mesh normals using the given morphmap FillRelativeMorphVertexCoords_modeler(strRelMorphMap); MakeNormals_modeler(); fprintf(_fpOutput, " {\n"); fprintf(_fpOutput, " NAME \"%s\";\n", strRelMorphMap); fprintf(_fpOutput, " RELATIVE TRUE;\n"); void *pMap = _meshEditOperations->pointVSet(_state,NULL, LWVMAP_MORF, strRelMorphMap); fprintf(_fpOutput, " MORPH_SET %d\n", _actRelMorphMapCounts[iRelMorphMap]); fprintf(_fpOutput, " {\n"); // for each polygonvertex {for(int iPolPnt=0; iPolPnt<_ctPolPnts; iPolPnt++) { // if morphed here float vRel[3]; if (_meshEditOperations->pointVGet(_state, _appPolPnts[iPolPnt].pp_idPnt, vRel)) { // write the coords and normal int iPnt = GetPntIndex_modeler(_appPolPnts[iPolPnt].pp_idPnt); fprintf(_fpOutput, " { %d; %g, %g, %g; %g, %g, %g; }\n", iPolPnt, _avPnts[iPnt][0], _avPnts[iPnt][1], -_avPnts[iPnt][2], _avPntNormals[iPnt][0], _avPntNormals[iPnt][1], -_avPntNormals[iPnt][2]); } }} fprintf(_fpOutput, " }\n"); fprintf(_fpOutput, " }\n"); }} // for each absmorphmap {for(int iAbsMorphMap=0; iAbsMorphMap<_ctUsedAbsMorphMapNames; iAbsMorphMap++) { const char *strAbsMorphMap = _astrAbsMorphMapNames[iAbsMorphMap]; // calculate mesh normals using the given morphmap FillAbsoluteMorphVertexCoords_modeler(strAbsMorphMap); MakeNormals_modeler(); fprintf(_fpOutput, " {\n"); fprintf(_fpOutput, " NAME \"%s\";\n", strAbsMorphMap); fprintf(_fpOutput, " RELATIVE FALSE;\n"); void *pMap = _meshEditOperations->pointVSet(_state,NULL, LWVMAP_MORF, strAbsMorphMap); fprintf(_fpOutput, " MORPH_SET %d\n", _actAbsMorphMapCounts[iAbsMorphMap]); fprintf(_fpOutput, " {\n"); // for each polygonvertex {for(int iPolPnt=0; iPolPnt<_ctPolPnts; iPolPnt++) { // if morphed here float vRel[3]; if (_meshEditOperations->pointVGet(_state, _appPolPnts[iPolPnt].pp_idPnt, vRel)) { // write the coords and normal int iPnt = GetPntIndex_modeler(_appPolPnts[iPolPnt].pp_idPnt); fprintf(_fpOutput, " { %d; %g, %g, %g; %g, %g, %g; }\n", iPolPnt, _avPnts[iPnt][0], _avPnts[iPnt][1], -_avPnts[iPnt][2], _avPntNormals[iPnt][0], _avPntNormals[iPnt][1], -_avPntNormals[iPnt][2]); } }} fprintf(_fpOutput, " }\n"); fprintf(_fpOutput, " }\n"); }} fprintf(_fpOutput, "}\n\n"); fprintf(_fpOutput, "SE_MESH_END;\n"); _msg->info("Saved:", fnmOut); // close and free everything if (_fpOutput!=NULL) { fclose(_fpOutput); _fpOutput=NULL; } if (_aidPntIDs!=NULL) { free(_aidPntIDs); _aidPntIDs = NULL; } if (_aidPolIDs!=NULL) { free(_aidPolIDs); _aidPolIDs = NULL; } if (_appPolPnts!=NULL) { free(_appPolPnts); _appPolPnts = NULL; } if (_appPntPols!=NULL) { free(_appPntPols); _appPntPols = NULL; } if (_avPnts!=NULL) { free(_avPnts); _avPnts = NULL; } if (_avPolNormals!=NULL) { free(_avPolNormals); _avPolNormals = NULL; } if (_avPntNormals!=NULL) { free(_avPntNormals); _avPntNormals = NULL; } if (strFileName!=NULL) { free(strFileName); strFileName = NULL; } if (_astrUVMapNames!=NULL) { free(_astrUVMapNames); _astrUVMapNames = NULL; } if (_astrWeightMapNames!=NULL) { free(_astrWeightMapNames); _astrWeightMapNames = NULL; } }