mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-03-23 15:25:35 +08:00
609 lines
13 KiB
C++
609 lines
13 KiB
C++
/*
|
|
* 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 <stdio.h>
|
|
#include <ctype.h>
|
|
#include "XObjDefs.h"
|
|
#include <stdlib.h>
|
|
//#include "PlatformUtils.h"
|
|
//#include <time.h>
|
|
#include <map>
|
|
#include <math.h>
|
|
#include <string.h>
|
|
|
|
#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<int>(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<string>& 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<vec_tex> v;
|
|
for (vector<vec_tex>::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<XObjCmd>::const_iterator c = inObj.cmds.begin();
|
|
c != inObj.cmds.end(); ++c)
|
|
{
|
|
for (vector<vec_tex>::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<vec_rgb>::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<double>& 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<int>(n);
|
|
v -= chances[n];
|
|
}
|
|
return static_cast<int>(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 <Processes.h>
|
|
|
|
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<int>(inLine.length())) ep = static_cast<int>(inLine.length());
|
|
if (sp > static_cast<int>(inLine.length())) sp = static_cast<int>(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<p_info> pts;
|
|
map<string, int> index;
|
|
|
|
void clear()
|
|
{
|
|
pts.clear();
|
|
index.clear();
|
|
}
|
|
|
|
int count(void)
|
|
{
|
|
return static_cast<int>(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<const int*>(xyz+0)),
|
|
*(reinterpret_cast<const int*>(xyz+1)),
|
|
*(reinterpret_cast<const int*>(xyz+2)),
|
|
*(reinterpret_cast<const int*>(st +0)),
|
|
*(reinterpret_cast<const int*>(st +1)));
|
|
string key(buf);
|
|
map<string, int>::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<string,int>::value_type(key, static_cast<int>(pts.size())));
|
|
pts.push_back(p);
|
|
return static_cast<int>(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<XObjCmd>& 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<int>(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<XObjCmd>::const_iterator cmd = inObj.cmds.begin();
|
|
cmd != inObj.cmds.end(); ++cmd)
|
|
{
|
|
vector<XObjCmd> newCmds;
|
|
DecomposeObjCmd(*cmd, newCmds, maxValence);
|
|
outObj.cmds.insert(outObj.cmds.end(), newCmds.begin(), newCmds.end());
|
|
}
|
|
}
|