/* 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 java.nio.ByteBuffer; import com.googlecode.gwtquake.shared.common.Constants; import com.googlecode.gwtquake.shared.common.Globals; import com.googlecode.gwtquake.shared.game.*; import com.googlecode.gwtquake.shared.util.Math3D; import com.googlecode.gwtquake.shared.util.Vec3Cache; public class Node { // common with leaf public int contents; // -1, to differentiate from leafs public int visframe; // node needs to be traversed if current //public float minmaxs[] = new float[6]; // for bounding box culling public float mins[] = new float[3]; // for bounding box culling public float maxs[] = new float[3]; // for bounding box culling public Node parent; // node specific public Plane plane; public Node children[] = new Node[2]; // unsigned short public int firstsurface; public int numsurfaces; /** * R_RecursiveWorldNode */ static void R_RecursiveWorldNode (Node node) { if (node.contents == Constants.CONTENTS_SOLID) return; // solid if (node.visframe != GlState.r_visframecount) return; if (Entities.R_CullBox(node.mins, node.maxs)) return; int c; Surface mark; // if a leaf node, draw stuff if (node.contents != -1) { Leaf pleaf = (Leaf)node; // check for door connected areas if (GlState.r_newrefdef.areabits != null) { if ( ((GlState.r_newrefdef.areabits[pleaf.area >> 3] & 0xFF) & (1 << (pleaf.area & 7)) ) == 0 ) return; // not visible } int markp = 0; mark = pleaf.getMarkSurface(markp); // first marked surface c = pleaf.nummarksurfaces; if (c != 0) { do { mark.visframe = GlState.r_framecount; mark = pleaf.getMarkSurface(++markp); // next surface } while (--c != 0); } return; } // node is just a decision point, so go down the apropriate sides // find which side of the node we are on Plane plane = node.plane; float dot; switch (plane.type) { case Constants.PLANE_X: dot = Surfaces.modelorg[0] - plane.dist; break; case Constants.PLANE_Y: dot = Surfaces.modelorg[1] - plane.dist; break; case Constants.PLANE_Z: dot = Surfaces.modelorg[2] - plane.dist; break; default: dot = Math3D.DotProduct(Surfaces.modelorg, plane.normal) - plane.dist; break; } int side, sidebit; if (dot >= 0.0f) { side = 0; sidebit = 0; } else { side = 1; sidebit = Constants.SURF_PLANEBACK; } // recurse down the children, front side first R_RecursiveWorldNode(node.children[side]); // draw stuff Surface surf; Image image; //for ( c = node.numsurfaces, surf = r_worldmodel.surfaces[node.firstsurface]; c != 0 ; c--, surf++) for ( c = 0; c < node.numsurfaces; c++) { surf = GlState.r_worldmodel.surfaces[node.firstsurface + c]; if (surf.visframe != GlState.r_framecount) continue; if ( (surf.flags & Constants.SURF_PLANEBACK) != sidebit ) continue; // wrong side if ((surf.texinfo.flags & Constants.SURF_SKY) != 0) { // just adds to visible sky bounds SkyBox.R_AddSkySurface(surf); } else if ((surf.texinfo.flags & (Constants.SURF_TRANS33 | Constants.SURF_TRANS66)) != 0) { // add to the translucent chain surf.texturechain = Surfaces.r_alpha_surfaces; Surfaces.r_alpha_surfaces = surf; } else { if ( ( surf.flags & Constants.SURF_DRAWTURB) == 0 ) { Surface.GL_RenderLightmappedPoly( surf ); } else { // the polygon is visible, so add it to the texture // sorted chain // FIXME: this is a hack for animation image = Texture.R_TextureAnimation(surf.texinfo); surf.texturechain = image.texturechain; image.texturechain = surf; } } } // recurse down the back side R_RecursiveWorldNode(node.children[1 - side]); } /** * RecursiveLightPoint * @param node * @param start * @param end * @return */ static int RecursiveLightPoint (Node node, float[] start, float[] end) { if (node.contents != -1) return -1; // didn't hit anything // calculate mid point // FIXME: optimize for axial Plane plane = node.plane; float front = Math3D.DotProduct (start, plane.normal) - plane.dist; float back = Math3D.DotProduct (end, plane.normal) - plane.dist; boolean side = (front < 0); int sideIndex = (side) ? 1 : 0; if ( (back < 0) == side) return RecursiveLightPoint (node.children[sideIndex], start, end); float frac = front / (front-back); float[] mid = Vec3Cache.get(); mid[0] = start[0] + (end[0] - start[0])*frac; mid[1] = start[1] + (end[1] - start[1])*frac; mid[2] = start[2] + (end[2] - start[2])*frac; // go down front side int r = RecursiveLightPoint (node.children[sideIndex], start, mid); if (r >= 0) { Vec3Cache.release(); // mid return r; // hit something } if ( (back < 0) == side ) { Vec3Cache.release(); // mid return -1; // didn't hit anuthing } // check for impact on this node Math3D.VectorCopy (mid, DynamicLights.lightspot); DynamicLights.lightplane = plane; int surfIndex = node.firstsurface; Surface surf; int s, t, ds, dt; Texture tex; ByteBuffer lightmap; int maps; for (int i=0 ; i<node.numsurfaces ; i++, surfIndex++) { surf = GlState.r_worldmodel.surfaces[surfIndex]; if ((surf.flags & (Constants.SURF_DRAWTURB | Constants.SURF_DRAWSKY)) != 0) continue; // no lightmaps tex = surf.texinfo; s = (int)(Math3D.DotProduct (mid, tex.vecs[0]) + tex.vecs[0][3]); t = (int)(Math3D.DotProduct (mid, tex.vecs[1]) + tex.vecs[1][3]); if (s < surf.texturemins[0] || t < surf.texturemins[1]) continue; ds = s - surf.texturemins[0]; dt = t - surf.texturemins[1]; if ( ds > surf.extents[0] || dt > surf.extents[1] ) continue; if (surf.samples == null) return 0; ds >>= 4; dt >>= 4; lightmap = surf.samples; int lightmapIndex = 0; Math3D.VectorCopy (Globals.vec3_origin, DynamicLights.pointcolor); if (lightmap != null) { float[] rgb; lightmapIndex += 3 * (dt * ((surf.extents[0] >> 4) + 1) + ds); float scale0, scale1, scale2; for (maps = 0 ; maps < Constants.MAXLIGHTMAPS && surf.styles[maps] != (byte)255; maps++) { rgb = GlState.r_newrefdef.lightstyles[surf.styles[maps] & 0xFF].rgb; scale0 = GlConfig.gl_modulate.value * rgb[0]; scale1 = GlConfig.gl_modulate.value * rgb[1]; scale2 = GlConfig.gl_modulate.value * rgb[2]; DynamicLights.pointcolor[0] += (lightmap.get(lightmapIndex + 0) & 0xFF) * scale0 * (1.0f/255); DynamicLights.pointcolor[1] += (lightmap.get(lightmapIndex + 1) & 0xFF) * scale1 * (1.0f/255); DynamicLights.pointcolor[2] += (lightmap.get(lightmapIndex + 2) & 0xFF) * scale2 * (1.0f/255); lightmapIndex += 3 * ((surf.extents[0] >> 4) + 1) * ((surf.extents[1] >> 4) + 1); } } Vec3Cache.release(); // mid return 1; } // go down back side r = RecursiveLightPoint (node.children[1 - sideIndex], mid, end); Vec3Cache.release(); // mid return r; } /* ================= Mod_SetParent ================= */ static void Mod_SetParent(Node node, Node parent) { node.parent = parent; if (node.contents != -1) return; Mod_SetParent(node.children[0], node); Mod_SetParent(node.children[1], node); } }