/*
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.
*/
/* Modifications
Copyright 2003-2004 Bytonic Software
Copyright 2010 Google Inc.
*/
package com.googlecode.gwtquake.shared.render;
import com.google.gwt.user.client.Command;
import com.googlecode.gwtquake.shared.client.Window;
import com.googlecode.gwtquake.shared.common.AsyncCallback;
import com.googlecode.gwtquake.shared.common.Com;
import com.googlecode.gwtquake.shared.common.Compatibility;
import com.googlecode.gwtquake.shared.common.ConsoleVariables;
import com.googlecode.gwtquake.shared.common.Constants;
import com.googlecode.gwtquake.shared.common.Lump;
import com.googlecode.gwtquake.shared.common.QuakeFiles;
import com.googlecode.gwtquake.shared.common.QuakeImage;
import com.googlecode.gwtquake.shared.common.ResourceLoader;
import com.googlecode.gwtquake.shared.common.TextureInfo;
import com.googlecode.gwtquake.shared.game.ConsoleVariable;
import com.googlecode.gwtquake.shared.game.Plane;
import com.googlecode.gwtquake.shared.util.Lib;
import com.googlecode.gwtquake.shared.util.Math3D;
import com.googlecode.gwtquake.shared.util.Vargs;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Vector;
//import com.google.gwt.user.client.Timer;
/**
* Model
*
* @author cwei
*/
public class Models {
// models.c -- model loading and caching
static Model loadmodel;
static int modfilelen;
static byte[] mod_novis = new byte[Constants.MAX_MAP_LEAFS/8];
static final int MAX_MOD_KNOWN = 512;
static Model world_model;
// the inline * models from the current map are kept seperate
static Model[] mod_inline = new Model[MAX_MOD_KNOWN];
// abstract void GL_SubdivideSurface(ModelSurface surface); // Warp.java
/*
===============
Mod_PointInLeaf
===============
*/
static Leaf Mod_PointInLeaf(float[] p, Model model)
{
Node node;
float d;
Plane plane;
if (model == null || model.nodes == null)
Com.Error (Constants.ERR_DROP, "Mod_PointInLeaf: bad model");
node = model.nodes[0]; // root node
while (true)
{
if (node.contents != -1)
return (Leaf)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
}
static byte[] decompressed = new byte[Constants.MAX_MAP_LEAFS / 8];
static byte[] model_visibility = new byte[Constants.MAX_MAP_VISIBILITY];
/*
===================
Mod_DecompressVis
===================
*/
static byte[] Mod_DecompressVis(byte[] in, int offset, Model 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
==============
*/
static byte[] Mod_ClusterPVS(int cluster, Model 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][Constants.DVIS_PVS], model);
}
// ===============================================================================
private static class ModelRequest {
public boolean loaded;
public ArrayList<AsyncCallback<Model>> callbacks = new ArrayList<AsyncCallback<Model>>();
public Model model = new Model();
}
private static HashMap<String, ModelRequest> modelReqs = new HashMap<String, ModelRequest>();
public static void clearModelCache() {
modelReqs.clear();
}
/*
================
Mod_Modellist_f
================
*/
static void Mod_Modellist_f()
{
int i;
Model mod;
int total;
total = 0;
Window.Printf(Constants.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;
// }
for (String name : modelReqs.keySet()) {
ModelRequest req = modelReqs.get(name);
Window.Printf (Constants.PRINT_ALL, "%8i : %s\n", new Vargs(2).add(req.model.extradatasize).add(req.model.name));
total += req.model.extradatasize;
}
Window.Printf (Constants.PRINT_ALL, "Total resident: " + total +'\n');
}
/*
===============
Mod_Init
===============
*/
static 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);
}
static ByteBuffer fileBuffer;
/*
==================
Mod_ForName
Loads in a model for the given name
==================
*/
static void Mod_ForName(final String name, AsyncCallback<Model> callback)
{
int i;
if (name == null || name.length() == 0)
Com.Error(Constants.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 || GlState.r_worldmodel == null || i >= GlState.r_worldmodel.numsubmodels) {
Compatibility.printStackTrace(new Exception("inline model number issue; world model: " + world_model));
Com.Error (Constants.ERR_DROP, "bad inline model number " + i + "; worldmodel: " + GlState.r_worldmodel + " numsubmodels: " + GlState.r_worldmodel.numsubmodels);
}
callback.onSuccess(mod_inline[i]);
return;
}
final ModelRequest req = modelReqs.get(name);
if (req != null) {
if (req.loaded) {
callback.onSuccess(req.model);
return;
}
req.callbacks.add(callback);
return;
}
final ModelRequest modelReq = new ModelRequest();
modelReq.model = new Model();
modelReq.callbacks.add(callback);
modelReqs.put(name, modelReq);
//
// load the file
//
final Model model = modelReq.model;
ResourceLoader.loadResourceAsync(name, new ResourceLoader.Callback() {
public void onSuccess(final ByteBuffer bb) {
// if (waitingForImages > 0) {
// gl.log ("still waiting for textures: "+waitingForImages);
// new Timer(){
//
// @Override
// public void run() {
// onSuccess(response);
// }}.schedule(5000);
// return;
// }
model.name = name;
modelReq.loaded = true;
fileBuffer = bb;
modfilelen = fileBuffer.limit();
loadmodel = model;
//
// fill it in
//
bb.order(ByteOrder.LITTLE_ENDIAN);
// call the apropriate loader
bb.mark();
int ident = bb.getInt();
bb.reset();
switch (ident)
{
case QuakeFiles.IDALIASHEADER:
Mod_LoadAliasModel(model, bb);
break;
case QuakeFiles.IDSPRITEHEADER:
Mod_LoadSpriteModel(model, bb);
break;
case QuakeFiles.IDBSPHEADER:
Mod_LoadBrushModel(model, bb);
break;
default:
Com.Error(Constants.ERR_DROP,"Mod_NumForName: unknown fileid for " + model);
break;
}
Models.fileBuffer = null; // free it for garbage collection
for (AsyncCallback<Model> callback : modelReq.callbacks) {
callback.onSuccess(model);
}
modelReq.callbacks.clear();
}
});
}
/*
===============================================================================
BRUSHMODEL LOADING
===============================================================================
*/
static ByteBuffer mod_base;
/*
=================
Mod_LoadLighting
=================
*/
static void Mod_LoadLighting(Lump l)
{
if (l.filelen == 0)
{
loadmodel.lightdata = null;
return;
}
// memcpy (loadmodel.lightdata, mod_base + l.fileofs, l.filelen);
loadmodel.lightdata = new byte[l.filelen];
// System.arraycopy(mod_base, l.fileofs, loadmodel.lightdata, 0, l.filelen);
mod_base.position(l.fileofs);
mod_base.get(loadmodel.lightdata, 0, l.filelen);
}
/*
=================
Mod_LoadVisibility
=================
*/
static void Mod_LoadVisibility(Lump l)
{
//int i;
if (l.filelen == 0)
{
loadmodel.vis = null;
return;
}
// System.arraycopy(mod_base, l.fileofs, model_visibility, 0, l.filelen);
mod_base.position(l.fileofs);
mod_base.get(model_visibility, 0, l.filelen);
ByteBuffer bb = ByteBuffer.wrap(model_visibility, 0, l.filelen);
loadmodel.vis = new QuakeFiles.dvis_t(bb.order(ByteOrder.LITTLE_ENDIAN));
/* done:
memcpy (loadmodel.vis, mod_base + l.fileofs, l.filelen);
loadmodel.vis.numclusters = LittleLong (loadmodel.vis.numclusters);
for (i=0 ; i<loadmodel.vis.numclusters ; i++)
{
loadmodel.vis.bitofs[i][0] = LittleLong (loadmodel.vis.bitofs[i][0]);
loadmodel.vis.bitofs[i][1] = LittleLong (loadmodel.vis.bitofs[i][1]);
}
*/
}
/*
=================
Mod_LoadVertexes
=================
*/
static void Mod_LoadVertexes(Lump l)
{
Vertex[] vertexes;
int i, count;
if ( (l.filelen % Vertex.DISK_SIZE) != 0)
Com.Error(Constants.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name);
count = l.filelen / Vertex.DISK_SIZE;
vertexes = new Vertex[count];
loadmodel.vertexes = vertexes;
loadmodel.numvertexes = count;
ByteBuffer bb = mod_base;
mod_base.position(l.fileofs);//ByteBuffer.wrap(mod_base, l.fileofs, l.filelen);
bb.order(ByteOrder.LITTLE_ENDIAN);
for ( i=0 ; i<count ; i++)
{
vertexes[i] = new Vertex(bb);
}
}
/*
=================
RadiusFromBounds
=================
*/
static float RadiusFromBounds(float[] mins, float[] maxs)
{
float[] corner = {0, 0, 0};
for (int i=0 ; i<3 ; i++)
{
corner[i] = Math.abs(mins[i]) > Math.abs(maxs[i]) ? Math.abs(mins[i]) : Math.abs(maxs[i]);
}
return Math3D.VectorLength(corner);
}
/*
=================
Mod_LoadSubmodels
=================
*/
static void Mod_LoadSubmodels(Lump l) {
if ((l.filelen % QuakeFiles.dmodel_t.SIZE) != 0)
Com.Error(Constants.ERR_DROP, "MOD_LoadBmodel: funny lump size in "
+ loadmodel.name);
int i, j;
int count = l.filelen / QuakeFiles.dmodel_t.SIZE;
// out = Hunk_Alloc ( count*sizeof(*out));
SubModel out;
SubModel[] outs = new SubModel[count];
for (i = 0; i < count; i++) {
outs[i] = new SubModel();
}
loadmodel.submodels = outs;
loadmodel.numsubmodels = count;
ByteBuffer bb = mod_base;mod_base.position(l.fileofs);//ByteBuffer.wrap(mod_base, l.fileofs, l.filelen);
bb.order(ByteOrder.LITTLE_ENDIAN);
QuakeFiles.dmodel_t in;
for (i = 0; i < count; i++) {
in = new QuakeFiles.dmodel_t(bb);
out = outs[i];
for (j = 0; j < 3; j++) { // spread the mins / maxs by a
// pixel
out.mins[j] = in.mins[j] - 1;
out.maxs[j] = in.maxs[j] + 1;
out.origin[j] = in.origin[j];
}
out.radius = RadiusFromBounds(out.mins, out.maxs);
out.headnode = in.headnode;
out.firstface = in.firstface;
out.numfaces = in.numfaces;
}
}
/*
=================
Mod_LoadEdges
=================
*/
static void Mod_LoadEdges (Lump l)
{
Edge[] edges;
int i, count;
if ( (l.filelen % Edge.DISK_SIZE) != 0)
Com.Error(Constants.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name);
count = l.filelen / Edge.DISK_SIZE;
// out = Hunk_Alloc ( (count + 1) * sizeof(*out));
edges = new Edge[count + 1];
loadmodel.edges = edges;
loadmodel.numedges = count;
ByteBuffer bb = mod_base;mod_base.position(l.fileofs);//ByteBuffer.wrap(mod_base, l.fileofs, l.filelen);
bb.order(ByteOrder.LITTLE_ENDIAN);
for ( i=0 ; i<count ; i++)
{
edges[i] = new Edge(bb);
}
}
/*
=================
Mod_LoadTexinfo
=================
*/
static void Mod_LoadTexinfo(Lump l)
{
TextureInfo in;
Texture[] out;
Texture step;
int i;
int next;
String name;
if ((l.filelen % TextureInfo.SIZE) != 0)
Com.Error (Constants.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name);
int count = l.filelen / TextureInfo.SIZE;
// out = Hunk_Alloc ( count*sizeof(*out));
out = new Texture[count];
for ( i=0 ; i<count ; i++) {
out[i] = new Texture();
}
loadmodel.texinfo = out;
loadmodel.numtexinfo = count;
ByteBuffer bb = mod_base;mod_base.position(l.fileofs);//ByteBuffer.wrap(mod_base, l.fileofs, l.filelen);
bb.order(ByteOrder.LITTLE_ENDIAN);
for ( i=0 ; i<count ; i++) {
in = new TextureInfo(bb);
out[i].vecs = in.vecs;
out[i].flags = in.flags;
next = in.nexttexinfo;
if (next > 0)
out[i].next = loadmodel.texinfo[next];
else
out[i].next = null;
name = "textures/" + in.texture + ".wal";
out[i].image = Images.findTexture(name, QuakeImage.it_wall);
if (out[i].image == null) {
Window.Printf(Constants.PRINT_ALL, "Couldn't load " + name + '\n');
out[i].image = GlState.r_notexture;
}
}
// count animation frames
for (i=0 ; i<count ; i++) {
out[i].numframes = 1;
for (step = out[i].next ; (step != null) && (step != out[i]) ; step=step.next)
out[i].numframes++;
}
}
/*
=================
Mod_LoadFaces
=================
*/
static void Mod_LoadFaces(Lump l) {
int i, surfnum;
int planenum, side;
int ti;
if ((l.filelen % QuakeFiles.dface_t.SIZE) != 0)
Com.Error(Constants.ERR_DROP, "MOD_LoadBmodel: funny lump size in "
+ loadmodel.name);
int count = l.filelen / QuakeFiles.dface_t.SIZE;
// out = Hunk_Alloc ( count*sizeof(*out));
Surface[] outs = new Surface[count];
for (i = 0; i < count; i++) {
outs[i] = new Surface();
}
loadmodel.surfaces = outs;
loadmodel.numsurfaces = count;
ByteBuffer bb = mod_base;mod_base.position(l.fileofs);//ByteBuffer.wrap(mod_base, l.fileofs, l.filelen);
bb.order(ByteOrder.LITTLE_ENDIAN);
GlState.currentmodel = loadmodel;
loadmodel.GL_BeginBuildingLightmaps();
QuakeFiles.dface_t in;
Surface out;
for (surfnum = 0; surfnum < count; surfnum++) {
in = new QuakeFiles.dface_t(bb);
out = outs[surfnum];
out.firstedge = in.firstedge;
out.numedges = in.numedges;
out.flags = 0;
out.polys = null;
planenum = in.planenum;
side = in.side;
if (side != 0)
out.flags |= Constants.SURF_PLANEBACK;
out.plane = loadmodel.planes[planenum];
ti = in.texinfo;
if (ti < 0 || ti >= loadmodel.numtexinfo)
Com.Error(Constants.ERR_DROP,
"MOD_LoadBmodel: bad texinfo number");
out.texinfo = loadmodel.texinfo[ti];
out.CalcSurfaceExtents();
// lighting info
for (i = 0; i < Constants.MAXLIGHTMAPS; i++)
out.styles[i] = in.styles[i];
i = in.lightofs;
if (i == -1)
out.samples = null;
else {
ByteBuffer pointer = ByteBuffer.wrap(loadmodel.lightdata);
pointer.position(i);
pointer = pointer.slice();
pointer.mark();
out.samples = pointer; // subarray
}
// set the drawing flags
if ((out.texinfo.flags & Constants.SURF_WARP) != 0) {
out.flags |= Constants.SURF_DRAWTURB;
for (i = 0; i < 2; i++) {
out.extents[i] = 16384;
out.texturemins[i] = -8192;
}
out.GL_SubdivideSurface(); // cut up polygon for warps
}
// create lightmaps and polygons
if ((out.texinfo.flags & (Constants.SURF_SKY | Constants.SURF_TRANS33
| Constants.SURF_TRANS66 | Constants.SURF_WARP)) == 0)
Surface.GL_CreateSurfaceLightmap(out);
if ((out.texinfo.flags & Constants.SURF_WARP) == 0)
Surface.GL_BuildPolygonFromSurface(out);
}
Surfaces.GL_EndBuildingLightmaps();
}
/*
=================
Mod_LoadNodes
=================
*/
static void Mod_LoadNodes(Lump l)
{
int i, j, count, p;
QuakeFiles.dnode_t in;
Node[] out;
if ((l.filelen % QuakeFiles.dnode_t.SIZE) != 0)
Com.Error(Constants.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name);
count = l.filelen / QuakeFiles.dnode_t.SIZE;
// out = Hunk_Alloc ( count*sizeof(*out));
out = new Node[count];
loadmodel.nodes = out;
loadmodel.numnodes = count;
ByteBuffer bb = mod_base;mod_base.position(l.fileofs);//ByteBuffer.wrap(mod_base, l.fileofs, l.filelen);
bb.order(ByteOrder.LITTLE_ENDIAN);
// initialize the tree array
for ( i=0 ; i<count ; i++) out[i] = new Node(); // do first before linking
// fill and link the nodes
for ( i=0 ; i<count ; i++)
{
in = new QuakeFiles.dnode_t(bb);
for (j=0 ; j<3 ; j++)
{
out[i].mins[j] = in.mins[j];
out[i].maxs[j] = in.maxs[j];
}
p = in.planenum;
out[i].plane = loadmodel.planes[p];
out[i].firstsurface = in.firstface;
out[i].numsurfaces = in.numfaces;
out[i].contents = -1; // differentiate from leafs
for (j=0 ; j<2 ; j++)
{
p = in.children[j];
if (p >= 0)
out[i].children[j] = loadmodel.nodes[p];
else
out[i].children[j] = loadmodel.leafs[-1 - p]; // mleaf_t extends mnode_t
}
}
Node.Mod_SetParent(loadmodel.nodes[0], null); // sets nodes and leafs
}
/*
=================
Mod_LoadLeafs
=================
*/
static void Mod_LoadLeafs(Lump l)
{
QuakeFiles.dleaf_t in;
Leaf[] out;
int i, j, count;
if ((l.filelen % QuakeFiles.dleaf_t.SIZE) != 0)
Com.Error (Constants.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name);
count = l.filelen / QuakeFiles.dleaf_t.SIZE;
// out = Hunk_Alloc ( count*sizeof(*out));
out = new Leaf[count];
loadmodel.leafs = out;
loadmodel.numleafs = count;
ByteBuffer bb = mod_base;mod_base.position(l.fileofs);//ByteBuffer.wrap(mod_base, l.fileofs, l.filelen);
bb.order(ByteOrder.LITTLE_ENDIAN);
for ( i=0 ; i<count ; i++)
{
in = new QuakeFiles.dleaf_t(bb);
out[i] = new Leaf();
for (j=0 ; j<3 ; j++)
{
out[i].mins[j] = in.mins[j];
out[i].maxs[j] = in.maxs[j];
}
out[i].contents = in.contents;
out[i].cluster = in.cluster;
out[i].area = in.area;
out[i].setMarkSurface(in.firstleafface, loadmodel.marksurfaces);
out[i].nummarksurfaces = in.numleaffaces;
}
}
/*
=================
Mod_LoadMarksurfaces
=================
*/
static void Mod_LoadMarksurfaces(Lump l)
{
int i, j, count;
Surface[] out;
if ((l.filelen % Constants.SIZE_OF_SHORT) != 0)
Com.Error(Constants.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name);
count = l.filelen / Constants.SIZE_OF_SHORT;
// out = Hunk_Alloc ( count*sizeof(*out));
out = new Surface[count];
loadmodel.marksurfaces = out;
loadmodel.nummarksurfaces = count;
ByteBuffer bb = mod_base;mod_base.position(l.fileofs);//ByteBuffer.wrap(mod_base, l.fileofs, l.filelen);
bb.order(ByteOrder.LITTLE_ENDIAN);
for ( i=0 ; i<count ; i++)
{
j = bb.getShort();
if (j < 0 || j >= loadmodel.numsurfaces)
Com.Error(Constants.ERR_DROP, "Mod_ParseMarksurfaces: bad surface number");
out[i] = loadmodel.surfaces[j];
}
}
/*
=================
Mod_LoadSurfedges
=================
*/
static void Mod_LoadSurfedges(Lump l)
{
int i, count;
int[] offsets;
if ( (l.filelen % Constants.SIZE_OF_INT) != 0)
Com.Error (Constants.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name);
count = l.filelen / Constants.SIZE_OF_INT;
if (count < 1 || count >= Constants.MAX_MAP_SURFEDGES)
Com.Error (Constants.ERR_DROP, "MOD_LoadBmodel: bad surfedges count in " + loadmodel.name + ": " + count);
offsets = new int[count];
loadmodel.surfedges = offsets;
loadmodel.numsurfedges = count;
ByteBuffer bb = mod_base;mod_base.position(l.fileofs);//ByteBuffer.wrap(mod_base, l.fileofs, l.filelen);
bb.order(ByteOrder.LITTLE_ENDIAN);
for ( i=0 ; i<count ; i++) offsets[i] = bb.getInt();
}
/*
=================
Mod_LoadPlanes
=================
*/
static void Mod_LoadPlanes(Lump l)
{
int i, j;
Plane[] out;
QuakeFiles.dplane_t in;
int count;
int bits;
if ((l.filelen % QuakeFiles.dplane_t.SIZE) != 0)
Com.Error(Constants.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name);
count = l.filelen / QuakeFiles.dplane_t.SIZE;
// out = Hunk_Alloc ( count*2*sizeof(*out));
out = new Plane[count * 2];
for (i = 0; i < count; i++) {
out[i] = new Plane();
}
loadmodel.planes = out;
loadmodel.numplanes = count;
ByteBuffer bb = mod_base;mod_base.position(l.fileofs);//ByteBuffer.wrap(mod_base, l.fileofs, l.filelen);
bb.order(ByteOrder.LITTLE_ENDIAN);
for ( i=0 ; i<count ; i++)
{
bits = 0;
in = new QuakeFiles.dplane_t(bb);
for (j=0 ; j<3 ; j++)
{
out[i].normal[j] = in.normal[j];
if (out[i].normal[j] < 0)
bits |= (1<<j);
}
out[i].dist = in.dist;
out[i].type = (byte)in.type;
out[i].signbits = (byte)bits;
}
}
/*
=================
Mod_LoadBrushModel
=================
*/
static void Mod_LoadBrushModel(Model mod, ByteBuffer buffer)
{
int i;
QuakeFiles.dheader_t header;
SubModel bm;
loadmodel.type = GlConstants.mod_brush;
world_model = loadmodel;
header = new QuakeFiles.dheader_t(buffer);
i = header.version;
if (i != Constants.BSPVERSION)
Com.Error (Constants.ERR_DROP, "Mod_LoadBrushModel: " + mod.name + " has wrong version number (" + i + " should be " + Constants.BSPVERSION + ")");
mod_base = fileBuffer; //(byte *)header;
mod_base.order(ByteOrder.LITTLE_ENDIAN);
// load into heap
Mod_LoadVertexes(header.lumps[Constants.LUMP_VERTEXES]); // ok
Mod_LoadEdges(header.lumps[Constants.LUMP_EDGES]); // ok
Mod_LoadSurfedges(header.lumps[Constants.LUMP_SURFEDGES]); // ok
Mod_LoadLighting(header.lumps[Constants.LUMP_LIGHTING]); // ok
Mod_LoadPlanes(header.lumps[Constants.LUMP_PLANES]); // ok
Mod_LoadTexinfo(header.lumps[Constants.LUMP_TEXINFO]); // ok
Mod_LoadFaces(header.lumps[Constants.LUMP_FACES]); // ok
Mod_LoadMarksurfaces(header.lumps[Constants.LUMP_LEAFFACES]);
Mod_LoadVisibility(header.lumps[Constants.LUMP_VISIBILITY]); // ok
Mod_LoadLeafs(header.lumps[Constants.LUMP_LEAFS]); // ok
Mod_LoadNodes(header.lumps[Constants.LUMP_NODES]); // ok
Mod_LoadSubmodels(header.lumps[Constants.LUMP_MODELS]);
mod.numframes = 2; // regular and alternate animation
//
// set up the submodels
//
Model starmod;
for (i=0 ; i<mod.numsubmodels ; i++)
{
bm = mod.submodels[i];
starmod = mod_inline[i] = loadmodel.copy();
starmod.firstmodelsurface = bm.firstface;
starmod.nummodelsurfaces = bm.numfaces;
starmod.firstnode = bm.headnode;
if (starmod.firstnode >= loadmodel.numnodes)
Com.Error(Constants.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;
}
Surfaces.staticBufferId = GlState.gl.generateStaticBufferId();
}
/*
==============================================================================
ALIAS MODELS
==============================================================================
*/
/*
=================
Mod_LoadAliasModel
=================
*/
static void Mod_LoadAliasModel (Model mod, ByteBuffer buffer)
{
QuakeFiles.dmdl_t pheader;
QuakeFiles.dstvert_t[] poutst;
QuakeFiles.dtriangle_t[] pouttri;
QuakeFiles.daliasframe_t[] poutframe;
int[] poutcmd;
pheader = new QuakeFiles.dmdl_t(buffer);
if (pheader.version != QuakeFiles.ALIAS_VERSION)
Com.Error(Constants.ERR_DROP, "%s has wrong version number (%i should be %i)",
new Vargs(3).add(mod.name).add(pheader.version).add(QuakeFiles.ALIAS_VERSION));
if (pheader.skinheight > GlConstants.MAX_LBM_HEIGHT)
Com.Error(Constants.ERR_DROP, "model "+ mod.name +" has a skin taller than " + GlConstants.MAX_LBM_HEIGHT);
if (pheader.num_xyz <= 0)
Com.Error(Constants.ERR_DROP, "model " + mod.name + " has no vertices");
if (pheader.num_xyz > QuakeFiles.MAX_VERTS)
Com.Error(Constants.ERR_DROP, "model " + mod.name +" has too many vertices");
if (pheader.num_st <= 0)
Com.Error(Constants.ERR_DROP, "model " + mod.name + " has no st vertices");
if (pheader.num_tris <= 0)
Com.Error(Constants.ERR_DROP, "model " + mod.name + " has no triangles");
if (pheader.num_frames <= 0)
Com.Error(Constants.ERR_DROP, "model " + mod.name + " has no frames");
//
// load base s and t vertices (not used in gl version)
//
poutst = new QuakeFiles.dstvert_t[pheader.num_st];
buffer.position(pheader.ofs_st);
for (int i=0 ; i<pheader.num_st ; i++)
{
poutst[i] = new QuakeFiles.dstvert_t(buffer);
}
//
// load triangle lists
//
pouttri = new QuakeFiles.dtriangle_t[pheader.num_tris];
buffer.position(pheader.ofs_tris);
for (int i=0 ; i<pheader.num_tris ; i++)
{
pouttri[i] = new QuakeFiles.dtriangle_t(buffer);
}
//
// load the frames
//
poutframe = new QuakeFiles.daliasframe_t[pheader.num_frames];
buffer.position(pheader.ofs_frames);
for (int i=0 ; i<pheader.num_frames ; i++)
{
poutframe[i] = new QuakeFiles.daliasframe_t(buffer);
// verts are all 8 bit, so no swapping needed
poutframe[i].verts = new int[pheader.num_xyz];
for (int k=0; k < pheader.num_xyz; k++) {
poutframe[i].verts[k] = buffer.getInt();
}
}
mod.type = GlConstants.mod_alias;
//
// load the glcmds
//
poutcmd = new int[pheader.num_glcmds];
buffer.position(pheader.ofs_glcmds);
for (int i=0 ; i<pheader.num_glcmds ; i++)
poutcmd[i] = buffer.getInt(); // LittleLong (pincmd[i]);
// register all skins
String[] skinNames = new String[pheader.num_skins];
byte[] nameBuf = new byte[QuakeFiles.MAX_SKINNAME];
buffer.position(pheader.ofs_skins);
for (int i=0 ; i<pheader.num_skins ; i++)
{
buffer.get(nameBuf);
skinNames[i] = Compatibility.newString(nameBuf);
int n = skinNames[i].indexOf('\0');
if (n > -1) {
skinNames[i] = skinNames[i].substring(0, n);
}
mod.skins[i] = Images.findTexture(skinNames[i], QuakeImage.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
=================
*/
static void Mod_LoadSpriteModel(Model mod, ByteBuffer buffer)
{
QuakeFiles.dsprite_t sprout = new QuakeFiles.dsprite_t(buffer);
if (sprout.version != QuakeFiles.SPRITE_VERSION)
Com.Error(Constants.ERR_DROP, "%s has wrong version number (%i should be %i)",
new Vargs(3).add(mod.name).add(sprout.version).add(QuakeFiles.SPRITE_VERSION));
if (sprout.numframes > QuakeFiles.MAX_MD2SKINS)
Com.Error(Constants.ERR_DROP, "%s has too many frames (%i > %i)",
new Vargs(3).add(mod.name).add(sprout.numframes).add(QuakeFiles.MAX_MD2SKINS));
for (int i=0 ; i<sprout.numframes ; i++)
{
mod.skins[i] = Images.findTexture(sprout.frames[i].name, QuakeImage.it_sprite);
}
mod.type = GlConstants.mod_sprite;
mod.extradata = sprout;
}
// =============================================================================
/*
@@@@@@@@@@@@@@@@@@@@@
R_BeginRegistration
Specifies the model that will be used as the world
@@@@@@@@@@@@@@@@@@@@@
*/
static void R_BeginRegistration(String model, final Command callback)
{
resetModelArrays();
Polygons.reset();
ConsoleVariable flushmap;
GlState.registration_sequence++;
GlState.r_oldviewcluster = -1; // force markleafs
final String fullname = "maps/" + model + ".bsp";
// explicitly free the old map if different
// this guarantees that mod_known[0] is the world map
flushmap = ConsoleVariables.Get("flushmap", "0", 0);
if ((world_model != null) && !world_model.name.equals(fullname) || flushmap.value != 0.0f) {
world_model.Mod_Free();
Com.Println("setting world_model to null");
world_model = null;
}
Mod_ForName(fullname, new AsyncCallback<Model>() {
public void onSuccess(Model response) {
// TODO(jgw): Not sure if this is creating a race condition.
Com.Println("setting world_model to " + response);
GlState.r_worldmodel = response;
callback.execute();
}
public void onFailure(Throwable e) {
Com.Error(Constants.ERR_DROP, "Mod_NumForName: " + fullname + " not found");
}
});
GlState.r_viewcluster = -1;
}
/*
@@@@@@@@@@@@@@@@@@@@@
R_RegisterModel
@@@@@@@@@@@@@@@@@@@@@
*/
static void R_RegisterModel(String name, final AsyncCallback<Model> callback) {
Mod_ForName(name, new AsyncCallback<Model>() {
public void onSuccess(Model mod) {
int i;
QuakeFiles.dmdl_t pheader;
QuakeFiles.dsprite_t sprout;
mod.registration_sequence = GlState.registration_sequence;
// register any images used by the models
if (mod.type == GlConstants.mod_sprite)
{
sprout = (QuakeFiles.dsprite_t)mod.extradata;
for (i=0 ; i<sprout.numframes ; i++)
mod.skins[i] = Images.findTexture(sprout.frames[i].name, QuakeImage.it_sprite);
}
else if (mod.type == GlConstants.mod_alias)
{
pheader = (QuakeFiles.dmdl_t)mod.extradata;
for (i=0 ; i<pheader.num_skins ; i++)
mod.skins[i] = Images.findTexture(pheader.skinNames[i], QuakeImage.it_skin);
// PGM
mod.numframes = pheader.num_frames;
// PGM
}
else if (mod.type == GlConstants.mod_brush)
{
for (i=0 ; i<mod.numtexinfo ; i++)
mod.texinfo[i].image.registration_sequence = GlState.registration_sequence;
}
if (callback != null) {
callback.onSuccess(mod);
}
}
public void onFailure(Throwable e) {
if (callback != null) {
callback.onFailure(e);
}
}
});
}
/*
@@@@@@@@@@@@@@@@@@@@@
R_EndRegistration
@@@@@@@@@@@@@@@@@@@@@
*/
static void R_EndRegistration()
{
Model mod;
// for (int i=0; i<mod_numknown ; i++) {
// mod = mod_known[i];
// if (mod.name.length() == 0)
// continue;
// if (mod.registration_sequence != registration_sequence)
// { // don't need this model
// Mod_Free(mod);
// } else {
// // precompile AliasModels
// if (mod.type == mod_alias)
// precompileGLCmds((qfiles.dmdl_t)mod.extradata);
// }
// }
for (String name : modelReqs.keySet()) {
ModelRequest req = modelReqs.get(name);
mod = req.model;
if (mod.name.length() == 0)
continue;
if (mod.registration_sequence != GlState.registration_sequence)
{ // don't need this model
mod.Mod_Free();
} else {
// precompile AliasModels
if (mod.type == GlConstants.mod_alias)
precompileGLCmds((QuakeFiles.dmdl_t)mod.extradata);
}
}
Images.GL_FreeUnusedImages();
//modelMemoryUsage();
}
// =============================================================================
/*
================
Mod_FreeAll
================
*/
static void Mod_FreeAll() {
// for (int i=0 ; i<mod_numknown ; i++) {
// if (mod_known[i].extradata != null)
// Mod_Free(mod_known[i]);
// }
for (String name : modelReqs.keySet()) {
ModelRequest req = modelReqs.get(name);
if (req.model.extradata != null)
req.model.Mod_Free();
}
}
/*
* new functions for vertex array handling
*/
static final int MODEL_BUFFER_SIZE = 50000;
static FloatBuffer globalModelTextureCoordBuf;
static ShortBuffer globalModelVertexIndexBuf;
static void init() {
globalModelTextureCoordBuf = Lib.newFloatBuffer(MODEL_BUFFER_SIZE * 2);
globalModelVertexIndexBuf = Lib.newShortBuffer(MODEL_BUFFER_SIZE);
}
static void precompileGLCmds(QuakeFiles.dmdl_t model) {
model.textureCoordBuf = globalModelTextureCoordBuf.slice();
model.vertexIndexBuf = globalModelVertexIndexBuf.slice();
Vector<Integer> tmp = new Vector<Integer>();
int count = 0;
int[] order = model.glCmds;
int orderIndex = 0;
while (true)
{
// get the vertex count and primitive type
count = order[orderIndex++];
if (count == 0)
break; // done
tmp.addElement(new Integer(count));
if (count < 0)
{
count = -count;
//gl.glBegin (GL11.GL_TRIANGLE_FAN);
}
else
{
//gl.glBegin (GL11.GL_TRIANGLE_STRIP);
}
do {
// texture coordinates come from the draw list
globalModelTextureCoordBuf.put(Compatibility.intBitsToFloat(order[orderIndex + 0]));
globalModelTextureCoordBuf.put(Compatibility.intBitsToFloat(order[orderIndex + 1]));
globalModelVertexIndexBuf.put((short) order[orderIndex + 2]);
orderIndex += 3;
} while (--count != 0);
}
int size = tmp.size();
model.counts = new int[size];
model.indexElements = new ShortBuffer[size];
count = 0;
int pos = 0;
for (int i = 0; i < model.counts.length; i++) {
count = ((Integer)tmp.get(i)).intValue();
model.counts[i] = count;
count = (count < 0) ? -count : count;
model.vertexIndexBuf.position(pos);
model.indexElements[i] = model.vertexIndexBuf.slice();
model.indexElements[i].limit(count);
pos += count;
}
model.staticTextureBufId = GlState.gl.generateStaticBufferId();
}
static void resetModelArrays() {
globalModelTextureCoordBuf.rewind();
globalModelVertexIndexBuf.rewind();
}
static void modelMemoryUsage() {
System.out.println("AliasModels: globalVertexBuffer size " + globalModelVertexIndexBuf.position());
}
}