📄 wmo.cpp
字号:
}
struct WMOGroupHeader {
int nameStart, nameStart2, flags;
float box1[3], box2[3];
short portalStart, portalCount;
short batches[4];
uint8 fogs[4];
int32 unk1, id, unk2, unk3;
};
void WMOGroup::initDisplayList()
{
Vec3D *vertices, *normals;
Vec2D *texcoords;
unsigned short *indices;
unsigned short *materials;
WMOBatch *batches;
int nBatches;
WMOGroupHeader gh;
short *useLights = 0;
int nLR = 0;
// open group file
char temp[256];
strcpy(temp, wmo->name.c_str());
temp[wmo->name.length()-4] = 0;
char fname[256];
sprintf(fname,"%s_%03d.wmo",temp, num);
MPQFile gf(fname);
gf.seek(0x14);
// read header
gf.read(&gh, sizeof(WMOGroupHeader));
WMOFog &wf = wmo->fogs[gh.fogs[0]];
if (wf.r2 <= 0) fog = -1; // default outdoor fog..?
else fog = gh.fogs[0];
name = string(wmo->groupnames + gh.nameStart);
desc = string(wmo->groupnames + gh.nameStart2);
b1 = Vec3D(gh.box1[0], gh.box1[2], -gh.box1[1]);
b2 = Vec3D(gh.box2[0], gh.box2[2], -gh.box2[1]);
gf.seek(0x58); // first chunk
char fourcc[5];
size_t size;
unsigned int *cv;
hascv = false;
while (!gf.isEof()) {
gf.read(fourcc,4);
gf.read(&size, 4);
flipcc(fourcc);
fourcc[4] = 0;
size_t nextpos = gf.getPos() + size;
// why copy stuff when I can just map it from memory ^_^
if (!strcmp(fourcc,"MOPY")) {
// materials per triangle
nTriangles = (int)size / 2;
materials = (unsigned short*)gf.getPointer();
}
else if (!strcmp(fourcc,"MOVI")) {
// indices
indices = (unsigned short*)gf.getPointer();
}
else if (!strcmp(fourcc,"MOVT")) {
nVertices = (int)size / 12;
// let's hope it's padded to 12 bytes, not 16...
vertices = (Vec3D*)gf.getPointer();
vmin = Vec3D( 9999999.0f, 9999999.0f, 9999999.0f);
vmax = Vec3D(-9999999.0f,-9999999.0f,-9999999.0f);
rad = 0;
for (int i=0; i<nVertices; i++) {
Vec3D v(vertices[i].x, vertices[i].z, -vertices[i].y);
if (v.x < vmin.x) vmin.x = v.x;
if (v.y < vmin.y) vmin.y = v.y;
if (v.z < vmin.z) vmin.z = v.z;
if (v.x > vmax.x) vmax.x = v.x;
if (v.y > vmax.y) vmax.y = v.y;
if (v.z > vmax.z) vmax.z = v.z;
}
center = (vmax + vmin) * 0.5f;
rad = (vmax-center).length();
}
else if (!strcmp(fourcc,"MONR")) {
normals = (Vec3D*)gf.getPointer();
}
else if (!strcmp(fourcc,"MOTV")) {
texcoords = (Vec2D*)gf.getPointer();
}
else if (!strcmp(fourcc,"MOLR")) {
nLR = (int)size / 2;
useLights = (short*)gf.getPointer();
}
else if (!strcmp(fourcc,"MODR")) {
if (ddr) delete ddr;
nDoodads = (int)size / 2;
ddr = new short[nDoodads];
gf.read(ddr,size);
}
else if (!strcmp(fourcc,"MOBA")) {
nBatches = (int)size / 24;
batches = (WMOBatch*)gf.getPointer();
/*
// batch logging
gLog("\nWMO group #%d - %s\nVertices: %d\nTriangles: %d\nIndices: %d\nBatches: %d\n",
this->num, this->name.c_str(), nVertices, nTriangles, nTriangles*3, nBatches);
WMOBatch *ba = batches;
for (int i=0; i<nBatches; i++) {
gLog("Batch %d:\t", i);
for (int j=0; j<12; j++) {
if ((j%4)==0 && j!=0) gLog("| ");
gLog("%d\t", ba[i].bytes[j]);
}
gLog("| %d\t%d\t| %d\t%d\t", ba[i].indexStart, ba[i].indexCount, ba[i].vertexStart, ba[i].vertexEnd);
gLog("%d\t%d\t%s\n", ba[i].flags, ba[i].texture, wmo->textures[ba[i].texture].c_str());
}
int l = nBatches-1;
gLog("Max index: %d\n", ba[l].indexStart + ba[l].indexCount);
*/
}
else if (!strcmp(fourcc,"MOCV")) {
//gLog("CV: %d\n", size);
hascv = true;
cv = (unsigned int*)gf.getPointer();
}
else if (!strcmp(fourcc,"MLIQ")) {
// liquids
WMOLiquidHeader hlq;
gf.read(&hlq, 0x1E);
//gLog("WMO Liquid: %dx%d, %dx%d, (%f,%f,%f) %d\n", hlq.X, hlq.Y, hlq.A, hlq.B, hlq.pos.x, hlq.pos.y, hlq.pos.z, hlq.type);
//lq = new Liquid(hlq.A, hlq.B, Vec3D(hlq.pos.x, hlq.pos.z, -hlq.pos.y));
//lq->initFromWMO(gf, wmo->mat[hlq.type], (flags&0x2000)!=0);
}
// TODO: figure out/use MFOG ?
gf.seek((int)nextpos);
}
// ok, make a display list
indoor = (flags&8192)!=0;
//gLog("Lighting: %s %X\n\n", indoor?"Indoor":"Outdoor", flags);
initLighting(nLR,useLights);
dl = glGenLists(1);
glNewList(dl, GL_COMPILE);
glDisable(GL_BLEND);
glColor4f(1,1,1,1);
/*
float xr=0,xg=0,xb=0;
if (flags & 0x0040) xr = 1;
if (flags & 0x2000) xg = 1;
if (flags & 0x8000) xb = 1;
glColor4f(xr,xg,xb,1);
*/
// assume that texturing is on, for unit 1
for (int b=0; b<nBatches; b++) {
WMOBatch *batch = &batches[b];
WMOMaterial *mat = &wmo->mat[batch->texture];
// setup texture
glBindTexture(GL_TEXTURE_2D, mat->tex);
bool atest = (mat->transparent) != 0;
if (atest) {
glEnable(GL_ALPHA_TEST);
float aval = 0;
if (mat->flags & 0x80) aval = 0.3f;
if (mat->flags & 0x01) aval = 0.0f;
glAlphaFunc(GL_GREATER, aval);
}
if (mat->flags & 0x04) glDisable(GL_CULL_FACE);
else glEnable(GL_CULL_FACE);
/*
float fr,fg,fb;
fr = rand()/(float)RAND_MAX;
fg = rand()/(float)RAND_MAX;
fb = rand()/(float)RAND_MAX;
glColor4f(fr,fg,fb,1);
*/
bool overbright = ((mat->flags & 0x10) && !hascv);
if (overbright) {
// TODO: use emissive color from the WMO Material instead of 1,1,1,1
GLfloat em[4] = {1,1,1,1};
glMaterialfv(GL_FRONT, GL_EMISSION, em);
}
// render
glBegin(GL_TRIANGLES);
for (int t=0, i=batch->indexStart; t<batch->indexCount; t++,i++) {
int a = indices[i];
if (indoor && hascv) {
setGLColor(cv[a]);
}
glNormal3f(normals[a].x, normals[a].z, -normals[a].y);
glTexCoord2fv(texcoords[a]);
glVertex3f(vertices[a].x, vertices[a].z, -vertices[a].y);
}
glEnd();
if (overbright) {
GLfloat em[4] = {0,0,0,1};
glMaterialfv(GL_FRONT, GL_EMISSION, em);
}
if (atest) {
glDisable(GL_ALPHA_TEST);
}
}
glColor4f(1,1,1,1);
glEnable(GL_CULL_FACE);
glEndList();
gf.close();
// hmm
indoor = false;
ok = true;
}
void WMOGroup::initLighting(int nLR, short *useLights)
{
dl_light = 0;
// "real" lighting?
if ((flags&0x2000) && hascv) {
Vec3D dirmin(1,1,1);
float lenmin;
int lmin;
for (int i=0; i<nDoodads; i++) {
lenmin = 999999.0f*999999.0f;
lmin = 0;
WMOModelInstance &mi = wmo->modelis[ddr[i]];
for (int j=0; j<wmo->nLights; j++) {
WMOLight &l = wmo->lights[j];
Vec3D dir = l.pos - mi.pos;
float ll = dir.lengthSquared();
if (ll < lenmin) {
lenmin = ll;
dirmin = dir;
lmin = j;
}
}
mi.light = lmin;
mi.ldir = dirmin;
}
outdoorLights = false;
} else {
outdoorLights = true;
}
}
void WMOGroup::draw()
{
if (!ok) {
visible = false;
return;
}
visible = true;
if (hascv) {
glDisable(GL_LIGHTING);
}
//setupFog();
glCallList(dl);
if (hascv) {
glEnable(GL_LIGHTING);
}
}
void WMOGroup::drawDoodads(int doodadset)
{
if (!visible) return;
if (nDoodads==0) return;
if (doodadset<0) return;
//setupFog();
/*
float xr=0,xg=0,xb=0;
if (flags & 0x0040) xr = 1;
//if (flags & 0x0008) xg = 1;
if (flags & 0x8000) xb = 1;
glColor4f(xr,xg,xb,1);
*/
// draw doodads
glColor4f(1,1,1,1);
for (int i=0; i<nDoodads; i++) {
short dd = ddr[i];
bool inSet;
if (doodadset==-1) {
inSet = false;
} else {
inSet = ( ((dd >= wmo->doodadsets[doodadset].start) && (dd < (wmo->doodadsets[doodadset].start+wmo->doodadsets[doodadset].size)))
|| ( wmo->includeDefaultDoodads && (dd >= wmo->doodadsets[0].start) && ((dd < (wmo->doodadsets[0].start+wmo->doodadsets[0].size) )) ) );
}
if (inSet) {
WMOModelInstance &mi = wmo->modelis[dd];
if (!outdoorLights) {
glDisable(GL_LIGHT0);
WMOLight::setupOnce(GL_LIGHT2, mi.ldir, mi.lcol);
} else {
glEnable(GL_LIGHT0);
}
wmo->modelis[dd].draw();
}
}
glDisable(GL_LIGHT2);
glColor4f(1,1,1,1);
}
void WMOGroup::drawLiquid()
{
if (!visible) return;
/*
// draw liquid
// TODO: culling for liquid boundingbox or something
if (lq) {
setupFog();
if (outdoorLights) {
//gWorld->outdoorLights(true);
} else {
// TODO: setup some kind of indoor lighting... ?
//gWorld->outdoorLights(false);
glEnable(GL_LIGHT2);
glLightfv(GL_LIGHT2, GL_AMBIENT, Vec4D(0.1f,0.1f,0.1f,1));
glLightfv(GL_LIGHT2, GL_DIFFUSE, Vec4D(0.8f,0.8f,0.8f,1));
glLightfv(GL_LIGHT2, GL_POSITION, Vec4D(0,1,0,0));
}
glDisable(GL_BLEND);
glDisable(GL_ALPHA_TEST);
glDepthMask(GL_TRUE);
glColor4f(1,1,1,1);
lq->draw();
glDisable(GL_LIGHT2);
}
*/
}
void WMOGroup::setupFog()
{
/*
if (outdoorLights || fog==-1) {
gWorld->setupFog();
} else {
wmo->fogs[fog].setup();
}
*/
}
WMOGroup::~WMOGroup()
{
cleanup();
if (ddr) delete ddr;
}
void WMOGroup::cleanup()
{
if (dl) glDeleteLists(dl, 1);
dl = 0;
if (dl_light) glDeleteLists(dl_light, 1);
dl_light = 0;
//if (lq) delete lq; lq = 0;
ok = false;
}
void WMOFog::init(MPQFile &f)
{
f.read(this, 0x30);
color = Vec4D( ((color1 & 0x00FF0000) >> 16)/255.0f, ((color1 & 0x0000FF00) >> 8)/255.0f,
(color1 & 0x000000FF)/255.0f, ((color1 & 0xFF000000) >> 24)/255.0f);
float temp;
temp = pos.y;
pos.y = pos.z;
pos.z = -temp;
fogstart = fogstart * fogend;
}
void WMOFog::setup()
{
/*
if (gWorld->drawfog) {
glFogfv(GL_FOG_COLOR, color);
glFogf(GL_FOG_START, fogstart);
glFogf(GL_FOG_END, fogend);
glEnable(GL_FOG);
} else {
glDisable(GL_FOG);
}
*/
}
/*
int WMOManager::add(std::string name)
{
int id;
if (names.find(name) != names.end()) {
id = names[name];
items[id]->addref();
//gLog("Loading WMO %s [already loaded]\n",name.c_str());
return id;
}
// load new
WMO *wmo = new WMO(name);
id = nextID();
do_add(name, id, wmo);
return id;
}
WMOInstance::WMOInstance(WMO *wmo, MPQFile &f) : wmo (wmo)
{
float ff[3];
f.read(&id, 4);
f.read(ff,12);
pos = Vec3D(ff[0],ff[1],ff[2]);
f.read(ff,12);
dir = Vec3D(ff[0],ff[1],ff[2]);
f.read(ff,12);
pos2 = Vec3D(ff[0],ff[1],ff[2]);
f.read(ff,12);
pos3 = Vec3D(ff[0],ff[1],ff[2]);
f.read(&d2,4);
f.read(&d3,4);
doodadset = (d2 & 0xFFFF0000) >> 16;
//gLog("WMO instance: %s (%d, %d)\n", wmo->name.c_str(), d2, d3);
}
void WMOInstance::draw()
{
if (ids.find(id) != ids.end()) return;
ids.insert(id);
glPushMatrix();
glTranslatef(pos.x, pos.y, pos.z);
float rot = -90.0f + dir.y;
// TODO: replace this with a single transform matrix calculated at load time
glRotatef(dir.y - 90.0f, 0, 1, 0);
glRotatef(-dir.x, 0, 0, 1);
glRotatef(dir.z, 1, 0, 0);
wmo->draw(doodadset,pos,-rot);
glPopMatrix();
}
void WMOInstance::reset()
{
ids.clear();
}
std::set<int> WMOInstance::ids;
*/
void WMOModelInstance::init(char *fname, MPQFile &f){ filename = fname; model = 0; float ff[3],temp; f.read(ff,12); pos = Vec3D(ff[0],ff[1],ff[2]); temp = pos.z; pos.z = -pos.y; pos.y = temp; f.read(&w,4); f.read(ff,12); dir = Vec3D(ff[0],ff[1],ff[2]); f.read(&sc,4); f.read(&d1,4); lcol = Vec3D(((d1&0xff0000)>>16) / 255.0f, ((d1&0x00ff00)>>8) / 255.0f, (d1&0x0000ff) / 255.0f);}void glQuaternionRotate(const Vec3D& vdir, float w){ Matrix m; Quaternion q(vdir, w); m.quaternionRotate(q); glMultMatrixf(m);}void WMOModelInstance::draw(){ if (!model) return; glPushMatrix(); glTranslatef(pos.x, pos.y, pos.z); Vec3D vdir(-dir.z,dir.x,dir.y); glQuaternionRotate(vdir,w); glScalef(sc,-sc,-sc); model->draw(); glPopMatrix();}void WMOModelInstance::loadModel(ModelManager &mm)
{
model = (Model*)mm.items[mm.add(filename)];
model->isWMO = true;
}
void WMOModelInstance::unloadModel(ModelManager &mm)
{
mm.delbyname(filename);
model = 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -