#include #include #include #include #include /*---------------------------------------------------------------------- Copyright (c) 2004 Open Dynamics Framework Group www.physicstools.org All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the Open Dynamics Framework Group nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -----------------------------------------------------------------------*/ // http://codesuppository.blogspot.com // // mailto: jratcliff@infiniplex.net // // http://www.amillionpixels.us // #include "float_math.h" #include "cd_wavefront.h" using namespace ConvexDecomposition; /*---------------------------------------------------------------------- Copyright (c) 2004 Open Dynamics Framework Group www.physicstools.org All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the Open Dynamics Framework Group nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -----------------------------------------------------------------------*/ #include namespace ConvexDecomposition { typedef std::vector IntVector; typedef std::vector FloatVector; #if defined(__APPLE__) || defined(__CELLOS_LV2__) #define stricmp(a, b) strcasecmp((a), (b)) #endif /*******************************************************************/ /******************** InParser.h ********************************/ /*******************************************************************/ class InPlaceParserInterface { public: virtual ~InPlaceParserInterface(){}; virtual int ParseLine(int lineno, int argc, const char **argv) = 0; // return TRUE to continue parsing, return FALSE to abort parsing process }; enum SeparatorType { ST_DATA, // is data ST_HARD, // is a hard separator ST_SOFT, // is a soft separator ST_EOS // is a comment symbol, and everything past this character should be ignored }; class InPlaceParser { public: InPlaceParser(void) { Init(); } InPlaceParser(char *data, int len) { Init(); SetSourceData(data, len); } InPlaceParser(const char *fname) { Init(); SetFile(fname); } ~InPlaceParser(void); void Init(void) { mQuoteChar = 34; mData = 0; mLen = 0; mMyAlloc = false; for (int i = 0; i < 256; i++) { mHard[i] = ST_DATA; mHardString[i * 2] = i; mHardString[i * 2 + 1] = 0; } mHard[0] = ST_EOS; mHard[32] = ST_SOFT; mHard[9] = ST_SOFT; mHard[13] = ST_SOFT; mHard[10] = ST_SOFT; } void SetFile(const char *fname); // use this file as source data to parse. void SetSourceData(char *data, int len) { mData = data; mLen = len; mMyAlloc = false; }; int Parse(InPlaceParserInterface *callback); // returns true if entire file was parsed, false if it aborted for some reason int ProcessLine(int lineno, char *line, InPlaceParserInterface *callback); const char **GetArglist(char *source, int &count); // convert source string into an arg list, this is a destructive parse. void SetHardSeparator(char c) // add a hard separator { mHard[(int)c] = ST_HARD; } void SetHard(char c) // add a hard separator { mHard[(int)c] = ST_HARD; } void SetCommentSymbol(char c) // comment character, treated as 'end of string' { mHard[(int)c] = ST_EOS; } void ClearHardSeparator(char c) { mHard[(int)c] = ST_DATA; } void DefaultSymbols(void); // set up default symbols for hard separator and comment symbol of the '#' character. bool EOS(char c) { if (mHard[(int)c] == ST_EOS) { return true; } return false; } void SetQuoteChar(char c) { mQuoteChar = c; } private: inline char *AddHard(int &argc, const char **argv, char *foo); inline bool IsHard(char c); inline char *SkipSpaces(char *foo); inline bool IsWhiteSpace(char c); inline bool IsNonSeparator(char c); // non separator, neither hard nor soft bool mMyAlloc; // whether or not *I* allocated the buffer and am responsible for deleting it. char *mData; // ascii data to parse. int mLen; // length of data SeparatorType mHard[256]; char mHardString[256 * 2]; char mQuoteChar; }; /*******************************************************************/ /******************** InParser.cpp ********************************/ /*******************************************************************/ void InPlaceParser::SetFile(const char *fname) { if (mMyAlloc) { free(mData); } mData = 0; mLen = 0; mMyAlloc = false; FILE *fph = fopen(fname, "rb"); if (fph) { fseek(fph, 0L, SEEK_END); mLen = ftell(fph); fseek(fph, 0L, SEEK_SET); if (mLen) { mData = (char *)malloc(sizeof(char) * (mLen + 1)); int ok = fread(mData, mLen, 1, fph); if (!ok) { free(mData); mData = 0; } else { mData[mLen] = 0; // zero byte terminate end of file marker. mMyAlloc = true; } } fclose(fph); } } InPlaceParser::~InPlaceParser(void) { if (mMyAlloc) { free(mData); } } #define MAXARGS 512 bool InPlaceParser::IsHard(char c) { return mHard[(int)c] == ST_HARD; } char *InPlaceParser::AddHard(int &argc, const char **argv, char *foo) { while (IsHard(*foo)) { const char *hard = &mHardString[*foo * 2]; if (argc < MAXARGS) { argv[argc++] = hard; } foo++; } return foo; } bool InPlaceParser::IsWhiteSpace(char c) { return mHard[(int)c] == ST_SOFT; } char *InPlaceParser::SkipSpaces(char *foo) { while (!EOS(*foo) && IsWhiteSpace(*foo)) foo++; return foo; } bool InPlaceParser::IsNonSeparator(char c) { if (!IsHard(c) && !IsWhiteSpace(c) && c != 0) return true; return false; } int InPlaceParser::ProcessLine(int lineno, char *line, InPlaceParserInterface *callback) { int ret = 0; const char *argv[MAXARGS]; int argc = 0; char *foo = line; while (!EOS(*foo) && argc < MAXARGS) { foo = SkipSpaces(foo); // skip any leading spaces if (EOS(*foo)) break; if (*foo == mQuoteChar) // if it is an open quote { foo++; if (argc < MAXARGS) { argv[argc++] = foo; } while (!EOS(*foo) && *foo != mQuoteChar) foo++; if (!EOS(*foo)) { *foo = 0; // replace close quote with zero byte EOS foo++; } } else { foo = AddHard(argc, argv, foo); // add any hard separators, skip any spaces if (IsNonSeparator(*foo)) // add non-hard argument. { bool quote = false; if (*foo == mQuoteChar) { foo++; quote = true; } if (argc < MAXARGS) { argv[argc++] = foo; } if (quote) { while (*foo && *foo != mQuoteChar) foo++; if (*foo) *foo = 32; } // continue..until we hit an eos .. while (!EOS(*foo)) // until we hit EOS { if (IsWhiteSpace(*foo)) // if we hit a space, stomp a zero byte, and exit { *foo = 0; foo++; break; } else if (IsHard(*foo)) // if we hit a hard separator, stomp a zero byte and store the hard separator argument { const char *hard = &mHardString[*foo * 2]; *foo = 0; if (argc < MAXARGS) { argv[argc++] = hard; } foo++; break; } foo++; } // end of while loop... } } } if (argc) { ret = callback->ParseLine(lineno, argc, argv); } return ret; } int InPlaceParser::Parse(InPlaceParserInterface *callback) // returns true if entire file was parsed, false if it aborted for some reason { assert(callback); if (!mData) return 0; int ret = 0; int lineno = 0; char *foo = mData; char *begin = foo; while (*foo) { if (*foo == 10 || *foo == 13) { lineno++; *foo = 0; if (*begin) // if there is any data to parse at all... { int v = ProcessLine(lineno, begin, callback); if (v) ret = v; } foo++; if (*foo == 10) foo++; // skip line feed, if it is in the carraige-return line-feed format... begin = foo; } else { foo++; } } lineno++; // lasst line. int v = ProcessLine(lineno, begin, callback); if (v) ret = v; return ret; } void InPlaceParser::DefaultSymbols(void) { SetHardSeparator(','); SetHardSeparator('('); SetHardSeparator(')'); SetHardSeparator('='); SetHardSeparator('['); SetHardSeparator(']'); SetHardSeparator('{'); SetHardSeparator('}'); SetCommentSymbol('#'); } const char **InPlaceParser::GetArglist(char *line, int &count) // convert source string into an arg list, this is a destructive parse. { const char **ret = 0; static const char *argv[MAXARGS]; int argc = 0; char *foo = line; while (!EOS(*foo) && argc < MAXARGS) { foo = SkipSpaces(foo); // skip any leading spaces if (EOS(*foo)) break; if (*foo == mQuoteChar) // if it is an open quote { foo++; if (argc < MAXARGS) { argv[argc++] = foo; } while (!EOS(*foo) && *foo != mQuoteChar) foo++; if (!EOS(*foo)) { *foo = 0; // replace close quote with zero byte EOS foo++; } } else { foo = AddHard(argc, argv, foo); // add any hard separators, skip any spaces if (IsNonSeparator(*foo)) // add non-hard argument. { bool quote = false; if (*foo == mQuoteChar) { foo++; quote = true; } if (argc < MAXARGS) { argv[argc++] = foo; } if (quote) { while (*foo && *foo != mQuoteChar) foo++; if (*foo) *foo = 32; } // continue..until we hit an eos .. while (!EOS(*foo)) // until we hit EOS { if (IsWhiteSpace(*foo)) // if we hit a space, stomp a zero byte, and exit { *foo = 0; foo++; break; } else if (IsHard(*foo)) // if we hit a hard separator, stomp a zero byte and store the hard separator argument { const char *hard = &mHardString[*foo * 2]; *foo = 0; if (argc < MAXARGS) { argv[argc++] = hard; } foo++; break; } foo++; } // end of while loop... } } } count = argc; if (argc) { ret = argv; } return ret; } /*******************************************************************/ /******************** Geometry.h ********************************/ /*******************************************************************/ class GeometryVertex { public: float mPos[3]; float mNormal[3]; float mTexel[2]; }; class GeometryInterface { public: virtual void NodeTriangle(const GeometryVertex *v1, const GeometryVertex *v2, const GeometryVertex *v3) {} virtual ~GeometryInterface() {} }; /*******************************************************************/ /******************** Obj.h ********************************/ /*******************************************************************/ class OBJ : public InPlaceParserInterface { public: int LoadMesh(const char *fname, GeometryInterface *callback); int ParseLine(int lineno, int argc, const char **argv); // return TRUE to continue parsing, return FALSE to abort parsing process private: void getVertex(GeometryVertex &v, const char *face) const; FloatVector mVerts; FloatVector mTexels; FloatVector mNormals; GeometryInterface *mCallback; }; /*******************************************************************/ /******************** Obj.cpp ********************************/ /*******************************************************************/ int OBJ::LoadMesh(const char *fname, GeometryInterface *iface) { int ret = 0; mVerts.clear(); mTexels.clear(); mNormals.clear(); mCallback = iface; InPlaceParser ipp(fname); ipp.Parse(this); return ret; } //static const char * GetArg(const char **argv,int i,int argc) //{ // const char * ret = 0; // if ( i < argc ) ret = argv[i]; // return ret; //} void OBJ::getVertex(GeometryVertex &v, const char *face) const { v.mPos[0] = 0; v.mPos[1] = 0; v.mPos[2] = 0; v.mTexel[0] = 0; v.mTexel[1] = 0; v.mNormal[0] = 0; v.mNormal[1] = 1; v.mNormal[2] = 0; int index = atoi(face) - 1; const char *texel = strstr(face, "/"); if (texel) { int tindex = atoi(texel + 1) - 1; if (tindex >= 0 && tindex < (int)(mTexels.size() / 2)) { const float *t = &mTexels[tindex * 2]; v.mTexel[0] = t[0]; v.mTexel[1] = t[1]; } const char *normal = strstr(texel + 1, "/"); if (normal) { int nindex = atoi(normal + 1) - 1; if (nindex >= 0 && nindex < (int)(mNormals.size() / 3)) { const float *n = &mNormals[nindex * 3]; v.mNormal[0] = n[0]; v.mNormal[1] = n[1]; v.mNormal[2] = n[2]; } } } if (index >= 0 && index < (int)(mVerts.size() / 3)) { const float *p = &mVerts[index * 3]; v.mPos[0] = p[0]; v.mPos[1] = p[1]; v.mPos[2] = p[2]; } } int OBJ::ParseLine(int lineno, int argc, const char **argv) // return TRUE to continue parsing, return FALSE to abort parsing process { int ret = 0; if (argc >= 1) { const char *foo = argv[0]; if (*foo != '#') { if (strcmp(argv[0], "v") == 0 && argc == 4) //if ( stricmp(argv[0],"v") == 0 && argc == 4 ) { float vx = (float)atof(argv[1]); float vy = (float)atof(argv[2]); float vz = (float)atof(argv[3]); mVerts.push_back(vx); mVerts.push_back(vy); mVerts.push_back(vz); } else if (strcmp(argv[0], "vt") == 0 && argc == 3) // else if ( stricmp(argv[0],"vt") == 0 && argc == 3 ) { float tx = (float)atof(argv[1]); float ty = (float)atof(argv[2]); mTexels.push_back(tx); mTexels.push_back(ty); } // else if ( stricmp(argv[0],"vn") == 0 && argc == 4 ) else if (strcmp(argv[0], "vn") == 0 && argc == 4) { float normalx = (float)atof(argv[1]); float normaly = (float)atof(argv[2]); float normalz = (float)atof(argv[3]); mNormals.push_back(normalx); mNormals.push_back(normaly); mNormals.push_back(normalz); } // else if ( stricmp(argv[0],"f") == 0 && argc >= 4 ) else if (strcmp(argv[0], "f") == 0 && argc >= 4) { GeometryVertex v[32]; int vcount = argc - 1; for (int i = 1; i < argc; i++) { getVertex(v[i - 1], argv[i]); } // need to generate a normal! #if 0 // not currently implemented if ( mNormals.empty() ) { Vector3d p1( v[0].mPos ); Vector3d p2( v[1].mPos ); Vector3d p3( v[2].mPos ); Vector3d n; n.ComputeNormal(p3,p2,p1); for (int i=0; iNodeTriangle(&v[0], &v[1], &v[2]); if (vcount >= 3) // do the fan { for (int i = 2; i < (vcount - 1); i++) { mCallback->NodeTriangle(&v[0], &v[i], &v[i + 1]); } } } } } return ret; } class BuildMesh : public GeometryInterface { public: int getIndex(const float *p) { int vcount = mVertices.size() / 3; if (vcount > 0) { //New MS STL library checks indices in debug build, so zero causes an assert if it is empty. const float *v = &mVertices[0]; for (int i = 0; i < vcount; i++) { if (v[0] == p[0] && v[1] == p[1] && v[2] == p[2]) return i; v += 3; } } mVertices.push_back(p[0]); mVertices.push_back(p[1]); mVertices.push_back(p[2]); return vcount; } virtual void NodeTriangle(const GeometryVertex *v1, const GeometryVertex *v2, const GeometryVertex *v3) { mIndices.push_back(getIndex(v1->mPos)); mIndices.push_back(getIndex(v2->mPos)); mIndices.push_back(getIndex(v3->mPos)); } const FloatVector &GetVertices(void) const { return mVertices; }; const IntVector &GetIndices(void) const { return mIndices; }; private: FloatVector mVertices; IntVector mIndices; }; WavefrontObj::WavefrontObj(void) { mVertexCount = 0; mTriCount = 0; mIndices = 0; mVertices = 0; } WavefrontObj::~WavefrontObj(void) { delete[] mIndices; delete[] mVertices; } unsigned int WavefrontObj::loadObj(const char *fname) // load a wavefront obj returns number of triangles that were loaded. Data is persists until the class is destructed. { unsigned int ret = 0; delete[] mVertices; mVertices = 0; delete[] mIndices; mIndices = 0; mVertexCount = 0; mTriCount = 0; BuildMesh bm; OBJ obj; obj.LoadMesh(fname, &bm); const FloatVector &vlist = bm.GetVertices(); const IntVector &indices = bm.GetIndices(); if (vlist.size()) { mVertexCount = vlist.size() / 3; mVertices = new float[mVertexCount * 3]; memcpy(mVertices, &vlist[0], sizeof(float) * mVertexCount * 3); mTriCount = indices.size() / 3; mIndices = new int[mTriCount * 3 * sizeof(int)]; memcpy(mIndices, &indices[0], sizeof(int) * mTriCount * 3); ret = mTriCount; } return ret; } } // namespace ConvexDecomposition