Files
RP411/DivLoader/VGCDivLoader.cpp
T
CydandClaude Opus 4.8 4abbf8879f Initial import of Red Planet v4.10 Win32 source
Imports the current Win32 source for the pod-racing game 'Red Planet',
built on the MUNGA engine and its L4 (Win32/DirectX) platform layer:

- MUNGA / MUNGA_L4: cross-platform engine core and Win32 backend
- RP / RP_L4: Red Planet game logic and Win32 application
- DivLoader, Setup1: asset loader and installer project
- lib, MUNGA_L4/openal, MUNGA_L4/sos: third-party audio dependencies

Removed stale Subversion metadata and added .gitignore/.gitattributes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 07:59:51 -05:00

1388 lines
30 KiB
C++

class DPLRenderer;
#include <winsock2.h>
#include "VGCDivLoader.h"
using namespace std;
hash_map<string, DivMaterial*> VGCDivLoader::mMaterials;
hash_map<string, DivTexture*> VGCDivLoader::mTextures;
hash_map<string, DivRamp*> VGCDivLoader::mRamps;
hash_map<string, DivObject*> VGCDivLoader::mObjects;
VGCDivLoader::VGCDivLoader()
{
//Initialize header to defaults
mHeaderInfo.precision = PRECISION_32BIT;
mHeaderInfo.scale = 1;
mHeaderInfo.type = TYPE_GEOMETRY;
mHeaderInfo.unit = UNIT_INCHES;
//Initialize surface props to defaults
mMasterProps.b_material_type = 2;
mMasterProps.b_material_name = 0;
mMasterProps.decal_offset = 0;
mMasterProps.drawmode = 0;
mMasterProps.drawmode_width = 1;
mMasterProps.f_material_type = 2;
mMasterProps.f_material_name = 0;
mMasterProps.faceted = 0;
mMasterProps.locks = 0;
mMasterProps.name = 0;
mMasterProps.plane = 0;
mMasterProps.specialInst = 0;
mMasterProps.vertex_mask = 0;
mMasterProps.isImmune = false;
mMasterProps.drawSpheres = false;
if (mTextures.find("NULL") == mTextures.end())
{
DivTexture *tex = new DivTexture;
memset(tex,0,sizeof(DivTexture));
tex->name = new char[5];
strcpy_s(tex->name,5,"NULL");
tex->texBuffer = new TexBuffer;
tex->texBuffer->height = 1;
tex->texBuffer->width = 1;
tex->texBuffer->m_buffer = new RGBA*[1];
tex->texBuffer->m_buffer[0] = new RGBA;
tex->texBuffer->m_buffer[0]->r = 255;
tex->texBuffer->m_buffer[0]->g = 255;
tex->texBuffer->m_buffer[0]->b = 255;
tex->texBuffer->m_buffer[0]->a = 255;
mTextures.insert(std::pair<string,DivTexture*>(string("NULL"),tex));
}
}
VGCDivLoader::~VGCDivLoader()
{
delete[] mMasterProps.b_material_name;
delete[] mMasterProps.name;
delete[] mMasterProps.f_material_name;
}
void VGCDivLoader::parse(LPDIRECT3DDEVICE9 device, char *fileName)
{
const char BIZ_IDSTR[] = "DIV-BIZ2";
const char VTX_IDSTR[] = "DIV-VTX2";
FILE file;
FILE *mFile = &file;
if (fopen_s(&mFile, fileName, "rb") != 0)
return;
char id[8];
fread(&id, 8, 1, mFile);
//Compare first 8 bits to make sure it's a biz2 file
if (strncmp(id,"DIV-BIZ2",8) != 0)
{
fclose(mFile);
return;
}
BIZ_IDENTITIES ident;
unsigned int len;
while(!feof(mFile))
{
//Retrieve ID and length of upcoming block
getBlock(mFile, ident, len);
switch(ident)
{
case HEADER:
parseHeader(mFile, len);
break;
case TRAILER:
//Output code here
fclose(mFile);
return;
case TEXTURE:
//This should be a geometry file
fseek(mFile,len,SEEK_CUR);
break;
case MATERIAL:
//This should be a geometry file
fseek(mFile,len,SEEK_CUR);
break;
case RAMP:
//This should be a geometry file
fseek(mFile,len,SEEK_CUR);
break;
case OBJECT:
parseObject(mFile, len, fileName);
break;
default:
//Unknown! Skip it
if (!feof(mFile))
{
fseek(mFile,len,SEEK_CUR);
}
break;
}
}
fclose(mFile);
}
void VGCDivLoader::parseHeader(FILE *file, unsigned int len)
{
//Set identifiable header props
BIZ_IDENTITIES ident;
unsigned int nlen;
while(len > 0)
{
len -= getBlock(file, ident,nlen);
len -= nlen;
switch(ident)
{
case HDR_VERSION:
fread(&mHeaderInfo.verMajor,1,1,file);
fread(&mHeaderInfo.verMinor,1,1,file);
break;
case HDR_DATE:
fread(&mHeaderInfo.dateDay,1,1,file);
fread(&mHeaderInfo.dateMon,1,1,file);
fread(&mHeaderInfo.dateYear,1,1,file);
break;
case HDR_TIME:
fread(&mHeaderInfo.timeHour,1,1,file);
fread(&mHeaderInfo.timeMin,1,1,file);
break;
case HDR_SCALE:
fread(&mHeaderInfo.scale,floatSize(),1,file);
break;
case HDR_PRECISION:
char precision;
fread(&precision,1,1,file);
mHeaderInfo.precision = (FloatPrecision)precision;
if (mHeaderInfo.precision == PRECISION_64BIT)
{
//Don't believe we have any 64 bit files, so fuck it
MessageBox(NULL,_T("PANIC 64 BIT"),_T("OH NOES"),MB_OK);
std::cout << "PANIC: 64 BIT PRECISION." << std::endl;
std::cin.get();
exit(1);
}
break;
case HDR_FILETYPE:
char type;
fread(&type,1,1,file);
mHeaderInfo.type = (FileType)type;
break;
case HDR_UNIT:
char unit;
fread(&unit,1,1,file);
mHeaderInfo.unit = (Unit)unit;
break;
default:
cout << "UNKNOWN HEADER ENTRY " << ident << endl;
}
}
}
TexBuffer *VGCDivLoader::LoadVTXFile(char *fileName, DivTexture *tex)
{
int len = strlen(fileName) + 5;
char *fullFileName = new char[len];
strcpy_s(fullFileName, len, fileName);
strcat_s(fullFileName, len, ".VTX");
FILE *texFile;
if (fopen_s(&texFile, fullFileName, "rb") != 0)
{
delete [] fullFileName;
return NULL;
}
delete [] fullFileName;
char id[8];
fread(id, 1, 8, texFile);
if (strncmp(id, "DIV-VTX2", 8) != 0)
{
fclose(texFile);
return NULL;
}
unsigned int blen;
BIZ_IDENTITIES ident;
while(!feof(texFile))
{
//Retrieve ID and length of upcoming block
getBlock(texFile, ident, blen);
switch(ident)
{
case HEADER:
fseek(texFile, blen, SEEK_CUR);
break;
case VTX_TEX:
tex->texBuffer = new TexBuffer;
unsigned int nlen;
while(blen > 0)
{
blen -= getBlock(texFile, ident, nlen);
blen -= nlen;
unsigned char pixelWidth;
unsigned char colorSettings;
switch(ident)
{
case VTX_COLOR:
fread(&colorSettings, 1, 1, texFile);
break;
case VTX_SIZE:
fread(&pixelWidth, 1, 1, texFile);
break;
case VTX_DIMENSIONS:
fread(&tex->texBuffer->width, 4, 1, texFile);
fread(&tex->texBuffer->height, 4, 1, texFile);
break;
default:
if (ident >= 0x68 && ident == 0x68 + colorSettings)
{
int dataPoints = 1;
if (colorSettings & 0x01)
{
dataPoints++;
}
if (colorSettings & 0x02)
{
dataPoints += 2;
}
int sampleSize = 1;
if (pixelWidth != 0x01)
{
MessageBox(NULL, _T("PANIC: UNEXPECTED SAMPLE SIZE FOR TEX"), _T("PROBLEM"), MB_OK);
}
int tLen = nlen / (dataPoints * sampleSize);
tex->texBuffer->m_buffer = new RGBA*[tex->texBuffer->height];
for (int i=0; i < tex->texBuffer->height; i++)
{
tex->texBuffer->m_buffer[i] = new RGBA[tex->texBuffer->width];
for (int j=0; j< tex->texBuffer->width; j++)
{
fread(&tex->texBuffer->m_buffer[i][j].r, 1, 1, texFile);
if (colorSettings & 0x02)
{
fread(&tex->texBuffer->m_buffer[i][j].g, 1, 1, texFile);
fread(&tex->texBuffer->m_buffer[i][j].b, 1, 1, texFile);
} else
{
tex->texBuffer->m_buffer[i][j].g = tex->texBuffer->m_buffer[i][i].r;
tex->texBuffer->m_buffer[i][j].b = tex->texBuffer->m_buffer[i][i].r;
}
if (colorSettings & 0x01)
{
fread(&tex->texBuffer->m_buffer[i][j].a, 1, 1, texFile);
} else
{
tex->texBuffer->m_buffer[i][j].a = 255;
}
}
}
} else
{
if (ident >= 0x68)
{
MessageBox(NULL, _T("BAD TEX INFO"), _T("PROBLEMS"), MB_OK);
} else
{
MessageBox(NULL, _T("UNKNOWN TAG IN VTX"), _T("PROBLEM"), MB_OK);
}
fseek(texFile, nlen, SEEK_CUR);
}
}
}
break;
case TRAILER:
fclose(texFile);
return tex->texBuffer;
break;
default:
MessageBox(NULL, _T("UNKNOWN VTX INFO"), _T("PROBLEM"), MB_OK);
fseek(texFile, blen, SEEK_CUR);
break;
}
}
fclose(texFile);
return tex->texBuffer;;
}
unsigned char getBSLData(int channel, int value)
{
int shift = (channel + (((channel + 1) % 2) * 2)) * 4;
int mask = 0xF0 << shift;
return (unsigned char)((value & mask) >> shift);
}
TexBuffer *VGCDivLoader::LoadBSLFile(char *fileName, char channelIndex, DivTexture *tex)
{
int len = strlen(fileName) + 5;
char *fullFileName = new char[len];
strcpy_s(fullFileName, len, fileName);
strcat_s(fullFileName, len, ".BSL");
FILE *texFile;
if (fopen_s(&texFile, fullFileName, "rb") != 0)
{
delete [] fullFileName;
return NULL;
}
char id[8];
fread(id, 1, 8, texFile);
delete [] fullFileName;
if (strncmp(id, "DIV-BSL2", 8) != 0)
{
fclose(texFile);
return NULL;
}
int width, height, depth, nTextures;
fread(&width, 4, 1, texFile);
fread(&height, 4, 1, texFile);
fread(&depth, 4, 1, texFile);
fseek(texFile, 4, SEEK_CUR);
fread(&nTextures, 4, 1, texFile);
int usefulChannel = channelIndex;
for (int i=0; i < nTextures; i++)
{
int header, channel;
fread(&header, 4, 1, texFile);
fread(&channel, 4, 1, texFile);
char *chanName = new char[header-4];
fread(chanName, 1, header - 4, texFile);
delete [] chanName;
}
tex->texBuffer = new TexBuffer;
tex->texBuffer->height = height;
tex->texBuffer->width = width;
tex->texBuffer->m_buffer = new RGBA*[height];
for (int i=0; i <height; i++)
{
tex->texBuffer->m_buffer[i] = new RGBA[width];
for (int j=0; j < width; j++)
{
int val;
fread(&val, 4, 1, texFile);
if (usefulChannel < 6)
{
unsigned char color = getBSLData(usefulChannel,val);
tex->texBuffer->m_buffer[i][j].a = 255;
tex->texBuffer->m_buffer[i][j].r = color;
tex->texBuffer->m_buffer[i][j].g = color;
tex->texBuffer->m_buffer[i][j].b = color;
} else
{
if (usefulChannel == 8)
{
tex->texBuffer->m_buffer[i][j].a = getBSLData(2, val);
} else
{
tex->texBuffer->m_buffer[i][j].a = 255;
}
tex->texBuffer->m_buffer[i][j].r = getBSLData(5, val);
tex->texBuffer->m_buffer[i][j].g = getBSLData(4, val);
tex->texBuffer->m_buffer[i][j].b = getBSLData(3, val);
}
}
}
return tex->texBuffer;
}
void VGCDivLoader::parseTexture(FILE *file, unsigned int len)
{
BIZ_IDENTITIES ident;
unsigned int nlen;
DivTexture *texture = new DivTexture;
memset(texture,0,sizeof(DivTexture));
texture->alpha = 255;
char *texMapName = NULL;
char channelIndex = 0;
while(len > 0)
{
len -= getBlock(file, ident,nlen);
len -= nlen;
char *fileName; //Name of tex raster file
char *ran; //Temp place for strings
switch(ident)
{
case GEN_NAME:
{
texture->name = new char[nlen];
fread(texture->name, 1, nlen, file);
break;
}
case GEN_SCOPE:
fseek(file,1,SEEK_CUR);
break;
case TEX_MAP:
texMapName = new char[nlen];
fread(texMapName,1,nlen,file);
break;
case TEX_MINIFY:
fread(&texture->minify,1,1,file);
break;
case TEX_MAGNIFY:
fread(&texture->magnify,1,1,file);
break;
case TEX_ALPHA:
fread(&texture->alpha,1,1,file);
break;
case TEX_WRAP_U:
fread(&texture->wrap_u,1,1,file);
break;
case TEX_WRAP_V:
fread(&texture->wrap_v, 1, 1, file);
break;
case TEX_DETAIL:
fread(&texture->detail_type, 1, 1, file);
if (nlen > 1)
{
texture->detail_name = new char[nlen - 1];
fread(texture->detail_name, 1, nlen - 1, file);
}
break;
case TEX_BSL_CHANNEL:
//This is the channel index being loaded for a BSL file
fread(&channelIndex, 1, 1, file);
break;
case SRF_SPECIAL:
{
char *pos;
//Awesome
ran = new char[nlen];
fread(ran,1,nlen,file);
if ((pos = strstr(ran,"SCROLL")) != NULL)
{
float startu,startv,endu,endv;
sscanf(pos, "SCROLL %f %f %f %f",&startu, &startv, &endu, &endv);
texture->doScroll = true;
texture->scrollU = endu;
texture->scrollV = endv;
}
delete [] ran;
break;
}
default:
MessageBox(NULL,_T("UNKNOWN TEXTURE ENTRY"),_T("PROBLEM"),MB_OK);
if (!feof(file))
{
fseek(file, nlen, SEEK_CUR);
}
}
}
//Load up the texture buffer
//Try to load as a BSL first
if ((texture->texBuffer = LoadBSLFile(texMapName, channelIndex, texture)) == NULL)
{
//If that didn't work, do VTX
texture->texBuffer = LoadVTXFile(texMapName, texture);
}
delete [] texMapName;
if (texture->name == NULL)
{
delete [] texture->name;
if (texture->detail_name != NULL)
{
delete [] texture->detail_name;
}
if (texture->texBuffer != NULL)
{
delete texture->texBuffer;
}
delete texture;
fseek(file,len,SEEK_CUR);
} else
{
//Add material
mTextures.insert(pair<string, DivTexture*>(string(texture->name),texture));
}
}
bool VGCDivLoader::parseMaterial(FILE *file, unsigned int len, char *filename)
{
BIZ_IDENTITIES ident;
unsigned int nlen;
DivMaterial *material = new DivMaterial;
memset(material,0,sizeof(DivMaterial));
material->amb_b = 1;
material->amb_g = 1;
material->amb_r = 1;
material->dif_b = 1;
material->dif_g = 1;
material->dif_r = 1;
material->emi_b = 0;
material->emi_g = 0;
material->emi_r = 0;
material->spe_b = 0;
material->spe_g = 0;
material->spe_p = 1;
material->spe_r = 0;
material->opa_b = 1;
material->opa_g = 1;
material->opa_r = 1;
material->isImmune = false;
while(len > 0)
{
len -= getBlock(file, ident, nlen);
len -= nlen;
char *ran; //Temp place for strings
switch(ident)
{
case GEN_NAME:
material->name = new char[nlen];
fread(material->name, 1, nlen, file);
break;
case GEN_SCOPE:
fseek(file,1,SEEK_CUR);
break;
case MAT_TEXTURE:
fread(&material->tex_type, 1, 1, file);
if (nlen > 1)
{
material->tex_name = new char[nlen - 1];
fread(material->tex_name, 1, nlen - 1, file);
}
//Precaching textures moves the file offset, so:
/*loc = ftell(file);
precacheTexture(material->tex_name,file);
fseek(file,loc,SEEK_SET);*/
//We don't precache textures anymore- they should be local, so screw it
break;
case MAT_ENVIRONMENT:
//Anyone know what this is for?
fread(&material->env_type, 1, 1, file);
material->env_name = new char[nlen - 1];
fread(material->env_name, 1, nlen - 1, file);
break;
case MAT_AMBIENT:
fread(&material->amb_r, floatSize(), 1, file);
fread(&material->amb_g, floatSize(), 1, file);
fread(&material->amb_b, floatSize(), 1, file);
break;
case MAT_DIFFUSE:
fread(&material->dif_r, floatSize(), 1, file);
fread(&material->dif_g, floatSize(), 1, file);
fread(&material->dif_b, floatSize(), 1, file);
break;
case MAT_SPECULAR:
fread(&material->spe_r, floatSize(), 1, file);
fread(&material->spe_g, floatSize(), 1, file);
fread(&material->spe_b, floatSize(), 1, file);
fread(&material->spe_p, floatSize(), 1, file);
break;
case MAT_EMISSIVE:
fread(&material->emi_r, floatSize(), 1, file);
fread(&material->emi_g, floatSize(), 1, file);
fread(&material->emi_b, floatSize(), 1, file);
break;
case MAT_OPACITY:
fread(&material->opa_r, floatSize(), 1, file);
fread(&material->opa_g, floatSize(), 1, file);
fread(&material->opa_b, floatSize(), 1, file);
break;
case MAT_RAMP:
material->ramp_name = new char[nlen];
fread(material->ramp_name, 1, nlen, file);
break;
case SRF_SPECIAL:
{
//Awesome!
ran = new char[nlen];
fread(ran,1,nlen,file);
char *immuneParam = strstr(ran, "IMMUNE");
if (immuneParam != NULL)
{
material->isImmune = true;
}
delete [] ran;
break;
}
default:
MessageBox(NULL,_T("UNKNOWN MATERIAL ENTRY"),_T("PROBLEM"),MB_OK);
if (!feof(file))
{
fseek(file, nlen, SEEK_CUR);
}
}
}
if (material->name == NULL)
{
//This material is nameless?
if (material->env_name)
{
delete [] material->env_name;
}
if (material->tex_name)
{
delete [] material->tex_name;
}
if (material->ramp_name)
{
delete [] material->ramp_name;
}
delete material;
return false;
} else
{
//If there's no material, set it to null.
if (material->tex_type != 2)
{
if (material->tex_name != NULL)
{
delete [] material->tex_name;
}
material->tex_type = 2;
material->tex_name = new char[5];
strcpy_s(material->tex_name, 5, "NULL");
}
//Add material
string fullMatName = string(filename);
fullMatName += string(":");
fullMatName += string(material->name);
mMaterials.insert(pair<string, DivMaterial*>(fullMatName,material));
return true;
}
}
void VGCDivLoader::precacheMaterial(char *name)
{
FILE *matFile = NULL;
//Get material file name
char *colon = strstr(name, ":");
int len = colon - name;
char *fname = new char[(len + 5)];
char *matFileName = new char[len + 1];
strncpy(matFileName,name,len);
matFileName[len] = 0;
strncpy(fname, name, len);
fname[len] = 0;
strcat_s(fname,len + 5, ".BMF");
//Get actual material name
int matNameLen = strlen(name) - len;
char *mname = new char[matNameLen];
strcpy_s(mname, matNameLen, colon + 1);
if (mMaterials.find(string(name)) != mMaterials.end())
{
//Already precached
return;
}
//Open the BMF file
int fullDirLen = strlen(fname) + 1;
char *fulldir = new char[fullDirLen];
strcpy_s(fulldir, fullDirLen, fname);
delete [] fname;
if (fopen_s(&matFile,fulldir,"rb") != 0)
{
MessageBox(NULL,_T("PANIC MAT FILE FAILED TO OPEN"),_T("SHIT"),MB_OK);
}
delete [] fulldir;
//Parse the file
char id[8];
fread(&id, 8, 1, matFile);
//Compare first 8 bits to make sure it's a biz2 file
if (strncmp(id,"DIV-BIZ2",8) != 0)
{
fclose(matFile);
MessageBox(NULL, _T("PANIC MAT FILE WAS NOT MAT FILE"),_T("Oh no"),MB_OK);
}
unsigned int blen;
BIZ_IDENTITIES ident;
bool done = false;
while(!feof(matFile))
{
//Retrieve ID and length of upcoming block
getBlock(matFile, ident, blen);
switch(ident)
{
case MATERIAL:
//This is the only relevant thing right now
parseMaterial(matFile, blen,matFileName);
break;
case RAMP:
parseRamp(matFile,blen);
break;
case TEXTURE:
parseTexture(matFile, blen);
break;
default:
if (!feof(matFile))
{
fseek(matFile,blen,SEEK_CUR);
}
}
}
delete [] mname;
delete [] matFileName;
}
void VGCDivLoader::parseRamp(FILE *file, unsigned int len)
{
BIZ_IDENTITIES ident;
unsigned int nlen;
DivRamp *ramp = new DivRamp;
memset(ramp, 0, sizeof(DivRamp));
char *name;
while(len > 0)
{
len -= getBlock(file, ident, nlen);
len -= nlen;
switch(ident)
{
case GEN_NAME:
name = new char[nlen];
fread(name, 1, nlen, file);
if (mRamps.find(string(name)) != mRamps.end())
{
delete [] name;
delete ramp;
fseek(file,len,SEEK_CUR);
return;
}
break;
case GEN_SCOPE:
if (nlen > 0)
{
fseek(file, nlen, SEEK_CUR);
}
break;
case RMP_DATA:
fread(&ramp->r1, floatSize(), 1, file);
fread(&ramp->g1, floatSize(), 1, file);
fread(&ramp->b1, floatSize(), 1, file);
fread(&ramp->r2, floatSize(), 1, file);
fread(&ramp->g2, floatSize(), 1, file);
fread(&ramp->b2, floatSize(), 1, file);
break;
default:
MessageBox(NULL, _T("UNRECOGNIZED RAMP BLOCK"), _T("PROBLEM"), MB_OK);
if (nlen > 0)
{
fseek(file, nlen, SEEK_CUR);
}
break;
}
}
mRamps[string(name)] = ramp;
delete [] name;
}
void VGCDivLoader::parseObject(FILE *file, unsigned int len, char *fileName)
{
BIZ_IDENTITIES ident;
unsigned int nlen;
DivObject *obj = new DivObject;
obj->objName = new char[strlen(fileName) + 1];
strcpy_s(obj->objName, strlen(fileName) + 1, fileName);
while(len > 0)
{
len -= getBlock(file, ident,nlen);
len -= nlen;
switch(ident)
{
//Set the master set of surface props
case GEN_NAME:
case SRF_F_MATERIAL:
case SRF_B_MATERIAL:
case SRF_PLANE:
case SRF_DRAWMODE:
case SRF_DECAL:
case SRF_FACETED:
case SRF_VERTEX:
case SRF_SPECIAL:
case SRF_LOCK:
parseSurface(file, ident,nlen,mMasterProps);
break;
case LOD:
parseLOD(obj, file, nlen);
break;
case PATCH:
parsePatch(obj, file, nlen);
break;
}
}
VGCDivLoader::mObjects.insert(std::pair<string, DivObject*>(string(obj->objName), obj));
}
void VGCDivLoader::parseLOD(DivObject *obj, FILE *file, unsigned int len)
{
//LOD's are only relevant if it's the closest LOD level,
//and the only relevant part are the patches
BIZ_IDENTITIES ident;
unsigned int nlen;
bool parse = false;
while(len > 0)
{
len -= getBlock(file, ident,nlen);
len -= nlen;
switch(ident)
{
case LOD_DISTANCE:
float in, out;
fread(&in,floatSize(),1,file);
fread(&out,floatSize(),1,file);
if (in < 5.0f)
{
//Don't even bother if this isn't a seriously close-up LOD
parse = true;
}
break;
case PATCH:
if (parse)
{
//If this is the right LOD, parse patches
parsePatch(obj, file, nlen);
} else
{
fseek(file,nlen,SEEK_CUR);
}
break;
default:
fseek(file,nlen,SEEK_CUR);
}
}
}
void VGCDivLoader::parsePatch(DivObject *obj, FILE *file, unsigned int len)
{
BIZ_IDENTITIES ident;
unsigned int nlen;
bool parse = false;
DivPatch *patch = new DivPatch;
patch->surface = mMasterProps;
int *indices;
DivVertex *verts;
while(len > 0)
{
len -= getBlock(file, ident,nlen);
len -= nlen;
int n;
switch(ident)
{
case GEN_NAME:
case SRF_F_MATERIAL:
case SRF_B_MATERIAL:
case SRF_PLANE:
case SRF_DRAWMODE:
case SRF_DECAL:
case SRF_FACETED:
case SRF_VERTEX:
case SRF_SPECIAL:
case SRF_LOCK:
//Edit our patch's surface props
parseSurface(file, ident,nlen,patch->surface);
break;
case POLYGON:
case POLYSTRIP:
//Polystrip is a tri fan. Incidentally, we're treating
//>3 sided polys like tri fans
nlen = 0;
while (!(ident & 0x80))
{
//Poly lists are the only relevant blocks
if (nlen > 0)
{
fseek(file,nlen,SEEK_CUR);
}
getBlock(file, ident,nlen);
}
//Parse list of polys
verts = parseVList(file, nlen,(unsigned char)(ident & 0x7F),n);
indices = new int[n];
for (int i=0; i<n; i++)
{
//Read in vertices to a list of indices
indices[i] = getVertex(verts[i], obj);
}
//addPolygon is what breaks down our list of indices down
//into multiple tris
addPolygon(indices,n,patch);
delete [] verts;
delete [] indices;
break;
case TRISTRIP:
nlen = 0;
while (!(ident & 0x80))
{
if (nlen > 0)
{
fseek(file,nlen,SEEK_CUR);
}
getBlock(file, ident,nlen);
}
verts = parseVList(file, nlen,(unsigned char)(ident & 0x7F),n);
indices = new int[n];
for (int i=0; i<n; i++)
{
//Read in vertices
indices[i] = getVertex(verts[i], obj);
}
for (int i=0; i < n-2; i++)
{
//For every tri in the strip, add the tri
addPolygon(indices,3,patch);
}
delete [] verts;
delete [] indices;
break;
case PMESH:
//PMESHES ARE HARD
parsePMesh(file, nlen,patch, obj);
break;
case SPHERE_LIST:
{
unsigned int inlen;
BIZ_IDENTITIES inident;
//Skip the width, read the verts
patch->surface.drawSpheres = true;
vector<DivVertex> verts;
while (nlen > 0)
{
nlen -= getBlock(file, inident, inlen);
nlen -= inlen;
if (inident == SPHERE)
{
for (int i=0; i < (inlen/(floatSize()*4)); i++)
{
float x,y,z,radius;
fread(&x,floatSize(),1,file);
fread(&y,floatSize(),1,file);
fread(&z,floatSize(),1,file);
fread(&radius,floatSize(),1,file);
DivVertex vert;
vert.x = x * this->mHeaderInfo.scale;
vert.y = y * this->mHeaderInfo.scale;
vert.z = z * this->mHeaderInfo.scale;
vert.mask = 0;
verts.insert(verts.end(),vert);
}
} else if (inlen > 0)
{
fseek(file,nlen,SEEK_CUR);
}
}
for (int i=0; i < verts.size(); i++)
{
getVertex(verts[i], obj);
}
//setupDrawOps(props);
break;
}
case LINE_LIST:
//Ignore these
fseek(file,nlen,SEEK_CUR);
break;
case TEXT:
//Ignore these
fseek(file,nlen,SEEK_CUR);
break;
default:
cout << "UNKNOWN PATCH VAR " << ident << endl;
}
}
obj->patches.insert(obj->patches.end(), patch);
}
void VGCDivLoader::parseSurface(FILE *file, BIZ_IDENTITIES ident, unsigned int len, SurfaceProps &props)
{
//These are really straightforward- just move the value
//from the file to the SurfaceProps struct
switch(ident)
{
case GEN_NAME:
props.name = new char[len];
fread(props.name,1,len,file);
break;
case SRF_F_MATERIAL:
fread(&props.f_material_type,1,1,file);
props.faceted |= 0x02;
if (len > 1)
{
props.f_material_name = new char[len-1];
fread(props.f_material_name,1,len-1,file);
precacheMaterial(props.f_material_name);
}
break;
case SRF_B_MATERIAL:
fread(&props.b_material_type,1,1,file);
props.faceted |= 0x01;
if (len > 1)
{
props.b_material_name = new char[len-1];
fread(props.b_material_name,1,len-1,file);
precacheMaterial(props.b_material_name);
}
break;
case SRF_PLANE:
fread(&props.plane,1,1,file);
break;
case SRF_DRAWMODE:
fread(&props.drawmode,1,1,file);
fread(&props.drawmode_width,1,1,file);
break;
case SRF_DECAL:
fread(&props.decal_offset,4,1,file);
break;
case SRF_FACETED:
fread(&props.faceted,1,1,file);
break;
case SRF_VERTEX:
fread(&props.vertex_mask,1,1,file);
break;
case SRF_SPECIAL:
props.specialInst = new char[len];
fread(props.specialInst,1,len,file);
break;
case SRF_LOCK:
fread(&props.locks,1,1,file);
break;
default:
cout << "UNKNOWN SURFACE VAR " << ident << endl;
}
}
void VGCDivLoader::parsePMesh(FILE *file, unsigned int len, DivPatch *patch, DivObject *obj)
{
//PMeshes are a list of vertices, and a list of vertex indices
//The pmeshes default to 3 side per poly, but can be set different
BIZ_IDENTITIES ident;
unsigned int nlen;
bool parse = false;
std::vector<ConList *> conLists;
DivVertex *vertList;
while(len > 0)
{
len -= getBlock(file, ident,nlen);
len -= nlen;
ConList *list;
switch(ident)
{
case PMESH_CONLIST:
//old conlists are tris only
list = new ConList;
list->sideCount = 3;
list->numIndices = nlen / 4;
list->indices = new int[list->numIndices];
fread(list->indices,4,list->numIndices,file);
conLists.insert(conLists.begin(),list);
break;
case PMESH_CONLIST_NEW:
//Newer ones specify the sideage of the polys
list = new ConList;
fread(&list->sideCount,1,1,file);
list->numIndices = (nlen-1) / 4;
list->indices = new int[list->numIndices];
fread(list->indices,4,list->numIndices,file);
conLists.insert(conLists.begin(),list);
break;
default:
if (ident & 0x80)
{
//Read in verts like normal
int n;
vertList = parseVList(file, nlen,(unsigned char)(ident & 0x7F), n);
} else
{
cout << "UNKNOWN PMESH VAR " << ident << endl;
fseek(file,nlen,SEEK_CUR);
}
}
}
while(!conLists.empty())
{
//For each connection list, add polygons from the conlist
//and destroy the connection list
ConList *list = *conLists.begin();
//Replace local vertex indexes with global ones
for (int i=0; i < list->numIndices; i++)
{
list->indices[i] = getVertex(vertList[list->indices[i]], obj);
}
//Add the individual polygons
for (int i=0; i < list->numIndices; i += list->sideCount)
{
addPolygon(list->indices + i,list->sideCount, patch);
}
delete [] list->indices;
delete list;
conLists.erase(conLists.begin());
}
delete [] vertList;
}
DivVertex *VGCDivLoader::parseVList(FILE *file, unsigned int len, unsigned char mask, int &n)
{
//The passed-in mask is the format of the vertices, and
//determines the in-file structure
//Figure out the size-per-vertex
int size = floatSize() * 3;
if (mask & 0x01)
{
//Normals
size += floatSize() * 3;
}
if (mask & 0x02)
{
//RGB
size += floatSize() * 4;
} else if (mask & 0x04)
{
//Luminance
size += floatSize() * 2;
}
if (mask & 0x08)
{
//2D Tex
size += floatSize() * 2;
} else if (mask & 0x10)
{
//3D Tex
size += floatSize() * 3;
}
//Use the size-per-vertex to derive the number of vertices
n = len / size;
DivVertex *buffer = new DivVertex[n];
//Read in each vertex
for (int i=0; i < n; i++)
{
buffer[i].mask = mask;
fread(&buffer[i].x,floatSize(),1,file);
fread(&buffer[i].y,floatSize(),1,file);
fread(&buffer[i].z,floatSize(),1,file);
//buffer[i].z = -buffer[i].z;
buffer[i].x *= this->mHeaderInfo.scale;
buffer[i].y *= this->mHeaderInfo.scale;
buffer[i].z *= this->mHeaderInfo.scale;
if (mask & 0x01)
{
//Normals
fread(&buffer[i].Nx,floatSize(),1,file);
fread(&buffer[i].Ny,floatSize(),1,file);
fread(&buffer[i].Nz,floatSize(),1,file);
} else
{
buffer[i].Nx = 0;
buffer[i].Ny = 0;
buffer[i].Nz = 0;
}
if (mask & 0x02)
{
//RGB
fread(&buffer[i].r,floatSize(),1,file);
fread(&buffer[i].g,floatSize(),1,file);
fread(&buffer[i].b,floatSize(),1,file);
}
if (mask & 0x04)
{
//Luminance
fread(&buffer[i].l,floatSize(),1,file);
}
if (mask & 0x06)
{
//RGB or Luminance
fread(&buffer[i].a,floatSize(),1,file);
}
if (mask & 0x18)
{
//2D or 3D texture
fread(&buffer[i].u,floatSize(),1,file);
//buffer[i].u = 1.0f - buffer[i].u;
fread(&buffer[i].v,floatSize(),1,file);
}
if (mask & 0x10)
{
//3D texture
fread(&buffer[i].w,floatSize(),1,file);
}
}
return buffer;
}
int VGCDivLoader::getBlock(FILE *file, BIZ_IDENTITIES &id, unsigned int &len)
{
//BIZ_IDENTTAG is ryan's structure that mimics the in-file structure
//of the first two bytes of a block
BIZ_IDENTTAG ident;
fread(&ident,sizeof(BIZ_IDENTTAG),1,file);
//Get ID value
int idVal = ident.identity;
idVal = (ident.configBit)?(idVal+2000):idVal;
id = (BIZ_IDENTITIES)idVal;
//Get File Length
if (ident.headerSize == 2)
{
fread(&len,4,1,file);
return 6;
} else if (ident.headerSize == 1)
{
unsigned short size;
fread(&size,2,1,file);
len = size;
return 4;
} else
{
unsigned char size;
fread(&size,1,1,file);
len = size;
return 3;
}
}
int VGCDivLoader::floatSize()
{
return 4;
}
int VGCDivLoader::getVertex(DivVertex vert, DivObject *obj)
{
//Try and find vertices, if the vertex doesn't exist, add it
std::vector<DivVertex>::iterator it = obj->vertices.begin();
int i=0;
while(it != obj->vertices.end() && memcmp(&(*it),&vert,sizeof(DivVertex)) != 0) {++it; ++i;}
if (it == obj->vertices.end())
{
obj->vertices.insert(obj->vertices.end(),vert);
}
//Return the vertex's index in the VGCDivLoader's vertex list
return i;
}
void VGCDivLoader::addPolygon(int *indices, int numIndices, DivPatch *patch)
{
//Break the polygon down into a triangle fan and add it to the tris
char facets = patch->surface.faceted;
for (int i=0; i < numIndices - 2; i++)
{
if (facets & 0x02)
{
Triangle tri;
tri.indices[2] = indices[0];
tri.indices[1] = indices[i+1];
tri.indices[0] = indices[i+2];
patch->frontTriangles.insert(patch->frontTriangles.end(),tri);
}
if (facets & 0x01)
{
Triangle tri;
tri.indices[0] = indices[0];
tri.indices[1] = indices[i+1];
tri.indices[2] = indices[i+2];
patch->backTriangles.insert(patch->backTriangles.end(),tri);
}
}
}