📄 wmo.cpp
字号:
#include "wmo.h"
#include "util.h"
//#include "world.h"
//#include "liquid.h"
using namespace std;
WMO::WMO(std::string name): ManagedItem(name)
{
MPQFile f(name.c_str());
ok = !f.isEof();
if (!ok) {
//gLog("Error loading WMO %s\n", name.c_str());
return;
}
//gLog("Loading WMO %s\n", name.c_str());
char fourcc[5];
size_t size;
float ff[3];
char *ddnames;
groupnames = 0;
skybox = 0;
doodadset = -1;
includeDefaultDoodads = true;
char *texbuf=0;
while (!f.isEof()) {
f.read(fourcc,4);
f.read(&size, 4);
flipcc(fourcc);
fourcc[4] = 0;
size_t nextpos = f.getPos() + size;
if (!strcmp(fourcc,"MOHD")) {
unsigned int col;
// header
f.read(&nTextures, 4);
f.read(&nGroups, 4);
f.read(&nP, 4);
f.read(&nLights, 4);
f.read(&nModels, 4);
f.read(&nDoodads, 4);
f.read(&nDoodadSets, 4);
f.read(&col, 4);
f.read(&nX, 4);
f.read(ff,12);
v1 = Vec3D(ff[0],ff[1],ff[2]);
f.read(ff,12);
v2 = Vec3D(ff[0],ff[1],ff[2]);
groups = new WMOGroup[nGroups];
mat = new WMOMaterial[nTextures];
}
else if (!strcmp(fourcc,"MOTX")) {
// textures
texbuf = new char[size];
f.read(texbuf, size);
}
else if (!strcmp(fourcc,"MOMT")) {
// materials
//WMOMaterialBlock bl;
for (int i=0; i<nTextures; i++) {
WMOMaterial *m = &mat[i];
f.read(m, 0x40);
string texpath(texbuf+m->nameStart);
fixname(texpath);
m->tex = texturemanager.add(texpath);
textures.push_back(texpath);
// need repeat turned on glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
/*
// material logging
gLog("Material %d:\t%d\t%d\t%d\t%X\t%d\t%X\t%d\t%f\t%f",
i, m->flags, m->d1, m->transparent, m->col1, m->d3, m->col2, m->d4, m->f1, m->f2);
for (int j=0; j<5; j++) gLog("\t%d", m->dx[j]);
gLog("\t - %s\n", texpath.c_str());
*/
}
}
else if (!strcmp(fourcc,"MOGN")) {
groupnames = new char[size];
memcpy(groupnames,f.getPointer(),size);
//groupnames = (char*)f.getPointer();
}
else if (!strcmp(fourcc,"MOGI")) {
// group info - important information! ^_^
for (int i=0; i<nGroups; i++) {
groups[i].init(this, f, i, groupnames);
}
}
else if (!strcmp(fourcc,"MOLT")) {
// Lights?
for (int i=0; i<nLights; i++) {
WMOLight l;
l.init(f);
lights.push_back(l);
}
}
else if (!strcmp(fourcc,"MODN")) {
// models ...
// MMID would be relative offsets for MMDX filenames
if (size) {
ddnames = (char*)f.getPointer();
fixnamen(ddnames, size);
char *p=ddnames,*end=p+size;
while (p<end) {
string path(p);
p+=strlen(p)+1;
while ((p<end) && (*p==0)) p++;
//gWorld->modelmanager.add(path);
models.push_back(path);
}
f.seekRelative((int)size);
}
}
else if (!strcmp(fourcc,"MODS")) {
for (int i=0; i<nDoodadSets; i++) {
WMODoodadSet dds;
f.read(&dds, 32);
doodadsets.push_back(dds);
}
}
else if (!strcmp(fourcc,"MODD")) {
nModels = (int)size / 0x28;
for (int i=0; i<nModels; i++) {
int ofs;
f.read(&ofs,4);
//Model *m = (Model*)gWorld->modelmanager.items[gWorld->modelmanager.get(ddnames + ofs)];
WMOModelInstance mi;
mi.init(ddnames+ofs, f);
modelis.push_back(mi);
}
}
else if (!strcmp(fourcc,"MOSB")) {
if (size>4) {
string path = (char*)f.getPointer();
fixname(path);
if (path.length()) {
//gLog("SKYBOX:\n");
//sbid = gWorld->modelmanager.add(path);
//skybox = (Model*)gWorld->modelmanager.items[sbid];
/*if (!skybox->ok) {
gWorld->modelmanager.del(sbid);
skybox = 0;
}
*/
}
}
}
else if (!strcmp(fourcc,"MOPV")) {
WMOPV p;
for (int i=0; i<nP; i++) {
f.read(ff,12);
p.a = Vec3D(ff[0],ff[2],-ff[1]);
f.read(ff,12);
p.b = Vec3D(ff[0],ff[2],-ff[1]);
f.read(ff,12);
p.c = Vec3D(ff[0],ff[2],-ff[1]);
f.read(ff,12);
p.d = Vec3D(ff[0],ff[2],-ff[1]);
pvs.push_back(p);
}
}
else if (!strcmp(fourcc,"MOPR")) {
int nn = (int)size / 8;
WMOPR *pr = (WMOPR*)f.getPointer();
for (int i=0; i<nn; i++) {
prs.push_back(*pr++);
}
}
else if (!strcmp(fourcc,"MFOG")) {
int nfogs = (int)size / 0x30;
for (int i=0; i<nfogs; i++) {
WMOFog fog;
fog.init(f);
fogs.push_back(fog);
}
}
f.seek((int)nextpos);
}
f.close();
delete[] texbuf;
//for (int i=0; i<nGroups; i++) groups[i].initDisplayList();
}
WMO::~WMO()
{
if (ok) {
//gLog("Unloading WMO %s\n", name.c_str());
delete[] groups;
for (vector<string>::iterator it = textures.begin(); it != textures.end(); ++it) {
texturemanager.delbyname(*it);
}
/*
for (vector<string>::iterator it = models.begin(); it != models.end(); ++it) {
//gWorld->modelmanager.delbyname(*it);
}
*/
delete[] mat;
/*
if (skybox) {
delete skybox;
//gWorld->modelmanager.del(sbid);
}
*/
loadedModels.clear();
delete groupnames;
}
}
void WMO::loadGroup(int id)
{
if (id==-1) {
for (int i=0; i<nGroups; i++) {
groups[i].initDisplayList();
groups[i].visible = true;
}
}
else if (id>=0 && id<nGroups) {
groups[id].initDisplayList();
for (int i=0; i<nGroups; i++) {
groups[i].visible = (i==id);
if (i!=id) groups[i].cleanup();
}
}
updateModels();
}
void WMO::showDoodadSet(int id)
{
doodadset = id;
updateModels();
}
void WMO::updateModels()
{
// 1. look for visible models that aren't loaded
for (int i=0; i<nGroups; i++) if (groups[i].visible) groups[i].updateModels(true);
// 2. unload unused models
for (int i=0; i<nGroups; i++) if (!groups[i].visible) groups[i].updateModels(false);
}
void WMO::update(int dt)
{
loadedModels.resetAnim();
loadedModels.updateEmitters(dt/1000.0f);
}
void WMOGroup::updateModels(bool load)
{
if (!ddr || !ok || nDoodads==0)
return;
for (int i=0; i<nDoodads; i++) {
short dd = ddr[i];
bool inSet;
if (wmo->doodadset==-1) {
inSet = false;
} else {
inSet = ( ((dd >= wmo->doodadsets[wmo->doodadset].start) && (dd < (wmo->doodadsets[wmo->doodadset].start+wmo->doodadsets[wmo->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 (load && !mi.model)
mi.loadModel(wmo->loadedModels);
else if (!load && mi.model)
mi.unloadModel(wmo->loadedModels);
}
}
}
void WMO::draw()
{
if (!ok) return;
for (int i=0; i<nGroups; i++) {
groups[i].draw();
}
for (int i=0; i<nGroups; i++) {
groups[i].drawDoodads(doodadset);
}
for (int i=0; i<nGroups; i++) {
groups[i].drawLiquid();
}
/*
// draw light placeholders
glDisable(GL_LIGHTING);
glDisable(GL_CULL_FACE);
glDisable(GL_TEXTURE_2D);
glBegin(GL_TRIANGLES);
for (int i=0; i<nLights; i++) {
glColor4fv(lights[i].fcolor);
glVertex3fv(lights[i].pos);
glVertex3fv(lights[i].pos + Vec3D(-0.5f,1,0));
glVertex3fv(lights[i].pos + Vec3D(0.5f,1,0));
}
glEnd();
glEnable(GL_TEXTURE_2D);
glEnable(GL_CULL_FACE);
glEnable(GL_LIGHTING);
glColor4f(1,1,1,1);
*/
/*
// draw fog positions..?
glDisable(GL_LIGHTING);
glDisable(GL_TEXTURE_2D);
for (size_t i=0; i<fogs.size(); i++) {
WMOFog &fog = fogs[i];
glColor4f(1,1,1,1);
glBegin(GL_LINE_LOOP);
glVertex3fv(fog.pos);
glVertex3fv(fog.pos + Vec3D(fog.rad1, 5, -fog.rad2));
glVertex3fv(fog.pos + Vec3D(fog.rad1, 5, fog.rad2));
glVertex3fv(fog.pos + Vec3D(-fog.rad1, 5, fog.rad2));
glVertex3fv(fog.pos + Vec3D(-fog.rad1, 5, -fog.rad2));
glEnd();
}
glEnable(GL_TEXTURE_2D);
glEnable(GL_LIGHTING);
*/
/*
// draw group boundingboxes
glDisable(GL_LIGHTING);
glDisable(GL_TEXTURE_2D);
for (int i=0; i<nGroups; i++) {
WMOGroup &g = groups[i];
float fc[2] = {1,0};
glColor4f(fc[i%2],fc[(i/2)%2],fc[(i/3)%2],1);
glBegin(GL_LINE_LOOP);
glVertex3f(g.b1.x, g.b1.y, g.b1.z);
glVertex3f(g.b1.x, g.b2.y, g.b1.z);
glVertex3f(g.b2.x, g.b2.y, g.b1.z);
glVertex3f(g.b2.x, g.b1.y, g.b1.z);
glVertex3f(g.b2.x, g.b1.y, g.b2.z);
glVertex3f(g.b2.x, g.b2.y, g.b2.z);
glVertex3f(g.b1.x, g.b2.y, g.b2.z);
glVertex3f(g.b1.x, g.b1.y, g.b2.z);
glEnd();
}
// draw portal relations
glBegin(GL_LINES);
for (size_t i=0; i<prs.size(); i++) {
WMOPR &pr = prs[i];
WMOPV &pv = pvs[pr.portal];
if (pr.dir>0) glColor4f(1,0,0,1);
else glColor4f(0,0,1,1);
Vec3D pc = (pv.a+pv.b+pv.c+pv.d)*0.25f;
Vec3D gc = (groups[pr.group].b1 + groups[pr.group].b2)*0.5f;
glVertex3fv(pc);
glVertex3fv(gc);
}
glEnd();
glColor4f(1,1,1,1);
// draw portals
for (int i=0; i<nP; i++) {
glBegin(GL_LINE_STRIP);
glVertex3fv(pvs[i].d);
glVertex3fv(pvs[i].c);
glVertex3fv(pvs[i].b);
glVertex3fv(pvs[i].a);
glEnd();
}
glEnable(GL_TEXTURE_2D);
glEnable(GL_LIGHTING);
*/
}
void WMO::drawSkybox()
{
if (skybox) {
// TODO: only draw sky if we are "inside" the WMO... ?
// We need to clear the depth buffer, because the skybox model can (will?)
// require it *. This is inefficient - is there a better way to do this?
// * planets in front of "space" in Caverns of Time
//glClear(GL_DEPTH_BUFFER_BIT);
// update: skybox models seem to have an explicit renderop ordering!
// that saves us the depth buffer clear and the depth testing, too
/*
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glPushMatrix();
Vec3D o = gWorld->camera;
glTranslatef(o.x, o.y, o.z);
const float sc = 2.0f;
glScalef(sc,sc,sc);
skybox->draw();
glPopMatrix();
gWorld->hadSky = true;
glEnable(GL_DEPTH_TEST);
*/
}
}
/*
void WMO::drawPortals()
{
// not used ;)
glBegin(GL_QUADS);
for (int i=0; i<nP; i++) {
glVertex3fv(pvs[i].d);
glVertex3fv(pvs[i].c);
glVertex3fv(pvs[i].b);
glVertex3fv(pvs[i].a);
}
glEnd();
}
*/
void WMOLight::init(MPQFile &f)
{
char type[4];
f.read(&type,4);
f.read(&color,4);
f.read(pos, 12);
f.read(&intensity, 4);
f.read(unk, 4*5);
f.read(&r,4);
pos = Vec3D(pos.x, pos.z, -pos.y);
// rgb? bgr? hm
float fa = ((color & 0xff000000) >> 24) / 255.0f;
float fr = ((color & 0x00ff0000) >> 16) / 255.0f;
float fg = ((color & 0x0000ff00) >> 8) / 255.0f;
float fb = ((color & 0x000000ff) ) / 255.0f;
fcolor = Vec4D(fr,fg,fb,fa);
fcolor *= intensity;
fcolor.w = 1.0f;
/*
// light logging
gLog("Light %08x @ (%4.2f,%4.2f,%4.2f)\t %4.2f, %4.2f, %4.2f, %4.2f, %4.2f, %4.2f, %4.2f\t(%d,%d,%d,%d)\n",
color, pos.x, pos.y, pos.z, intensity,
unk[0], unk[1], unk[2], unk[3], unk[4], r,
type[0], type[1], type[2], type[3]);
*/
}
void WMOLight::setup(GLint light)
{
// not used right now -_-
GLfloat LightAmbient[] = {0, 0, 0, 1.0f};
GLfloat LightPosition[] = {pos.x, pos.y, pos.z, 0.0f};
glLightfv(light, GL_AMBIENT, LightAmbient);
glLightfv(light, GL_DIFFUSE, fcolor);
glLightfv(light, GL_POSITION,LightPosition);
glEnable(light);
}
void WMOLight::setupOnce(GLint light, Vec3D dir, Vec3D lcol)
{
Vec4D position(dir, 0);
//Vec4D position(0,1,0,0);
Vec4D ambient = Vec4D(lcol * 0.3f, 1);
//Vec4D ambient = Vec4D(0.101961f, 0.062776f, 0, 1);
Vec4D diffuse = Vec4D(lcol, 1);
//Vec4D diffuse = Vec4D(0.439216f, 0.266667f, 0, 1);
glLightfv(light, GL_AMBIENT, ambient);
glLightfv(light, GL_DIFFUSE, diffuse);
glLightfv(light, GL_POSITION,position);
glEnable(light);
}
void WMOGroup::init(WMO *wmo, MPQFile &f, int num, char *names)
{
this->wmo = wmo;
this->num = num;
// extract group info from f
f.read(&flags,4);
float ff[3];
f.read(ff,12);
v1 = Vec3D(ff[0],ff[1],ff[2]);
f.read(ff,12);
v2 = Vec3D(ff[0],ff[1],ff[2]);
int nameOfs;
f.read(&nameOfs,4);
// TODO: get proper name from group header and/or dbc?
/*
if (nameOfs > 0) {
name = string(names + nameOfs);
} else name = "(no name)";
*/
ddr = 0;
nDoodads = 0;
//lq = 0;
ok = false;
visible = false;
}
struct WMOBatch {
signed char bytes[12];
unsigned int indexStart;
unsigned short indexCount, vertexStart, vertexEnd;
unsigned char flags, texture;
};
void setGLColor(unsigned int col)
{
//glColor4ubv((GLubyte*)(&col));
GLubyte r,g,b,a;
a = (col & 0xFF000000) >> 24;
r = (col & 0x00FF0000) >> 16;
g = (col & 0x0000FF00) >> 8;
b = (col & 0x000000FF);
glColor4ub(r,g,b,1);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -