/* * Surf.java * Copyright (C) 2003 * * $Id: Surf.java,v 1.1 2004-07-07 19:59:43 hzi 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.jogl; import java.awt.geom.AffineTransform; import java.awt.image.AffineTransformOp; import java.awt.image.BufferedImage; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.IntBuffer; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import net.java.games.jogl.GL; import jake2.Defines; import jake2.client.dlight_t; import jake2.client.entity_t; import jake2.client.lightstyle_t; import jake2.game.cplane_t; import jake2.imageio.ImageFrame; import jake2.render.glpoly_t; import jake2.render.image_t; import jake2.render.medge_t; import jake2.render.mleaf_t; import jake2.render.mnode_t; import jake2.render.model_t; import jake2.render.msurface_t; import jake2.render.mtexinfo_t; import jake2.util.Lib; import jake2.util.Math3D; /** * Surf * * @author cwei */ public abstract class Surf extends Draw { // GL_RSURF.C: surface-related refresh code float[] modelorg = {0, 0, 0}; // relative to viewpoint msurface_t r_alpha_surfaces; static final int DYNAMIC_LIGHT_WIDTH = 128; static final int DYNAMIC_LIGHT_HEIGHT = 128; static final int LIGHTMAP_BYTES = 4; static final int BLOCK_WIDTH = 128; static final int BLOCK_HEIGHT = 128; static final int MAX_LIGHTMAPS = 128; int c_visible_lightmaps; int c_visible_textures; static final int GL_LIGHTMAP_FORMAT = GL.GL_RGBA; static class gllightmapstate_t { int internal_format; int current_lightmap_texture; msurface_t[] lightmap_surfaces = new msurface_t[MAX_LIGHTMAPS]; int[] allocated = new int[BLOCK_WIDTH]; // the lightmap texture data needs to be kept in // main memory so texsubimage can update properly byte[] lightmap_buffer = new byte[4 * BLOCK_WIDTH * BLOCK_HEIGHT]; public void clearLightmapSurfaces() { for (int i = 0; i < MAX_LIGHTMAPS; i++) lightmap_surfaces[i] = new msurface_t(); } } gllightmapstate_t gl_lms = new gllightmapstate_t(); // // static void LM_InitBlock( void ); // static void LM_UploadBlock( qboolean dynamic ); // static qboolean LM_AllocBlock (int w, int h, int *x, int *y); // // extern void R_SetCacheState( msurface_t *surf ); // extern void R_BuildLightMap (msurface_t *surf, byte *dest, int stride); // // Model.java abstract byte[] Mod_ClusterPVS(int cluster, model_t model); // Warp.java abstract void R_DrawSkyBox(); abstract void R_AddSkySurface(msurface_t surface); abstract void R_ClearSkyBox(); abstract void EmitWaterPolys(msurface_t fa); // Light.java abstract void R_MarkLights (dlight_t light, int bit, mnode_t node); abstract void R_SetCacheState( msurface_t surf ); abstract void R_BuildLightMap(msurface_t surf, ByteBuffer dest, int stride); /* ============================================================= BRUSH MODELS ============================================================= */ /* =============== R_TextureAnimation Returns the proper texture for a given time and base texture =============== */ image_t R_TextureAnimation(mtexinfo_t tex) { int c; if (tex.next == null) return tex.image; c = currententity.frame % tex.numframes; while (c != 0) { tex = tex.next; c--; } return tex.image; } /* ================ DrawGLPoly ================ */ void DrawGLPoly(glpoly_t p) { int i; float[] v; gl.glBegin(GL.GL_POLYGON); for (i=0 ; iextents[0]>>4)+1; // tmax = (surf->extents[1]>>4)+1; // // if ( LM_AllocBlock( smax, tmax, &surf->dlight_s, &surf->dlight_t ) ) // { // base = gl_lms.lightmap_buffer; // base += ( surf->dlight_t * BLOCK_WIDTH + surf->dlight_s ) * LIGHTMAP_BYTES; // // R_BuildLightMap (surf, base, BLOCK_WIDTH*LIGHTMAP_BYTES); // } // else // { // msurface_t *drawsurf; // // // upload what we have so far // LM_UploadBlock( true ); // // // draw all surfaces that use this lightmap // for ( drawsurf = newdrawsurf; drawsurf != surf; drawsurf = drawsurf->lightmapchain ) // { // if ( drawsurf->polys ) // DrawGLPolyChain( drawsurf->polys, // ( drawsurf->light_s - drawsurf->dlight_s ) * ( 1.0 / 128.0 ), // ( drawsurf->light_t - drawsurf->dlight_t ) * ( 1.0 / 128.0 ) ); // } // // newdrawsurf = drawsurf; // // // clear the block // LM_InitBlock(); // // // try uploading the block now // if ( !LM_AllocBlock( smax, tmax, &surf->dlight_s, &surf->dlight_t ) ) // { // ri.Sys_Error( ERR_FATAL, "Consecutive calls to LM_AllocBlock(%d,%d) failed (dynamic)\n", smax, tmax ); // } // // base = gl_lms.lightmap_buffer; // base += ( surf->dlight_t * BLOCK_WIDTH + surf->dlight_s ) * LIGHTMAP_BYTES; // // R_BuildLightMap (surf, base, BLOCK_WIDTH*LIGHTMAP_BYTES); // } } /* ** draw remainder of dynamic lightmaps that haven't been uploaded yet */ // if ( newdrawsurf != null ) // LM_UploadBlock( true ); // // for ( surf = newdrawsurf; surf != null; surf = surf.lightmapchain ) // { // if ( surf.polys != null ) // DrawGLPolyChain( surf.polys, ( surf.light_s - surf.dlight_s ) * ( 1.0f / 128.0f ), ( surf.light_t - surf.dlight_t ) * ( 1.0f / 128.0f ) ); // } } /* ** restore state */ gl.glDisable(GL.GL_BLEND); gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA); gl.glDepthMask( true ); } /* ================ R_RenderBrushPoly ================ */ void R_RenderBrushPoly(msurface_t fa) { int maps; image_t image; boolean is_dynamic = false; c_brush_polys++; image = R_TextureAnimation(fa.texinfo); if ((fa.flags & Defines.SURF_DRAWTURB) != 0) { GL_Bind( image.texnum ); // warp texture, no lightmaps GL_TexEnv( GL.GL_MODULATE ); gl.glColor4f( gl_state.inverse_intensity, gl_state.inverse_intensity, gl_state.inverse_intensity, 1.0F ); EmitWaterPolys (fa); GL_TexEnv( GL.GL_REPLACE ); return; } else { GL_Bind( image.texnum ); GL_TexEnv( GL.GL_REPLACE ); } // ====== // PGM if((fa.texinfo.flags & Defines.SURF_FLOWING) != 0) DrawGLFlowingPoly(fa); else DrawGLPoly (fa.polys); // PGM // ====== // /* // ** check for lightmap modification // */ // for ( maps = 0; maps < MAXLIGHTMAPS && fa->styles[maps] != 255; maps++ ) // { // if ( r_newrefdef.lightstyles[fa->styles[maps]].white != fa->cached_light[maps] ) // goto dynamic; // } // // // dynamic this frame or dynamic previously // if ( ( fa->dlightframe == r_framecount ) ) // { // dynamic: // if ( gl_dynamic->value ) // { // if (!( fa->texinfo->flags & (SURF_SKY|SURF_TRANS33|SURF_TRANS66|SURF_WARP ) ) ) // { // is_dynamic = true; // } // } // } // if ( is_dynamic ) { // if ( ( fa->styles[maps] >= 32 || fa->styles[maps] == 0 ) && ( fa->dlightframe != r_framecount ) ) // { // unsigned temp[34*34]; // int smax, tmax; // // smax = (fa->extents[0]>>4)+1; // tmax = (fa->extents[1]>>4)+1; // // R_BuildLightMap( fa, (void *)temp, smax*4 ); // R_SetCacheState( fa ); // // GL_Bind( gl_state.lightmap_textures + fa->lightmaptexturenum ); // // qglTexSubImage2D( GL_TEXTURE_2D, 0, // fa->light_s, fa->light_t, // smax, tmax, // GL_LIGHTMAP_FORMAT, // GL_UNSIGNED_BYTE, temp ); // // fa->lightmapchain = gl_lms.lightmap_surfaces[fa->lightmaptexturenum]; // gl_lms.lightmap_surfaces[fa->lightmaptexturenum] = fa; // } // else // { // fa->lightmapchain = gl_lms.lightmap_surfaces[0]; // gl_lms.lightmap_surfaces[0] = fa; // } } else { fa.lightmapchain = gl_lms.lightmap_surfaces[fa.lightmaptexturenum]; gl_lms.lightmap_surfaces[fa.lightmaptexturenum] = fa; } } /* ================ R_DrawAlphaSurfaces Draw water surfaces and windows. The BSP tree is waled front to back, so unwinding the chain of alpha_surfaces will draw back to front, giving proper ordering. ================ */ void R_DrawAlphaSurfaces() { msurface_t s; float intens; // // go back to the world matrix // gl.glLoadMatrixf(r_world_matrix); gl.glEnable (GL.GL_BLEND); GL_TexEnv(GL.GL_MODULATE ); // the textures are prescaled up for a better lighting range, // so scale it back down intens = gl_state.inverse_intensity; for (s=r_alpha_surfaces ; s != null ; s=s.texturechain) { GL_Bind(s.texinfo.image.texnum); c_brush_polys++; if ((s.texinfo.flags & Defines.SURF_TRANS33) != 0) gl.glColor4f (intens, intens, intens, 0.33f); else if ((s.texinfo.flags & Defines.SURF_TRANS66) != 0) gl.glColor4f (intens, intens, intens, 0.66f); else gl.glColor4f (intens,intens,intens,1); if ((s.flags & Defines.SURF_DRAWTURB) != 0) EmitWaterPolys(s); else if((s.texinfo.flags & Defines.SURF_FLOWING) != 0) // PGM 9/16/98 DrawGLFlowingPoly (s); // PGM else DrawGLPoly (s.polys); } GL_TexEnv( GL.GL_REPLACE ); gl.glColor4f (1,1,1,1); gl.glDisable (GL.GL_BLEND); r_alpha_surfaces = null; } /* ================ DrawTextureChains ================ */ void DrawTextureChains() { int i; msurface_t s; image_t image; c_visible_textures = 0; if ( !qglSelectTextureSGIS && !qglActiveTextureARB ) { for (i = 0; i < numgltextures ; i++) { image = gltextures[i]; if (image.registration_sequence == 0) continue; s = image.texturechain; if (s == null) continue; c_visible_textures++; for ( ; s != null ; s=s.texturechain) R_RenderBrushPoly(s); image.texturechain = null; } } else { for (i = 0; i < numgltextures ; i++) { image = gltextures[i]; if (image.registration_sequence == 0) continue; if (image.texturechain == null) continue; c_visible_textures++; for ( s = image.texturechain; s != null ; s=s.texturechain) { if ( ( s.flags & Defines.SURF_DRAWTURB) == 0 ) R_RenderBrushPoly(s); } } GL_EnableMultitexture( false ); for (i = 0; i < numgltextures ; i++) { image = gltextures[i]; if (image.registration_sequence == 0) continue; s = image.texturechain; if (s == null) continue; for ( ; s != null ; s=s.texturechain) { if ( (s.flags & Defines.SURF_DRAWTURB) != 0 ) R_RenderBrushPoly(s); } image.texturechain = null; } } GL_TexEnv( GL.GL_REPLACE ); } void GL_RenderLightmappedPoly( msurface_t surf ) { int i, nv = surf.polys.numverts; int map = 0; float[] v; image_t image = R_TextureAnimation( surf.texinfo ); boolean is_dynamic = false; int lmtex = surf.lightmaptexturenum; glpoly_t p; // for ( map = 0; map < Defines.MAXLIGHTMAPS && surf.styles[map] != (byte)255; map++ ) // { // if ( r_newrefdef.lightstyles[surf.styles[map] & 0xFF].white != surf.cached_light[map] ) // goto dynamic; // } // // // dynamic this frame or dynamic previously // if ( ( surf.dlightframe == r_framecount ) ) // { // dynamic: // if ( gl_dynamic.value ) // { // if ( !(surf.texinfo.flags & (SURF_SKY|SURF_TRANS33|SURF_TRANS66|SURF_WARP ) ) ) // { // is_dynamic = true; // } // } // } // if ( is_dynamic ) { // unsigned temp[128*128]; // int smax, tmax; // if ( ( (surf.styles[map] & 0xFF) >= 32 || surf.styles[map] == 0 ) && ( surf.dlightframe != r_framecount ) ) { // smax = (surf.extents[0]>>4)+1; // tmax = (surf.extents[1]>>4)+1; // // R_BuildLightMap( surf, (void *)temp, smax*4 ); // R_SetCacheState( surf ); // // GL_MBind( GL_TEXTURE1, gl_state.lightmap_textures + surf.lightmaptexturenum ); // // lmtex = surf.lightmaptexturenum; // // gl.glTexSubImage2D( GL_TEXTURE_2D, 0, // surf.light_s, surf.light_t, // smax, tmax, // GL_LIGHTMAP_FORMAT, // GL_UNSIGNED_BYTE, temp ); // } else { // smax = (surf.extents[0]>>4)+1; // tmax = (surf.extents[1]>>4)+1; // // R_BuildLightMap( surf, (void *)temp, smax*4 ); // // GL_MBind( GL_TEXTURE1, gl_state.lightmap_textures + 0 ); // // lmtex = 0; // // gl.glTexSubImage2D( GL_TEXTURE_2D, 0, // surf.light_s, surf.light_t, // smax, tmax, // GL_LIGHTMAP_FORMAT, // GL_UNSIGNED_BYTE, temp ); // } c_brush_polys++; GL_MBind( GL_TEXTURE0, image.texnum ); GL_MBind( GL_TEXTURE1, gl_state.lightmap_textures + lmtex ); // ========== // PGM if ((surf.texinfo.flags & Defines.SURF_FLOWING) != 0) { float scroll; scroll = -64 * ( (r_newrefdef.time / 40.0f) - (int)(r_newrefdef.time / 40.0f) ); if(scroll == 0.0f) scroll = -64.0f; for ( p = surf.polys; p != null; p = p.chain ) { gl.glBegin (GL.GL_POLYGON); for (i=0 ; i< nv; i++) { v = p.verts[i]; gl.glMultiTexCoord2fARB(GL_TEXTURE0, (v[3] + scroll), v[4]); gl.glMultiTexCoord2fARB(GL_TEXTURE1, v[5], v[6]); //gglMTexCoord2fSGIS( GL_TEXTURE0, v[3], v[4]); //gglMTexCoord2fSGIS( GL_TEXTURE1, v[5], v[6]); gl.glVertex3fv(v); } gl.glEnd (); } } else { for ( p = surf.polys; p != null; p = p.chain ) { gl.glBegin (GL.GL_POLYGON); for (i=0 ; i< nv; i++) { v = p.verts[i]; gl.glMultiTexCoord2fARB(GL_TEXTURE0, v[3], v[4]); gl.glMultiTexCoord2fARB(GL_TEXTURE1, v[5], v[6]); //gglMTexCoord2fSGIS( GL_TEXTURE0, v[3], v[4]); //gglMTexCoord2fSGIS( GL_TEXTURE1, v[5], v[6]); gl.glVertex3fv(v); } gl.glEnd (); } } // PGM // ========== } else { c_brush_polys++; GL_MBind( GL_TEXTURE0, image.texnum ); GL_MBind( GL_TEXTURE1, gl_state.lightmap_textures + lmtex); // ========== // PGM if ((surf.texinfo.flags & Defines.SURF_FLOWING) != 0) { float scroll; scroll = -64 * ( (r_newrefdef.time / 40.0f) - (int)(r_newrefdef.time / 40.0f) ); if(scroll == 0.0) scroll = -64.0f; for ( p = surf.polys; p != null; p = p.chain ) { gl.glBegin(GL.GL_POLYGON); for (i=0 ; i< nv; i++) { v = p.verts[i]; gl.glMultiTexCoord2fARB(GL_TEXTURE0, (v[3]+scroll), v[4]); gl.glMultiTexCoord2fARB(GL_TEXTURE1, v[5], v[6]); // qglMTexCoord2fSGIS( GL_TEXTURE0, (v[3]+scroll), v[4]); // qglMTexCoord2fSGIS( GL_TEXTURE1, v[5], v[6]); gl.glVertex3fv(v); } gl.glEnd(); } } else { // PGM // ========== for ( p = surf.polys; p != null; p = p.chain ) { gl.glBegin (GL.GL_POLYGON); for (i=0 ; i< nv; i++) { v = p.verts[i]; gl.glMultiTexCoord2fARB(GL_TEXTURE0, v[3], v[4]); gl.glMultiTexCoord2fARB(GL_TEXTURE1, v[5], v[6]); //gglMTexCoord2fSGIS( GL_TEXTURE0, v[3], v[4]); //gglMTexCoord2fSGIS( GL_TEXTURE1, v[5], v[6]); gl.glVertex3fv(v); } gl.glEnd (); } // ========== // PGM } // PGM // ========== } } /* ================= R_DrawInlineBModel ================= */ void R_DrawInlineBModel() { int i, k; cplane_t pplane; float dot; msurface_t psurf; dlight_t lt; // calculate dynamic lighting for bmodel if ( gl_flashblend.value == 0 ) { for (k=0 ; ksurfaces[currentmodel->firstmodelsurface]; int psurfp = currentmodel.firstmodelsurface; msurface_t[] surfaces; surfaces = currentmodel.surfaces; //psurf = surfaces[psurfp]; if ( (currententity.flags & Defines.RF_TRANSLUCENT) != 0 ) { gl.glEnable (GL.GL_BLEND); gl.glColor4f (1,1,1,0.25f); GL_TexEnv( GL.GL_MODULATE ); } // // draw texture // for (i=0 ; i BACKFACE_EPSILON))) { if ((psurf.texinfo.flags & (Defines.SURF_TRANS33 | Defines.SURF_TRANS66)) != 0 ) { // add to the translucent chain psurf.texturechain = r_alpha_surfaces; r_alpha_surfaces = psurf; } else if ( qglMTexCoord2fSGIS && ( psurf.flags & Defines.SURF_DRAWTURB ) == 0 ) { GL_RenderLightmappedPoly( psurf ); } else { GL_EnableMultitexture( false ); R_RenderBrushPoly( psurf ); GL_EnableMultitexture( true ); } } } if ( (currententity.flags & Defines.RF_TRANSLUCENT) == 0 ) { if ( !qglMTexCoord2fSGIS ) R_BlendLightmaps(); } else { gl.glDisable (GL.GL_BLEND); gl.glColor4f (1,1,1,1); GL_TexEnv( GL.GL_REPLACE ); } } /* ================= R_DrawBrushModel ================= */ void R_DrawBrushModel(entity_t e) { float[] mins = {0, 0, 0}; float[] maxs = {0, 0, 0}; int i; boolean rotated; if (currentmodel.nummodelsurfaces == 0) return; currententity = e; gl_state.currenttextures[0] = gl_state.currenttextures[1] = -1; if (e.angles[0] != 0 || e.angles[1] != 0 || e.angles[2] != 0) { rotated = true; for (i=0 ; i<3 ; i++) { mins[i] = e.origin[i] - currentmodel.radius; maxs[i] = e.origin[i] + currentmodel.radius; //System.out.println("rotate: " + Lib.vtos(mins) + " " + Lib.vtos(maxs)); } } else { rotated = false; Math3D.VectorAdd(e.origin, currentmodel.mins, mins); Math3D.VectorAdd(e.origin, currentmodel.maxs, maxs); //System.out.println(" " + Lib.vtos(mins) + " " + Lib.vtos(maxs)); } if (R_CullBox(mins, maxs)) { //System.out.println("origin " + Lib.vtos(e.origin) + " +++ " + Lib.vtos(currentmodel.mins)); return; } gl.glColor3f (1,1,1); // memset (gl_lms.lightmap_surfaces, 0, sizeof(gl_lms.lightmap_surfaces)); gl_lms.clearLightmapSurfaces(); Math3D.VectorSubtract (r_newrefdef.vieworg, e.origin, modelorg); if (rotated) { float[] temp = {0, 0, 0}; float[] forward = {0, 0, 0}; float[] right = {0, 0, 0}; float[] up = {0, 0, 0}; Math3D.VectorCopy (modelorg, temp); Math3D.AngleVectors (e.angles, forward, right, up); modelorg[0] = Math3D.DotProduct (temp, forward); modelorg[1] = -Math3D.DotProduct (temp, right); modelorg[2] = Math3D.DotProduct (temp, up); } gl.glPushMatrix(); e.angles[0] = -e.angles[0]; // stupid quake bug e.angles[2] = -e.angles[2]; // stupid quake bug R_RotateForEntity(e); e.angles[0] = -e.angles[0]; // stupid quake bug e.angles[2] = -e.angles[2]; // stupid quake bug GL_EnableMultitexture( true ); GL_SelectTexture(GL_TEXTURE0); GL_TexEnv( GL.GL_REPLACE ); GL_SelectTexture(GL_TEXTURE1); GL_TexEnv( GL.GL_MODULATE ); R_DrawInlineBModel(); GL_EnableMultitexture( false ); gl.glPopMatrix(); } /* ============================================================= WORLD MODEL ============================================================= */ /* ================ R_RecursiveWorldNode ================ */ void R_RecursiveWorldNode (mnode_t node) { int c, side, sidebit; cplane_t plane; msurface_t surf; msurface_t mark; mleaf_t pleaf; float dot = 0; image_t image; if (node.contents == Defines.CONTENTS_SOLID) return; // solid if (node.visframe != r_visframecount) return; if (R_CullBox(node.mins, node.maxs)) return; // if a leaf node, draw stuff if (node.contents != -1) { pleaf = (mleaf_t)node; // check for door connected areas if (r_newrefdef.areabits != null) { if ( ((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 = 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 = node.plane; switch (plane.type) { case Defines.PLANE_X: dot = modelorg[0] - plane.dist; break; case Defines.PLANE_Y: dot = modelorg[1] - plane.dist; break; case Defines.PLANE_Z: dot = modelorg[2] - plane.dist; break; default: dot = Math3D.DotProduct(modelorg, plane.normal) - plane.dist; break; } if (dot >= 0.0f) { side = 0; sidebit = 0; } else { side = 1; sidebit = Defines.SURF_PLANEBACK; } // recurse down the children, front side first R_RecursiveWorldNode(node.children[side]); // draw stuff //for ( c = node.numsurfaces, surf = r_worldmodel.surfaces[node.firstsurface]; c != 0 ; c--, surf++) for ( c = 0; c < node.numsurfaces; c++) { surf = r_worldmodel.surfaces[node.firstsurface + c]; if (surf.visframe != r_framecount) continue; if ( (surf.flags & Defines.SURF_PLANEBACK) != sidebit ) continue; // wrong side if ((surf.texinfo.flags & Defines.SURF_SKY) != 0) { // just adds to visible sky bounds R_AddSkySurface(surf); } else if ((surf.texinfo.flags & (Defines.SURF_TRANS33 | Defines.SURF_TRANS66)) != 0) { // add to the translucent chain surf.texturechain = r_alpha_surfaces; r_alpha_surfaces = surf; } else { if ( qglMTexCoord2fSGIS && ( surf.flags & Defines.SURF_DRAWTURB) == 0 ) { GL_RenderLightmappedPoly( surf ); } else { // the polygon is visible, so add it to the texture // sorted chain // FIXME: this is a hack for animation image = R_TextureAnimation(surf.texinfo); surf.texturechain = image.texturechain; image.texturechain = surf; } } } // recurse down the back side R_RecursiveWorldNode(node.children[1 - side]); } /* ============= R_DrawWorld ============= */ void R_DrawWorld() { entity_t ent = new entity_t(); if (r_drawworld.value == 0) return; if ( (r_newrefdef.rdflags & Defines.RDF_NOWORLDMODEL) != 0 ) return; currentmodel = r_worldmodel; Math3D.VectorCopy(r_newrefdef.vieworg, modelorg); // auto cycle the world frame for texture animation // memset (&ent, 0, sizeof(ent)); ent.frame = (int)(r_newrefdef.time*2); currententity = ent; gl_state.currenttextures[0] = gl_state.currenttextures[1] = -1; gl.glColor3f (1,1,1); // memset (gl_lms.lightmap_surfaces, 0, sizeof(gl_lms.lightmap_surfaces)); gl_lms.clearLightmapSurfaces(); R_ClearSkyBox(); if ( qglMTexCoord2fSGIS ) { GL_EnableMultitexture( true ); GL_SelectTexture( GL_TEXTURE0); GL_TexEnv( GL.GL_REPLACE ); GL_SelectTexture( GL_TEXTURE1); if ( gl_lightmap.value != 0) GL_TexEnv( GL.GL_REPLACE ); else GL_TexEnv( GL.GL_MODULATE ); R_RecursiveWorldNode(r_worldmodel.nodes[0]); // root node GL_EnableMultitexture( false ); } else { R_RecursiveWorldNode(r_worldmodel.nodes[0]); // root node } /* ** theoretically nothing should happen in the next two functions ** if multitexture is enabled */ DrawTextureChains(); R_BlendLightmaps(); R_DrawSkyBox(); R_DrawTriangleOutlines(); } byte[] fatvis = new byte[Defines.MAX_MAP_LEAFS / 8]; /* =============== R_MarkLeaves Mark the leaves and nodes that are in the PVS for the current cluster =============== */ void R_MarkLeaves() { byte[] vis; //byte[] fatvis = new byte[Defines.MAX_MAP_LEAFS / 8]; Arrays.fill(fatvis, (byte)0); mnode_t node; int i, c; mleaf_t leaf; int cluster; if (r_oldviewcluster == r_viewcluster && r_oldviewcluster2 == r_viewcluster2 && r_novis.value == 0 && r_viewcluster != -1) return; // development aid to let you run around and see exactly where // the pvs ends if (gl_lockpvs.value != 0) return; r_visframecount++; r_oldviewcluster = r_viewcluster; r_oldviewcluster2 = r_viewcluster2; if (r_novis.value != 0 || r_viewcluster == -1 || r_worldmodel.vis == null) { // mark everything for (i=0 ; i>3] & 0xFF) & (1 << (cluster & 7))) != 0) { node = (mnode_t)leaf; do { if (node.visframe == r_visframecount) break; node.visframe = r_visframecount; node = node.parent; } while (node != null); } } } /* ============================================================================= LIGHTMAP ALLOCATION ============================================================================= */ void LM_InitBlock() { Arrays.fill(gl_lms.allocated, 0); } void LM_UploadBlock( boolean dynamic ) { int texture; int height = 0; if ( dynamic ) { texture = 0; } else { texture = gl_lms.current_lightmap_texture; } GL_Bind( gl_state.lightmap_textures + texture ); gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR); gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR); if ( dynamic ) { int i; for ( i = 0; i < BLOCK_WIDTH; i++ ) { if ( gl_lms.allocated[i] > height ) height = gl_lms.allocated[i]; } gl.glTexSubImage2D( GL.GL_TEXTURE_2D, 0, 0, 0, BLOCK_WIDTH, height, GL_LIGHTMAP_FORMAT, GL.GL_UNSIGNED_BYTE, gl_lms.lightmap_buffer ); } else { gl.glTexImage2D( GL.GL_TEXTURE_2D, 0, gl_lms.internal_format, BLOCK_WIDTH, BLOCK_HEIGHT, 0, GL_LIGHTMAP_FORMAT, GL.GL_UNSIGNED_BYTE, gl_lms.lightmap_buffer ); if ( ++gl_lms.current_lightmap_texture == MAX_LIGHTMAPS ) ri.Sys_Error( Defines.ERR_DROP, "LM_UploadBlock() - MAX_LIGHTMAPS exceeded\n" ); //debugLightmap(gl_lms.lightmap_buffer, 128, 128, 4); } } // returns a texture number and the position inside it boolean LM_AllocBlock (int w, int h, pos_t pos) { int x = pos.x; int y = pos.y; int i, j; int best, best2; best = BLOCK_HEIGHT; for (i=0 ; i= best) break; if (gl_lms.allocated[i+j] > best2) best2 = gl_lms.allocated[i+j]; } if (j == w) { // this is a valid spot pos.x = x = i; pos.y = y = best = best2; } } if (best + h > BLOCK_HEIGHT) return false; for (i=0 ; i 0) { r_pedge = pedges[lindex]; vec = currentmodel.vertexes[r_pedge.v[0]].position; } else { r_pedge = pedges[-lindex]; vec = currentmodel.vertexes[r_pedge.v[1]].position; } s = Math3D.DotProduct (vec, fa.texinfo.vecs[0]) + fa.texinfo.vecs[0][3]; s /= fa.texinfo.image.width; t = Math3D.DotProduct (vec, fa.texinfo.vecs[1]) + fa.texinfo.vecs[1][3]; t /= fa.texinfo.image.height; Math3D.VectorAdd (total, vec, total); Math3D.VectorCopy (vec, poly.verts[i]); poly.verts[i][3] = s; poly.verts[i][4] = t; // // lightmap texture coordinates // s = Math3D.DotProduct (vec, fa.texinfo.vecs[0]) + fa.texinfo.vecs[0][3]; s -= fa.texturemins[0]; s += fa.light_s*16; s += 8; s /= BLOCK_WIDTH*16; //fa.texinfo.texture.width; t = Math3D.DotProduct (vec, fa.texinfo.vecs[1]) + fa.texinfo.vecs[1][3]; t -= fa.texturemins[1]; t += fa.light_t*16; t += 8; t /= BLOCK_HEIGHT*16; //fa.texinfo.texture.height; poly.verts[i][5] = s; poly.verts[i][6] = t; } poly.numverts = lnumverts; } /* ======================== GL_CreateSurfaceLightmap ======================== */ void GL_CreateSurfaceLightmap(msurface_t surf) { int smax, tmax; ByteBuffer base; if ( (surf.flags & (Defines.SURF_DRAWSKY | Defines.SURF_DRAWTURB)) != 0) return; smax = (surf.extents[0]>>4)+1; tmax = (surf.extents[1]>>4)+1; pos_t lightPos = new pos_t(surf.light_s, surf.light_t); if ( !LM_AllocBlock( smax, tmax, lightPos ) ) { LM_UploadBlock( false ); LM_InitBlock(); lightPos = new pos_t(surf.light_s, surf.light_t); if ( !LM_AllocBlock( smax, tmax, lightPos ) ) { ri.Sys_Error( Defines.ERR_FATAL, "Consecutive calls to LM_AllocBlock(" + smax +"," + tmax +") failed\n"); } } // kopiere die koordinaten zurueck surf.light_s = lightPos.x; surf.light_t = lightPos.y; surf.lightmaptexturenum = gl_lms.current_lightmap_texture; // base = gl_lms.lightmap_buffer; base = ByteBuffer.wrap(gl_lms.lightmap_buffer); int basep = (surf.light_t * BLOCK_WIDTH + surf.light_s) * LIGHTMAP_BYTES; base.position(basep); R_SetCacheState( surf ); R_BuildLightMap(surf, base.slice(), BLOCK_WIDTH * LIGHTMAP_BYTES); } lightstyle_t[] lightstyles; /* ================== GL_BeginBuildingLightmaps ================== */ void GL_BeginBuildingLightmaps(model_t m) { // static lightstyle_t lightstyles[MAX_LIGHTSTYLES]; int i; int[] dummy = new int[128*128]; // init lightstyles if ( lightstyles == null ) { lightstyles = new lightstyle_t[Defines.MAX_LIGHTSTYLES]; for (i = 0; i < lightstyles.length; i++) { lightstyles[i] = new lightstyle_t(); } } // memset( gl_lms.allocated, 0, sizeof(gl_lms.allocated) ); Arrays.fill(gl_lms.allocated, 0); r_framecount = 1; // no dlightcache GL_EnableMultitexture( true ); GL_SelectTexture( GL_TEXTURE1); /* ** setup the base lightstyles so the lightmaps won't have to be regenerated ** the first time they're seen */ for (i=0 ; i < Defines.MAX_LIGHTSTYLES ; i++) { lightstyles[i].rgb[0] = 1; lightstyles[i].rgb[1] = 1; lightstyles[i].rgb[2] = 1; lightstyles[i].white = 3; } r_newrefdef.lightstyles = lightstyles; if (gl_state.lightmap_textures == 0) { gl_state.lightmap_textures = TEXNUM_LIGHTMAPS; } gl_lms.current_lightmap_texture = 1; /* ** if mono lightmaps are enabled and we want to use alpha ** blending (a,1-a) then we're likely running on a 3DLabs ** Permedia2. In a perfect world we'd use a GL_ALPHA lightmap ** in order to conserve space and maximize bandwidth, however ** this isn't a perfect world. ** ** So we have to use alpha lightmaps, but stored in GL_RGBA format, ** which means we only get 1/16th the color resolution we should when ** using alpha lightmaps. If we find another board that supports ** only alpha lightmaps but that can at least support the GL_ALPHA ** format then we should change this code to use real alpha maps. */ char format = gl_monolightmap.string.toUpperCase().charAt(0); if ( format == 'A' ) { gl_lms.internal_format = gl_tex_alpha_format; } /* ** try to do hacked colored lighting with a blended texture */ else if ( format == 'C' ) { gl_lms.internal_format = gl_tex_alpha_format; } else if ( format == 'I' ) { gl_lms.internal_format = GL.GL_INTENSITY8; } else if ( format == 'L' ) { gl_lms.internal_format = GL.GL_LUMINANCE8; } else { gl_lms.internal_format = gl_tex_solid_format; } /* ** initialize the dynamic lightmap texture */ GL_Bind( gl_state.lightmap_textures + 0 ); gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR); gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR); gl.glTexImage2D( GL.GL_TEXTURE_2D, 0, gl_lms.internal_format, BLOCK_WIDTH, BLOCK_HEIGHT, 0, GL_LIGHTMAP_FORMAT, GL.GL_UNSIGNED_BYTE, dummy ); } /* ======================= GL_EndBuildingLightmaps ======================= */ void GL_EndBuildingLightmaps() { LM_UploadBlock( false ); GL_EnableMultitexture( false ); } ImageFrame frame; void debugLightmap(byte[] buf, int w, int h, float scale) { IntBuffer pix = ByteBuffer.wrap(buf).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer(); int[] pixel = new int[w * h]; pix.get(pixel); BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_4BYTE_ABGR); image.setRGB(0, 0, w, h, pixel, 0, w); AffineTransformOp op = new AffineTransformOp(AffineTransform.getScaleInstance(scale, scale), AffineTransformOp.TYPE_NEAREST_NEIGHBOR); BufferedImage tmp = op.filter(image, null); if (frame == null) { frame = new ImageFrame(null); frame.show(); } frame.showImage(tmp); } }