void ParticleSystem::init(const MPQFile& f, const ModelParticleEmitterDef &mta, int *globals) { speed.init(mta.EmissionSpeed, f, globals); variation.init(mta.SpeedVariation, f, globals); spread.init(mta.VerticalRange, f, globals); lat.init(mta.HorizontalRange, f, globals); gravity.init(mta.Gravity, f, globals); lifespan.init(mta.Lifespan, f, globals); rate.init(mta.EmissionRate, f, globals); areal.init(mta.EmissionAreaLength, f, globals); areaw.init(mta.EmissionAreaWidth, f, globals); deacceleration.init(mta.Gravity2, f, globals); enabled.init(mta.en, f, globals); Vec3D colors2[3]; memcpy(colors2, f.getBuffer() + mta.p.colors.ofsKeys, sizeof(Vec3D) * 3); for (size_t i = 0; i<3; ++i) { float opacity = *reinterpret_cast<int16_t*>(f.getBuffer() + mta.p.opacity.ofsKeys + i * 2); colors[i] = Vec4D(colors2[i].x / 255.0f, colors2[i].y / 255.0f, colors2[i].z / 255.0f, opacity / 32767.0f); sizes[i] = (*reinterpret_cast<float*>(f.getBuffer() + mta.p.sizes.ofsKeys + i * 4))*mta.p.scales[i]; } mid = 0.5; slowdown = mta.p.slowdown; rotation = mta.p.rotation; pos = fixCoordSystem(mta.pos); _texture = model->_textures[mta.texture]; blend = mta.blend; rows = mta.rows; cols = mta.cols; type = mta.ParticleType; //order = mta.s2; order = mta.ParticleType>0 ? -1 : 0; parent = model->bones + mta.bone; switch (mta.EmitterType) { case 1: emitter = new PlaneParticleEmitter(this); break; case 2: emitter = new SphereParticleEmitter(this); break; } //transform = mta.flags & 1024; billboard = !(mta.flags & 4096); manim = mtime = 0; rem = 0; tofs = misc::frand(); // init tiles for (int i = 0; i<rows*cols; ++i) { TexCoordSet tc; initTile(tc.tc, i); tiles.push_back(tc); } }
void RibbonEmitter::init(const MPQFile &f, ModelRibbonEmitterDef &mta, int *globals) { color.init(mta.color, f, globals); opacity.init(mta.opacity, f, globals); above.init(mta.above, f, globals); below.init(mta.below, f, globals); parent = model->bones + mta.bone; uint32_t *texlist = reinterpret_cast<uint32_t*>(f.getBuffer() + mta.ofsTextures); // just use the first texture for now; most models I've checked only had one _texture = model->_textures[texlist[0]]; tpos = pos = fixCoordSystem(mta.pos); //! \todo figure out actual correct way to calculate length // in BFD, res is 60 and len is 0.6, the trails are very short (too long here) // in CoT, res and len are like 10 but the trails are supposed to be much longer (too short here) numsegs = (int)mta.res; seglen = mta.length; length = mta.res * seglen; // create first segment RibbonSegment rs; rs.pos = tpos; rs.len = 0; segs.push_back(rs); }
void Model::initStatic(MPQFile &f) { origVertices = (ModelVertex*)(f.getBuffer() + header.ofsVertices); initCommon(f); dlist = glGenLists(1); glNewList(dlist, GL_COMPILE); drawModel(); glEndList(); // clean up vertices, indices etc delete[] vertices; delete[] normals; delete[] indices; if (colors) delete[] colors; if (transparency) delete[] transparency; }
bool Model::isAnimated(MPQFile &f) { // see if we have any animated bones ModelBoneDef *bo = (ModelBoneDef*)(f.getBuffer() + header.ofsBones); animGeometry = false; animBones = false; ind = false; ModelVertex *verts = (ModelVertex*)(f.getBuffer() + header.ofsVertices); for (size_t i=0; i<header.nVertices && !animGeometry; i++) { for (size_t b=0; b<4; b++) { if (verts[i].weights[b]>0) { ModelBoneDef &bb = bo[verts[i].bones[b]]; if (bb.translation.type || bb.rotation.type || bb.scaling.type || (bb.flags&8)) { if (bb.flags&8) { // if we have billboarding, the model will need per-instance animation ind = true; } animGeometry = true; break; } } } } if (animGeometry) animBones = true; else { for (size_t i=0; i<header.nBones; i++) { ModelBoneDef &bb = bo[i]; if (bb.translation.type || bb.rotation.type || bb.scaling.type) { animBones = true; break; } } } animTextures = header.nTexAnims > 0; bool animMisc = header.nCameras>0 || // why waste time, pretty much all models with cameras need animation header.nLights>0 || // same here header.nParticleEmitters>0 || header.nRibbonEmitters>0; if (animMisc) animBones = true; // animated colors if (header.nColors) { ModelColorDef *cols = (ModelColorDef*)(f.getBuffer() + header.ofsColors); for (size_t i=0; i<header.nColors; i++) { if (cols[i].color.type!=0 || cols[i].opacity.type!=0) { animMisc = true; break; } } } // animated opacity if (header.nTransparency && !animMisc) { ModelTransDef *trs = (ModelTransDef*)(f.getBuffer() + header.ofsTransparency); for (size_t i=0; i<header.nTransparency; i++) { if (trs[i].trans.type!=0) { animMisc = true; break; } } } // guess not... return animGeometry || animTextures || animMisc; }
void Model::initAnimated(MPQFile &f) { origVertices = new ModelVertex[header.nVertices]; memcpy(origVertices, f.getBuffer() + header.ofsVertices, header.nVertices * sizeof(ModelVertex)); glGenBuffersARB(1,&vbuf); glGenBuffersARB(1,&tbuf); const size_t size = header.nVertices * sizeof(float); vbufsize = 3 * size; initCommon(f); if (header.nAnimations > 0) { anims = new ModelAnimation[header.nAnimations]; memcpy(anims, f.getBuffer() + header.ofsAnimations, header.nAnimations * sizeof(ModelAnimation)); animfiles = new MPQFile[header.nAnimations]; char tempname[256]; for(size_t i=0; i<header.nAnimations; i++) { sprintf(tempname, "%s%04d-%02d.anim", fullname.c_str(), anims[i].animID, anims[i].subAnimID); if (MPQFile::getSize(tempname) > 0) { animfiles[i].openFile(tempname); } } } if (animBones) { // init bones... bones = new Bone[header.nBones]; ModelBoneDef *mb = (ModelBoneDef*)(f.getBuffer() + header.ofsBones); for (size_t i=0; i<header.nBones; i++) { bones[i].init(f, mb[i], globalSequences, animfiles); } } if (!animGeometry) { glBindBufferARB(GL_ARRAY_BUFFER_ARB, vbuf); glBufferDataARB(GL_ARRAY_BUFFER_ARB, vbufsize, vertices, GL_STATIC_DRAW_ARB); glGenBuffersARB(1,&nbuf); glBindBufferARB(GL_ARRAY_BUFFER_ARB, nbuf); glBufferDataARB(GL_ARRAY_BUFFER_ARB, vbufsize, normals, GL_STATIC_DRAW_ARB); delete[] vertices; delete[] normals; } Vec2D *texcoords = new Vec2D[header.nVertices]; for (size_t i=0; i<header.nVertices; i++) texcoords[i] = origVertices[i].texcoords; glBindBufferARB(GL_ARRAY_BUFFER_ARB, tbuf); glBufferDataARB(GL_ARRAY_BUFFER_ARB, 2*size, texcoords, GL_STATIC_DRAW_ARB); delete[] texcoords; if (animTextures) { texAnims = new TextureAnim[header.nTexAnims]; ModelTexAnimDef *ta = (ModelTexAnimDef*)(f.getBuffer() + header.ofsTexAnims); for (size_t i=0; i<header.nTexAnims; i++) { texAnims[i].init(f, ta[i], globalSequences); } } // particle systems if (header.nParticleEmitters) { ModelParticleEmitterDef *pdefs = (ModelParticleEmitterDef *)(f.getBuffer() + header.ofsParticleEmitters); particleSystems = new ParticleSystem[header.nParticleEmitters]; for (size_t i=0; i<header.nParticleEmitters; i++) { particleSystems[i].model = this; particleSystems[i].init(f, pdefs[i], globalSequences); } } // ribbons if (header.nRibbonEmitters) { ModelRibbonEmitterDef *rdefs = (ModelRibbonEmitterDef *)(f.getBuffer() + header.ofsRibbonEmitters); ribbons = new RibbonEmitter[header.nRibbonEmitters]; for (size_t i=0; i<header.nRibbonEmitters; i++) { ribbons[i].model = this; ribbons[i].init(f, rdefs[i], globalSequences); } } // just use the first camera, meh if (header.nCameras>0) { ModelCameraDef *camDefs = (ModelCameraDef*)(f.getBuffer() + header.ofsCameras); cam.init(f, camDefs[0], globalSequences); } // init lights if (header.nLights) { lights = new ModelLight[header.nLights]; ModelLightDef *lDefs = (ModelLightDef*)(f.getBuffer() + header.ofsLights); for (size_t i=0; i<header.nLights; i++) lights[i].init(f, lDefs[i], globalSequences); } animcalc = false; }
void Model::initCommon(MPQFile &f) { // assume: origVertices already set if (!animGeometry) { vertices = new Vec3D[header.nVertices]; normals = new Vec3D[header.nVertices]; } //Vec3D vmin = Vec3D( 9999999.0f, 9999999.0f, 9999999.0f); //Vec3D vmax = Vec3D(-9999999.0f,-9999999.0f,-9999999.0f); // vertices, normals for (size_t i=0; i<header.nVertices; i++) { origVertices[i].pos = fixCoordSystem(origVertices[i].pos); origVertices[i].normal = fixCoordSystem(origVertices[i].normal); if (!animGeometry) { vertices[i] = origVertices[i].pos; normals[i] = origVertices[i].normal.normalize(); } float len = origVertices[i].pos.lengthSquared(); if (len > rad){ rad = len; } /* if (origVertices[i].pos.x < vmin.x) vmin.x = origVertices[i].pos.x; if (origVertices[i].pos.y < vmin.y) vmin.y = origVertices[i].pos.y; if (origVertices[i].pos.z < vmin.z) vmin.z = origVertices[i].pos.z; if (origVertices[i].pos.x > vmax.x) vmax.x = origVertices[i].pos.x; if (origVertices[i].pos.y > vmax.y) vmax.y = origVertices[i].pos.y; if (origVertices[i].pos.z > vmax.z) vmax.z = origVertices[i].pos.z; */ } rad = sqrtf(rad); //rad = std::max(vmin.length(),vmax.length()); // textures ModelTextureDef *texdef = (ModelTextureDef*)(f.getBuffer() + header.ofsTextures); if (header.nTextures) { textures = new TextureID[header.nTextures]; char texname[256]; for (size_t i=0; i<header.nTextures; i++) { // Error check if (i > TEXTURE_MAX-1) { gLog("Error: Model Texture %d over %d", header.nTextures, TEXTURE_MAX); break; } if (texdef[i].type == 0) { strncpy(texname, (const char*)(f.getBuffer() + texdef[i].nameOfs), texdef[i].nameLen); texname[texdef[i].nameLen] = 0; textures[i] = video.textures.add(texname); } else { // special texture - only on characters and such... textures[i] = 0; specialTextures[i] = texdef[i].type; if (texdef[i].type < TEXTURE_MAX) useReplaceTextures[texdef[i].type] = true; if (texdef[i].type == 3) { // a fix for weapons with type-3 textures. replaceTextures[texdef[i].type] = video.textures.add("Item\\ObjectComponents\\Weapon\\ArmorReflect4.BLP"); } } } } // init colors if (header.nColors) { colors = new ModelColor[header.nColors]; ModelColorDef *colorDefs = (ModelColorDef*)(f.getBuffer() + header.ofsColors); for (size_t i=0; i<header.nColors; i++) colors[i].init(f, colorDefs[i], globalSequences); } // init transparency int16 *transLookup = (int16*)(f.getBuffer() + header.ofsTransparencyLookup); if (header.nTransparency) { transparency = new ModelTransparency[header.nTransparency]; ModelTransDef *trDefs = (ModelTransDef*)(f.getBuffer() + header.ofsTransparency); for (size_t i=0; i<header.nTransparency; i++) transparency[i].init(f, trDefs[i], globalSequences); } // just use the first LOD/view if (header.nViews > 0) { // indices - allocate space, too std::string lodname = fullname.substr(0, fullname.length()-3); fullname = lodname; lodname.append("00.skin"); MPQFile g(lodname.c_str()); if (g.isEof()) { gLog("Error: loading lod [%s]\n", lodname.c_str()); g.close(); return; } ModelView *view = (ModelView*)(g.getBuffer()); uint16 *indexLookup = (uint16*)(g.getBuffer() + view->ofsIndex); uint16 *triangles = (uint16*)(g.getBuffer() + view->ofsTris); nIndices = view->nTris; indices = new uint16[nIndices]; for (size_t i = 0; i<nIndices; i++) { indices[i] = indexLookup[triangles[i]]; } // render ops ModelGeoset *ops = (ModelGeoset*)(g.getBuffer() + view->ofsSub); ModelTexUnit *tex = (ModelTexUnit*)(g.getBuffer() + view->ofsTex); ModelRenderFlags *renderFlags = (ModelRenderFlags*)(f.getBuffer() + header.ofsTexFlags); uint16 *texlookup = (uint16*)(f.getBuffer() + header.ofsTexLookup); uint16 *texanimlookup = (uint16*)(f.getBuffer() + header.ofsTexAnimLookup); int16 *texunitlookup = (int16*)(f.getBuffer() + header.ofsTexUnitLookup); showGeosets = new bool[view->nSub]; for (size_t i=0; i<view->nSub; i++) { showGeosets[i] = true; } for (size_t j = 0; j<view->nTex; j++) { ModelRenderPass pass; pass.usetex2 = false; pass.useEnvMap = false; pass.cull = false; pass.trans = false; pass.unlit = false; pass.noZWrite = false; pass.billboard = false; size_t geoset = tex[j].op; pass.geoset = (int)geoset; pass.indexStart = ops[geoset].istart; pass.indexCount = ops[geoset].icount; pass.vertexStart = ops[geoset].vstart; pass.vertexEnd = pass.vertexStart + ops[geoset].vcount; pass.order = tex[j].shading; //TextureID texid = textures[texlookup[tex[j].textureid]]; //pass.texture = texid; pass.tex = texlookup[tex[j].textureid]; // TODO: figure out these flags properly -_- ModelRenderFlags &rf = renderFlags[tex[j].flagsIndex]; pass.blendmode = rf.blend; pass.color = tex[j].colorIndex; pass.opacity = transLookup[tex[j].transid]; pass.unlit = (rf.flags & RENDERFLAGS_UNLIT)!=0; pass.cull = (rf.flags & RENDERFLAGS_TWOSIDED)==0 && rf.blend==0; pass.billboard = (rf.flags & RENDERFLAGS_BILLBOARD) != 0; pass.useEnvMap = (texunitlookup[tex[j].texunit] == -1) && pass.billboard && rf.blend>2; pass.noZWrite = (rf.flags & RENDERFLAGS_ZBUFFERED) != 0; // ToDo: Work out the correct way to get the true/false of transparency pass.trans = (pass.blendmode>0) && (pass.opacity>0); // Transparency - not the correct way to get transparency pass.p = ops[geoset].BoundingBox[0].x; // Texture flags pass.swrap = (texdef[pass.tex].flags & TEXTURE_WRAPX) != 0; // Texture wrap X pass.twrap = (texdef[pass.tex].flags & TEXTURE_WRAPY) != 0; // Texture wrap Y if (animTextures) { if (tex[j].flags & TEXTUREUNIT_STATIC) { pass.texanim = -1; // no texture animation } else { pass.texanim = texanimlookup[tex[j].texanimid]; } } else { pass.texanim = -1; // no texture animation } passes.push_back(pass); } g.close(); // transparent parts come later std::sort(passes.begin(), passes.end()); } // zomg done }
void Model::initCommon(MPQFile &f) { // assume: origVertices already set if (!animGeometry) { vertices = new Vec3D[header.nVertices]; normals = new Vec3D[header.nVertices]; } //Vec3D vmin = Vec3D( 9999999.0f, 9999999.0f, 9999999.0f); //Vec3D vmax = Vec3D(-9999999.0f,-9999999.0f,-9999999.0f); // vertices, normals for (size_t i=0; i<header.nVertices; i++) { origVertices[i].pos = fixCoordSystem(origVertices[i].pos); origVertices[i].normal = fixCoordSystem(origVertices[i].normal); if (!animGeometry) { vertices[i] = origVertices[i].pos; normals[i] = origVertices[i].normal.normalize(); } float len = origVertices[i].pos.lengthSquared(); if (len > rad){ rad = len; } /* if (origVertices[i].pos.x < vmin.x) vmin.x = origVertices[i].pos.x; if (origVertices[i].pos.y < vmin.y) vmin.y = origVertices[i].pos.y; if (origVertices[i].pos.z < vmin.z) vmin.z = origVertices[i].pos.z; if (origVertices[i].pos.x > vmax.x) vmax.x = origVertices[i].pos.x; if (origVertices[i].pos.y > vmax.y) vmax.y = origVertices[i].pos.y; if (origVertices[i].pos.z > vmax.z) vmax.z = origVertices[i].pos.z; */ } rad = sqrtf(rad); //rad = std::max(vmin.length(),vmax.length()); // textures ModelTextureDef *texdef = (ModelTextureDef*)(f.getBuffer() + header.ofsTextures); if (header.nTextures) { textures = new TextureID[header.nTextures]; for (size_t i=0; i<header.nTextures; i++) { char texname[256]; if (texdef[i].type == 0) { strncpy(texname, f.getBuffer() + texdef[i].nameOfs, texdef[i].nameLen); texname[texdef[i].nameLen] = 0; std::string path(texname); fixname(path); textures[i] = video.textures.add(texname); } else { // special texture - only on characters and such... textures[i] = 0; } } } // init colors if (header.nColors) { colors = new ModelColor[header.nColors]; ModelColorDef *colorDefs = (ModelColorDef*)(f.getBuffer() + header.ofsColors); for (size_t i=0; i<header.nColors; i++) colors[i].init(f, colorDefs[i], globalSequences); } // init transparency int16 *transLookup = (int16*)(f.getBuffer() + header.ofsTransparencyLookup); if (header.nTransparency) { transparency = new ModelTransparency[header.nTransparency]; ModelTransDef *trDefs = (ModelTransDef*)(f.getBuffer() + header.ofsTransparency); for (size_t i=0; i<header.nTransparency; i++) transparency[i].init(f, trDefs[i], globalSequences); } // just use the first LOD/view // indices - allocate space, too ModelView *view = (ModelView*)(f.getBuffer() + header.ofsViews); uint16 *indexLookup = (uint16*)(f.getBuffer() + view->ofsIndex); uint16 *triangles = (uint16*)(f.getBuffer() + view->ofsTris); nIndices = view->nTris; indices = new uint16[nIndices]; for (size_t i = 0; i<nIndices; i++) { indices[i] = indexLookup[triangles[i]]; } // render ops ModelGeoset *ops = (ModelGeoset*)(f.getBuffer() + view->ofsSub); ModelTexUnit *tex = (ModelTexUnit*)(f.getBuffer() + view->ofsTex); ModelRenderFlags *renderFlags = (ModelRenderFlags*)(f.getBuffer() + header.ofsTexFlags); uint16 *texlookup = (uint16*)(f.getBuffer() + header.ofsTexLookup); uint16 *texanimlookup = (uint16*)(f.getBuffer() + header.ofsTexAnimLookup); int16 *texunitlookup = (int16*)(f.getBuffer() + header.ofsTexUnitLookup); /* for (size_t i = 0; i<view->nSub; i++) { ModelRenderPass pass; pass.usetex2 = false; pass.indexStart = ops[i].istart; pass.indexCount = ops[i].icount; // textures for (size_t j = 0; j<view->nTex; j++) { if (tex[j].op==i) { TextureID texid = textures[texlookup[tex[j].textureid]]; if (tex[j].texunit==0) { pass.texture = texid; // TODO: figure out these flags properly -_- ModelRenderFlags &rf = renderFlags[tex[j].flagsIndex]; //pass.useenvmap = (rf.flags2 & 6)==6; //pass.useenvmap = rf.blend == 6; // ??? pass.useenvmap = texunitlookup[tex[j].texunit] == -1; pass.blendmode = rf.blend; pass.color = tex[j].colorIndex; pass.opacity = transLookup[tex[j].transid]; pass.cull = (rf.flags & 4)==0 && rf.blend==0; pass.unlit = (rf.flags & 3)!=0; pass.nozwrite = pass.blendmode >= 2; //(rf.flags & 16)!=0; pass.trans = pass.blendmode != 0; pass.p = ops[i].v.x; if (animTextures) { if (tex[j].flags & 16) { pass.texanim = -1; // no texture animation } else { pass.texanim = texanimlookup[tex[j].texanimid]; } } else { pass.texanim = -1; // no texture animation } } else if (tex[j].texunit==1) { pass.texture2 = texid; //pass.usetex2 = true; } } } passes.push_back(pass); } */ for (size_t j = 0; j<view->nTex; j++) { ModelRenderPass pass; pass.usetex2 = false; pass.texture2 = 0; size_t geoset = tex[j].op; pass.indexStart = ops[geoset].istart; pass.indexCount = ops[geoset].icount; pass.vertexStart = ops[geoset].vstart; pass.vertexEnd = pass.vertexStart + ops[geoset].vcount; pass.order = tex[j].order; TextureID texid = textures[texlookup[tex[j].textureid]]; pass.texture = texid; // TODO: figure out these flags properly -_- ModelRenderFlags &rf = renderFlags[tex[j].flagsIndex]; pass.useenvmap = texunitlookup[tex[j].texunit] == -1; pass.blendmode = rf.blend; pass.color = tex[j].colorIndex; pass.opacity = transLookup[tex[j].transid]; pass.cull = (rf.flags & 4)==0 && rf.blend==0; pass.unlit = (rf.flags & 3)!=0; pass.nozwrite = pass.blendmode >= 2; //(rf.flags & 16)!=0; pass.trans = pass.blendmode != 0; pass.p = ops[geoset].v.x; if (animTextures) { if (tex[j].flags & 16) { pass.texanim = -1; // no texture animation } else { pass.texanim = texanimlookup[tex[j].texanimid]; } } else { pass.texanim = -1; // no texture animation } passes.push_back(pass); } // transparent parts come later std::sort(passes.begin(), passes.end()); // zomg done }