/* * Copyright (c) 2004, Laminar Research. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #include "XUtils.h" #include #include #include "XObjDefs.h" #include //#include "PlatformUtils.h" //#include #include #include #include #if !defined(XUTILS_EXCLUDE_MAC_CRAP) && defined(__MACH__) #define XUTILS_EXCLUDE_MAC_CRAP 1 #endif #if APL //using namespace Metrowerks; #endif static char * my_fgets(char * s, int n, FILE * file) { char * p = s; int c; if (--n < 0) return(NULL); if (n) do { c = fgetc(file); if (c == EOF) { if (/*feof(file) &&*/ p != s) break; else { return(NULL); } } *p++ = c; } while (c != '\n' && c != '\r' && --n); *p = 0; return(s); } StTextFileScanner::StTextFileScanner(const char * file, bool skip) : mFile(fopen(file, "r")), mDone(false), mSkipBlanks(skip) { read_next(); } StTextFileScanner::~StTextFileScanner() { if (mFile) fclose(mFile); } bool StTextFileScanner::done() { return mDone; } void StTextFileScanner::next() { read_next(); } string StTextFileScanner::get() { return mBuf; } void StTextFileScanner::read_next(void) { mBuf.clear(); mDone = true; char buf[4096]; while (mFile && /*!feof(mFile) &&*/ my_fgets(buf, sizeof(buf), mFile)) { int len = static_cast(strlen(buf)); while ((len > 0) && (buf[len-1] == '\r' || buf[len-1] == '\n')) { buf[len-1] = 0; --len; } if (buf[0] == '\r' || buf[0] == '\n') mBuf = buf+1; else mBuf = buf; if (!mBuf.empty() || !mSkipBlanks) { mDone = false; return; } } } void BreakString(const string& line, vector& words) { words.clear(); string::const_iterator i = line.begin(); while (i < line.end()) { string::const_iterator s = i; while (s < line.end() && isspace(*s)) ++s; string::const_iterator e = s; while (e < line.end() && !isspace(*e)) ++e; if (s < e) words.push_back(string(s,e)); i = e; } } void StringToUpper(string& s) { for (string::iterator i = s.begin(); i != s.end(); ++i) *i = toupper(*i); } bool HasExtNoCase(const string& inStr, const char * inExt) { string s(inStr); string e(inExt); StringToUpper(s); StringToUpper(e); if (s.rfind(e) == (s.length() - e.length())) return true; return false; } void ChangePolyCmdCW(XObjCmd& ioCmd) { vector v; for (vector::reverse_iterator riter = ioCmd.st.rbegin(); riter != ioCmd.st.rend(); ++riter) { v.push_back(*riter); } ioCmd.st = v; } bool GetNextNoComments(StTextFileScanner& f, string& s) { while(!f.done()) { s = f.get(); f.next(); if (s.empty() || s[0] != '#') return true; } return false; } double GetObjRadius(const XObj& inObj) { double dist = 0, d; for (vector::const_iterator c = inObj.cmds.begin(); c != inObj.cmds.end(); ++c) { for (vector::const_iterator v = c->st.begin(); v != c->st.end(); ++v) { d = sqrt(v->v[0] * v->v[0] + v->v[1] * v->v[1] + v->v[2] * v->v[2]); if (d > dist) dist = d; } for (vector::const_iterator p = c->rgb.begin(); p != c->rgb.end(); ++p) { d = sqrt(p->v[0] * p->v[0] + p->v[1] * p->v[1] + p->v[2] * p->v[2]); if (d > dist) dist = d; } } return dist; } int PickRandom(vector& chances) { double v = (double) (rand() % RAND_MAX) / (double) RAND_MAX; for (size_t n = 0; n < chances.size(); ++n) { if (v < chances[n]) return static_cast(n); v -= chances[n]; } return static_cast(chances.size()); } bool RollDice(double inProb) { if (inProb <= 0.0) return false; if (inProb >= 1.0) return true; double v = (double) (rand() % RAND_MAX) / (double) RAND_MAX; return v < inProb; } double RandRange(double mmin, double mmax) { if (mmin >= mmax) return mmin; double v = (double) rand() / (double) RAND_MAX; return mmin + ((mmax - mmin) * v); } double RandRangeBias(double mmin, double mmax, double biasRatio, double randomAmount) { double span = mmax - mmin; double lower_rat = biasRatio * (1.0 - randomAmount); double upper_rat = lower_rat + randomAmount; return RandRange(mmin + span * lower_rat,mmin + span * upper_rat); } #if 0 void StripPath(string& ioPath) { string::size_type sep = ioPath.rfind(DIR_CHAR); if (sep != ioPath.npos) ioPath = ioPath.substr(sep+1,ioPath.npos); } void StripPathCP(string& ioPath) { string::size_type sep; sep = ioPath.rfind(':'); if (sep != ioPath.npos) ioPath = ioPath.substr(sep+1,ioPath.npos); sep = ioPath.rfind('\\'); if (sep != ioPath.npos) ioPath = ioPath.substr(sep+1,ioPath.npos); } void ExtractPath(string& ioPath) { string::size_type sep = ioPath.rfind(DIR_CHAR); if (sep != ioPath.npos) ioPath = ioPath.substr(0, sep); } #endif #if APL #if !defined(XUTILS_EXCLUDE_MAC_CRAP) #include OSErr FindSuperFolder(const FSSpec& inItem, FSSpec& outFolder) { CInfoPBRec paramBlock; OSErr err; paramBlock.dirInfo.ioCompletion = NULL; paramBlock.dirInfo.ioNamePtr = (StringPtr) (&(outFolder.name)); paramBlock.dirInfo.ioVRefNum = inItem.vRefNum; paramBlock.dirInfo.ioFDirIndex = -1; paramBlock.dirInfo.ioDrDirID = inItem.parID; err = ::PBGetCatInfoSync(¶mBlock); if (err != noErr) return err; outFolder.vRefNum = paramBlock.dirInfo.ioVRefNum; outFolder.parID= paramBlock.dirInfo.ioDrParID; return noErr; } void AppPath(string& outString) { ProcessSerialNumber psn = { 0, kCurrentProcess }; ProcessInfoRec info = { 0 }; FSSpec spec; Str255 name; info.processInfoLength = sizeof(info); info.processAppSpec = &spec; info.processName = name; GetProcessInformation(&psn, &info); FSSpec_2_String(spec, outString); } void FSSpec_2_String(const FSSpec& inSpec, string& outString) { outString.clear(); FSSpec foo(inSpec); FSSpec foo2; while (1) { if (outString.empty()) outString = std::string(foo.name+1, foo.name+foo.name[0]+1); else outString = std::string(foo.name+1, foo.name+foo.name[0]+1) + std::string(":") + outString; if (FindSuperFolder(foo, foo2) != noErr) break; foo = foo2; } } #endif #endif void ExtractFixedRecordString( const string& inLine, int inBegin, int inEnd, string& outString) { int sp = inBegin-1; int ep = inEnd; if (ep > static_cast(inLine.length())) ep = static_cast(inLine.length()); if (sp > static_cast(inLine.length())) sp = static_cast(inLine.length()); while ((sp < ep) && (inLine[sp] == ' ')) ++sp; while ((ep > sp) && (inLine[ep-1] == ' ')) --ep; outString = inLine.substr(sp, ep - sp); } bool ExtractFixedRecordLong( const string& inLine, int inBegin, int inEnd, long& outLong) { string foo; ExtractFixedRecordString(inLine, inBegin, inEnd, foo); if (foo.empty()) return false; outLong = strtol(foo.c_str(), NULL, 10); return true; } bool ExtractFixedRecordUnsignedLong( const string& inLine, int inBegin, int inEnd, unsigned long& outUnsignedLong) { string foo; ExtractFixedRecordString(inLine, inBegin, inEnd, foo); if (foo.empty()) return false; outUnsignedLong = strtoul(foo.c_str(), NULL, 10); return true; } //#pragma mark - struct XPointPool::XPointPoolImp { struct p_info { float xyz[3]; float st[2]; }; vector pts; map index; void clear() { pts.clear(); index.clear(); } int count(void) { return static_cast(pts.size()); } int accumulate(const float xyz[3], const float st[2]) { static char buf[256]; sprintf(buf,"%08X%08X%08X|%08x%08x", *(reinterpret_cast(xyz+0)), *(reinterpret_cast(xyz+1)), *(reinterpret_cast(xyz+2)), *(reinterpret_cast(st +0)), *(reinterpret_cast(st +1))); string key(buf); map::iterator i = index.find(key); if (i != index.end()) return i->second; p_info p; memcpy(p.xyz, xyz, sizeof(p.xyz)); memcpy(p.st, st, sizeof(p.st)); pts.push_back(p); index.insert(map::value_type(key, static_cast(pts.size()))); pts.push_back(p); return static_cast(pts.size())-1; } void get(int i, float xyz[3], float st[2]) { p_info& p = pts[i]; memcpy(xyz,p.xyz,sizeof(p.xyz)); memcpy(st,p.st,sizeof(p.st)); } }; XPointPool::XPointPool() { mImp = new XPointPoolImp; } XPointPool::~XPointPool() { delete mImp; } void XPointPool::clear() { mImp->clear(); } int XPointPool::accumulate(const float xyz[3], const float st[2]) { return mImp->accumulate(xyz, st); } void XPointPool::get(int index, float xyz[3], float st[2]) { mImp->get(index,xyz,st); } int XPointPool::count(void) { return mImp->count(); } void DecomposeObjCmd(const XObjCmd& inCmd, vector& outCmds, int maxValence) { XObjCmd c; c.cmdType = type_Poly; c.cmdID = obj_Tri; switch(inCmd.cmdID) { case obj_Tri: // Triangles never need breaking down. outCmds.push_back(inCmd); break; case obj_Quad: case obj_Quad_Hard: case obj_Smoke_Black: case obj_Smoke_White: case obj_Movie: // Quads - split into triangles if necessary. if (maxValence > 3) { outCmds.push_back(inCmd); outCmds.back().cmdID = obj_Quad; } else { outCmds.push_back(inCmd); outCmds.back().cmdID = obj_Tri; outCmds.back().st.erase(outCmds.back().st.begin()+3); outCmds.push_back(inCmd); outCmds.back().cmdID = obj_Tri; outCmds.back().st.erase(outCmds.back().st.begin()+1); } break; case obj_Polygon: // Polygons might be ok. But if we have to break them down, // we generate N-2 triangles in a fan configuration. if (maxValence < static_cast(inCmd.st.size())) { c.st.push_back(inCmd.st[0]); c.st.push_back(inCmd.st[1]); c.st.push_back(inCmd.st[2]); for (size_t n = 2; n < inCmd.st.size(); ++n) { c.st[1] = inCmd.st[n-1]; c.st[2] = inCmd.st[n ]; outCmds.push_back(c); } } else outCmds.push_back(inCmd); break; case obj_Tri_Strip: // Triangle strips - every other triangle's vertices // are backward! c.st.push_back(inCmd.st[0]); c.st.push_back(inCmd.st[1]); c.st.push_back(inCmd.st[2]); for (size_t n = 2; n < inCmd.st.size(); ++n) { if (n%2) { c.st[0] = inCmd.st[n-2]; c.st[1] = inCmd.st[n ]; c.st[2] = inCmd.st[n-1]; outCmds.push_back(c); } else { c.st[0] = inCmd.st[n-2]; c.st[1] = inCmd.st[n-1]; c.st[2] = inCmd.st[n ]; outCmds.push_back(c); } } break; case obj_Tri_Fan: // Tri fan - run around the triangle fan emitting triangles. c.st.push_back(inCmd.st[0]); c.st.push_back(inCmd.st[1]); c.st.push_back(inCmd.st[2]); for (size_t n = 2; n < inCmd.st.size(); ++n) { c.st[1] = inCmd.st[n-1]; c.st[2] = inCmd.st[n ]; outCmds.push_back(c); } break; case obj_Quad_Strip: // Quad strips can become either quads or triangles!! if (maxValence > 3) { c.cmdID = obj_Quad; c.st.push_back(inCmd.st[0]); c.st.push_back(inCmd.st[1]); c.st.push_back(inCmd.st[2]); c.st.push_back(inCmd.st[3]); for (size_t n = 2; n < inCmd.st.size(); n += 2) { c.st[0] = inCmd.st[n-2]; c.st[1] = inCmd.st[n-1]; c.st[2] = inCmd.st[n+1]; c.st[3] = inCmd.st[n ]; outCmds.push_back(c); } } else { c.st.push_back(inCmd.st[0]); c.st.push_back(inCmd.st[1]); c.st.push_back(inCmd.st[2]); for (size_t n = 2; n < inCmd.st.size(); ++n) { if (n%2) { c.st[0] = inCmd.st[n-2]; c.st[1] = inCmd.st[n ]; c.st[2] = inCmd.st[n-1]; outCmds.push_back(c); } else { c.st[0] = inCmd.st[n-2]; c.st[1] = inCmd.st[n-1]; c.st[2] = inCmd.st[n ]; outCmds.push_back(c); } } } break; default: outCmds.push_back(inCmd); } } void DecomposeObj(const XObj& inObj, XObj& outObj, int maxValence) { outObj.cmds.clear(); outObj.texture = inObj.texture; for (vector::const_iterator cmd = inObj.cmds.begin(); cmd != inObj.cmds.end(); ++cmd) { vector newCmds; DecomposeObjCmd(*cmd, newCmds, maxValence); outObj.cmds.insert(outObj.cmds.end(), newCmds.begin(), newCmds.end()); } }