/* * Model.java * Copyright (C) 2003 * * $Id: Model.java,v 1.7 2005-02-08 17:52:20 cawe Exp $ */ /* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package jake2.render.lwjgl; import jake2.Defines; import jake2.client.VID; import jake2.game.cplane_t; import jake2.game.cvar_t; import jake2.qcommon.*; import jake2.render.*; import jake2.util.Math3D; import jake2.util.Vargs; import java.nio.*; import java.util.Arrays; import java.util.Vector; import org.lwjgl.BufferUtils; /** * Model * * @author cwei */ public abstract class Model extends Surf { // models.c -- model loading and caching model_t loadmodel; int modfilelen; byte[] mod_novis = new byte[Defines.MAX_MAP_LEAFS/8]; static final int MAX_MOD_KNOWN = 512; model_t[] mod_known = new model_t[MAX_MOD_KNOWN]; int mod_numknown; // the inline * models from the current map are kept seperate model_t[] mod_inline = new model_t[MAX_MOD_KNOWN]; abstract void GL_SubdivideSurface(msurface_t surface); // Warp.java /* =============== Mod_PointInLeaf =============== */ mleaf_t Mod_PointInLeaf(float[] p, model_t model) { mnode_t node; float d; cplane_t plane; if (model == null || model.nodes == null) Com.Error (Defines.ERR_DROP, "Mod_PointInLeaf: bad model"); node = model.nodes[0]; // root node while (true) { if (node.contents != -1) return (mleaf_t)node; plane = node.plane; d = Math3D.DotProduct(p, plane.normal) - plane.dist; if (d > 0) node = node.children[0]; else node = node.children[1]; } // never reached } byte[] decompressed = new byte[Defines.MAX_MAP_LEAFS / 8]; byte[] model_visibility = new byte[Defines.MAX_MAP_VISIBILITY]; /* =================== Mod_DecompressVis =================== */ byte[] Mod_DecompressVis(byte[] in, int offset, model_t model) { int c; byte[] out; int outp, inp; int row; row = (model.vis.numclusters+7)>>3; out = decompressed; outp = 0; inp = offset; if (in == null) { // no vis info, so make all visible while (row != 0) { out[outp++] = (byte)0xFF; row--; } return decompressed; } do { if (in[inp] != 0) { out[outp++] = in[inp++]; continue; } c = in[inp + 1] & 0xFF; inp += 2; while (c != 0) { out[outp++] = 0; c--; } } while (outp < row); return decompressed; } /* ============== Mod_ClusterPVS ============== */ byte[] Mod_ClusterPVS(int cluster, model_t model) { if (cluster == -1 || model.vis == null) return mod_novis; //return Mod_DecompressVis( (byte *)model.vis + model.vis.bitofs[cluster][Defines.DVIS_PVS], model); return Mod_DecompressVis(model_visibility, model.vis.bitofs[cluster][Defines.DVIS_PVS], model); } // =============================================================================== /* ================ Mod_Modellist_f ================ */ void Mod_Modellist_f() { int i; model_t mod; int total; total = 0; VID.Printf(Defines.PRINT_ALL,"Loaded models:\n"); for (i=0; i < mod_numknown ; i++) { mod = mod_known[i]; if (mod.name.length() == 0) continue; VID.Printf (Defines.PRINT_ALL, "%8i : %s\n", new Vargs(2).add(mod.extradatasize).add(mod.name)); total += mod.extradatasize; } VID.Printf (Defines.PRINT_ALL, "Total resident: " + total +'\n'); } /* =============== Mod_Init =============== */ void Mod_Init() { // init mod_known for (int i=0; i < MAX_MOD_KNOWN; i++) { mod_known[i] = new model_t(); } Arrays.fill(mod_novis, (byte)0xff); } byte[] fileBuffer; /* ================== Mod_ForName Loads in a model for the given name ================== */ model_t Mod_ForName(String name, boolean crash) { model_t mod = null; int i; if (name == null || name.length() == 0) Com.Error(Defines.ERR_DROP, "Mod_ForName: NULL name"); // // inline models are grabbed only from worldmodel // if (name.charAt(0) == '*') { i = Integer.parseInt(name.substring(1)); if (i < 1 || r_worldmodel == null || i >= r_worldmodel.numsubmodels) Com.Error (Defines.ERR_DROP, "bad inline model number"); return mod_inline[i]; } // // search the currently loaded models // for (i=0; i Math.abs(maxs[i]) ? Math.abs(mins[i]) : Math.abs(maxs[i]); } return Math3D.VectorLength(corner); } /* ================= Mod_LoadSubmodels ================= */ void Mod_LoadSubmodels(lump_t l) { qfiles.dmodel_t in; mmodel_t[] out; int i, j, count; if ((l.filelen % qfiles.dmodel_t.SIZE) != 0) Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name); count = l.filelen / qfiles.dmodel_t.SIZE; // out = Hunk_Alloc ( count*sizeof(*out)); out = new mmodel_t[count]; loadmodel.submodels = out; loadmodel.numsubmodels = count; ByteBuffer bb = ByteBuffer.wrap(mod_base, l.fileofs, l.filelen); bb.order(ByteOrder.LITTLE_ENDIAN); for ( i=0 ; i 0) out[i].next = loadmodel.texinfo[next]; else out[i].next = null; name = "textures/" + in.texture + ".wal"; out[i].image = GL_FindImage(name, it_wall); if (out[i].image == null) { VID.Printf(Defines.PRINT_ALL, "Couldn't load " + name + '\n'); out[i].image = r_notexture; } } // count animation frames for (i=0 ; i= 0) v = loadmodel.vertexes[loadmodel.edges[e].v[0]]; else v = loadmodel.vertexes[loadmodel.edges[-e].v[1]]; for (j=0 ; j<2 ; j++) { val = v.position[0] * tex.vecs[j][0] + v.position[1] * tex.vecs[j][1] + v.position[2] * tex.vecs[j][2] + tex.vecs[j][3]; if (val < mins[j]) mins[j] = val; if (val > maxs[j]) maxs[j] = val; } } for (int i=0 ; i<2 ; i++) { bmins[i] = (int)Math.floor(mins[i]/16); bmaxs[i] = (int)Math.ceil(maxs[i]/16); s.texturemins[i] = (short)(bmins[i] * 16); s.extents[i] = (short)((bmaxs[i] - bmins[i]) * 16); } } /* ================= Mod_LoadFaces ================= */ void Mod_LoadFaces (lump_t l) { qfiles.dface_t in; msurface_t[] out; int i, count, surfnum; int planenum, side; int ti; if ((l.filelen % qfiles.dface_t.SIZE) != 0) Com.Error (Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name); count = l.filelen / qfiles.dface_t.SIZE; // out = Hunk_Alloc ( count*sizeof(*out)); out = new msurface_t[count]; loadmodel.surfaces = out; loadmodel.numsurfaces = count; ByteBuffer bb = ByteBuffer.wrap(mod_base, l.fileofs, l.filelen); bb.order(ByteOrder.LITTLE_ENDIAN); currentmodel = loadmodel; GL_BeginBuildingLightmaps(loadmodel); for ( surfnum=0 ; surfnum= loadmodel.numtexinfo) Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: bad texinfo number"); out[surfnum].texinfo = loadmodel.texinfo[ti]; CalcSurfaceExtents(out[surfnum]); // lighting info for (i=0 ; i= 0) out[i].children[j] = loadmodel.nodes[p]; else out[i].children[j] = loadmodel.leafs[-1 - p]; // mleaf_t extends mnode_t } } Mod_SetParent(loadmodel.nodes[0], null); // sets nodes and leafs } /* ================= Mod_LoadLeafs ================= */ void Mod_LoadLeafs(lump_t l) { qfiles.dleaf_t in; mleaf_t[] out; int i, j, count, p; if ((l.filelen % qfiles.dleaf_t.SIZE) != 0) Com.Error (Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name); count = l.filelen / qfiles.dleaf_t.SIZE; // out = Hunk_Alloc ( count*sizeof(*out)); out = new mleaf_t[count]; loadmodel.leafs = out; loadmodel.numleafs = count; ByteBuffer bb = ByteBuffer.wrap(mod_base, l.fileofs, l.filelen); bb.order(ByteOrder.LITTLE_ENDIAN); for ( i=0 ; i= loadmodel.numsurfaces) Com.Error(Defines.ERR_DROP, "Mod_ParseMarksurfaces: bad surface number"); out[i] = loadmodel.surfaces[j]; } } /* ================= Mod_LoadSurfedges ================= */ void Mod_LoadSurfedges(lump_t l) { int i, count; int[] offsets; if ( (l.filelen % Defines.SIZE_OF_INT) != 0) Com.Error (Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name); count = l.filelen / Defines.SIZE_OF_INT; if (count < 1 || count >= Defines.MAX_MAP_SURFEDGES) Com.Error (Defines.ERR_DROP, "MOD_LoadBmodel: bad surfedges count in " + loadmodel.name + ": " + count); offsets = new int[count]; loadmodel.surfedges = offsets; loadmodel.numsurfedges = count; ByteBuffer bb = ByteBuffer.wrap(mod_base, l.fileofs, l.filelen); bb.order(ByteOrder.LITTLE_ENDIAN); for ( i=0 ; i= loadmodel.numnodes) Com.Error(Defines.ERR_DROP, "Inline model " + i + " has bad firstnode"); Math3D.VectorCopy(bm.maxs, starmod.maxs); Math3D.VectorCopy(bm.mins, starmod.mins); starmod.radius = bm.radius; if (i == 0) loadmodel = starmod.copy(); starmod.numleafs = bm.visleafs; } } /* ============================================================================== ALIAS MODELS ============================================================================== */ /* ================= Mod_LoadAliasModel ================= */ void Mod_LoadAliasModel (model_t mod, ByteBuffer buffer) { int i, j; qfiles.dmdl_t pheader; qfiles.dstvert_t[] poutst; qfiles.dtriangle_t[] pouttri; qfiles.daliasframe_t[] poutframe; int[] poutcmd; pheader = new qfiles.dmdl_t(buffer); if (pheader.version != qfiles.ALIAS_VERSION) Com.Error(Defines.ERR_DROP, "%s has wrong version number (%i should be %i)", new Vargs(3).add(mod.name).add(pheader.version).add(qfiles.ALIAS_VERSION)); if (pheader.skinheight > MAX_LBM_HEIGHT) Com.Error(Defines.ERR_DROP, "model "+ mod.name +" has a skin taller than " + MAX_LBM_HEIGHT); if (pheader.num_xyz <= 0) Com.Error(Defines.ERR_DROP, "model " + mod.name + " has no vertices"); if (pheader.num_xyz > qfiles.MAX_VERTS) Com.Error(Defines.ERR_DROP, "model " + mod.name +" has too many vertices"); if (pheader.num_st <= 0) Com.Error(Defines.ERR_DROP, "model " + mod.name + " has no st vertices"); if (pheader.num_tris <= 0) Com.Error(Defines.ERR_DROP, "model " + mod.name + " has no triangles"); if (pheader.num_frames <= 0) Com.Error(Defines.ERR_DROP, "model " + mod.name + " has no frames"); // // load base s and t vertices (not used in gl version) // poutst = new qfiles.dstvert_t[pheader.num_st]; buffer.position(pheader.ofs_st); for (i=0 ; i -1) { skinNames[i] = skinNames[i].substring(0, n); } mod.skins[i] = GL_FindImage(skinNames[i], it_skin); } // set the model arrays pheader.skinNames = skinNames; // skin names pheader.stVerts = poutst; // textur koordinaten pheader.triAngles = pouttri; // dreiecke pheader.glCmds = poutcmd; // STRIP or FAN pheader.aliasFrames = poutframe; // frames mit vertex array mod.extradata = pheader; mod.mins[0] = -32; mod.mins[1] = -32; mod.mins[2] = -32; mod.maxs[0] = 32; mod.maxs[1] = 32; mod.maxs[2] = 32; precompileGLCmds(pheader); } /* ============================================================================== SPRITE MODELS ============================================================================== */ /* ================= Mod_LoadSpriteModel ================= */ void Mod_LoadSpriteModel(model_t mod, ByteBuffer buffer) { qfiles.dsprite_t sprout = new qfiles.dsprite_t(buffer); if (sprout.version != qfiles.SPRITE_VERSION) Com.Error(Defines.ERR_DROP, "%s has wrong version number (%i should be %i)", new Vargs(3).add(mod.name).add(sprout.version).add(qfiles.SPRITE_VERSION)); if (sprout.numframes > qfiles.MAX_MD2SKINS) Com.Error(Defines.ERR_DROP, "%s has too many frames (%i > %i)", new Vargs(3).add(mod.name).add(sprout.numframes).add(qfiles.MAX_MD2SKINS)); for (int i=0 ; i