diff options
Diffstat (limited to 'src/jake2/render/jogl/Surf.java')
-rw-r--r-- | src/jake2/render/jogl/Surf.java | 1701 |
1 files changed, 1701 insertions, 0 deletions
diff --git a/src/jake2/render/jogl/Surf.java b/src/jake2/render/jogl/Surf.java new file mode 100644 index 0000000..7baf69f --- /dev/null +++ b/src/jake2/render/jogl/Surf.java @@ -0,0 +1,1701 @@ +/* + * 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 ; i<p.numverts ; i++) + { + v = p.verts[i]; + gl.glTexCoord2f(v[3], v[4]); + gl.glVertex3fv(v); + } + gl.glEnd(); + } + + // ============ + // PGM + /* + ================ + DrawGLFlowingPoly -- version of DrawGLPoly that handles scrolling texture + ================ + */ + void DrawGLFlowingPoly(msurface_t fa) + { + int i; + float[] v; + glpoly_t p; + float scroll; + + p = fa.polys; + + scroll = -64 * ( (r_newrefdef.time / 40.0f) - (int)(r_newrefdef.time / 40.0f) ); + if(scroll == 0.0f) + scroll = -64.0f; + + gl.glBegin (GL.GL_POLYGON); + for (i=0 ; i<p.numverts ; i++) + { + v = p.verts[i]; + gl.glTexCoord2f ((v[3] + scroll), v[4]); + gl.glVertex3fv( v ); + } + gl.glEnd (); + } + // PGM + // ============ + + /* + ** R_DrawTriangleOutlines + */ + void R_DrawTriangleOutlines() + { + int i, j; + glpoly_t p; + + if (gl_showtris.value == 0) + return; + + gl.glDisable (GL.GL_TEXTURE_2D); + gl.glDisable (GL.GL_DEPTH_TEST); + gl.glColor4f (1,1,1,1); + + for (i=0 ; i<MAX_LIGHTMAPS ; i++) + { + msurface_t surf; + + for ( surf = gl_lms.lightmap_surfaces[i]; surf != null; surf = surf.lightmapchain ) + { + p = surf.polys; + for ( ; p != null ; p=p.chain) + { + for (j=2 ; j<p.numverts ; j++ ) + { + gl.glBegin (GL.GL_LINE_STRIP); + gl.glVertex3fv (p.verts[0]); + gl.glVertex3fv (p.verts[j-1]); + gl.glVertex3fv (p.verts[j]); + gl.glVertex3fv (p.verts[0]); + gl.glEnd (); + } + } + } + } + + gl.glEnable (GL.GL_DEPTH_TEST); + gl.glEnable (GL.GL_TEXTURE_2D); + } + + /* + ** DrawGLPolyChain + */ + void DrawGLPolyChain( glpoly_t p, float soffset, float toffset ) + { + if ( soffset == 0 && toffset == 0 ) + { + for ( ; p != null; p = p.chain ) + { + float[] v; + int j; + + gl.glBegin(GL.GL_POLYGON); + for (j=0 ; j<p.numverts ; j++) + { + v = p.verts[j]; + gl.glTexCoord2f (v[5], v[6] ); + gl.glVertex3fv( v ); + } + gl.glEnd(); + } + } + else + { + for ( ; p != null; p = p.chain ) + { + float[] v; + int j; + + gl.glBegin(GL.GL_POLYGON); + for (j=0 ; j<p.numverts ; j++) + { + v = p.verts[j]; + gl.glTexCoord2f (v[5] - soffset, v[6] - toffset ); + gl.glVertex3fv( v ); + } + gl.glEnd(); + } + } + } + + /* + ** R_BlendLightMaps + ** + ** This routine takes all the given light mapped surfaces in the world and + ** blends them into the framebuffer. + */ + void R_BlendLightmaps() + { + int i; + msurface_t surf; + msurface_t newdrawsurf = null; + + // don't bother if we're set to fullbright + if (r_fullbright.value != 0) + return; + if (r_worldmodel.lightdata == null) + return; + + // don't bother writing Z + gl.glDepthMask( false ); + + /* + ** set the appropriate blending mode unless we're only looking at the + ** lightmaps. + */ + if (gl_lightmap.value == 0) + { + gl.glEnable(GL.GL_BLEND); + + if ( gl_saturatelighting.value != 0) + { + gl.glBlendFunc( GL.GL_ONE, GL.GL_ONE ); + } + else + { + char format = gl_monolightmap.string.toUpperCase().charAt(0); + if ( format != '0' ) + { + switch ( format ) + { + case 'I': + gl.glBlendFunc(GL.GL_ZERO, GL.GL_SRC_COLOR ); + break; + case 'L': + gl.glBlendFunc(GL.GL_ZERO, GL.GL_SRC_COLOR ); + break; + case 'A': + default: + gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA ); + break; + } + } + else + { + gl.glBlendFunc(GL.GL_ZERO, GL.GL_SRC_COLOR ); + } + } + } + + if ( currentmodel == r_worldmodel ) + c_visible_lightmaps = 0; + + /* + ** render static lightmaps first + */ + for ( i = 1; i < MAX_LIGHTMAPS; i++ ) + { + if ( gl_lms.lightmap_surfaces[i] != null ) + { + if (currentmodel == r_worldmodel) + c_visible_lightmaps++; + + GL_Bind( gl_state.lightmap_textures + i); + + for ( surf = gl_lms.lightmap_surfaces[i]; surf != null; surf = surf.lightmapchain ) + { + if ( surf.polys != null ) + DrawGLPolyChain( surf.polys, 0, 0 ); + } + } + } + + + // TODO impl: render dynamic lightmaps + + /* + ** render dynamic lightmaps + */ + if ( gl_dynamic.value != 0 ) + { + LM_InitBlock(); + + GL_Bind( gl_state.lightmap_textures+0 ); + + if (currentmodel == r_worldmodel) + c_visible_lightmaps++; + + newdrawsurf = gl_lms.lightmap_surfaces[0]; + + for ( surf = gl_lms.lightmap_surfaces[0]; surf != null; surf = surf.lightmapchain ) + { +// int smax, tmax; +// byte *base; +// +// smax = (surf->extents[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 ; k<r_newrefdef.num_dlights ; k++) + { + lt = r_newrefdef.dlights[k]; + R_MarkLights(lt, 1<<k, currentmodel.nodes[currentmodel.firstnode]); + } + } + + // psurf = ¤tmodel->surfaces[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<currentmodel.nummodelsurfaces ; i++) + { + psurf = surfaces[psurfp++]; + // find which side of the node we are on + pplane = psurf.plane; + + dot = Math3D.DotProduct(modelorg, pplane.normal) - pplane.dist; + + // draw the polygon + if (((psurf.flags & Defines.SURF_PLANEBACK) != 0 && (dot < -BACKFACE_EPSILON)) || + ((psurf.flags & Defines.SURF_PLANEBACK) == 0 && (dot > 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<r_worldmodel.numleafs ; i++) + r_worldmodel.leafs[i].visframe = r_visframecount; + for (i=0 ; i<r_worldmodel.numnodes ; i++) + r_worldmodel.nodes[i].visframe = r_visframecount; + return; + } + + vis = Mod_ClusterPVS(r_viewcluster, r_worldmodel); + // may have to combine two clusters because of solid water boundaries + if (r_viewcluster2 != r_viewcluster) + { + // memcpy (fatvis, vis, (r_worldmodel.numleafs+7)/8); + System.arraycopy(vis, 0, fatvis, 0, (r_worldmodel.numleafs+7) / 8); + vis = Mod_ClusterPVS(r_viewcluster2, r_worldmodel); + c = (r_worldmodel.numleafs + 31) / 32; + int k = 0; + for (i=0 ; i<c ; i++) { + fatvis[k] |= vis[k++]; + fatvis[k] |= vis[k++]; + fatvis[k] |= vis[k++]; + fatvis[k] |= vis[k++]; + } + + vis = fatvis; + } + + for ( i=0; i < r_worldmodel.numleafs; i++) + { + leaf = r_worldmodel.leafs[i]; + cluster = leaf.cluster; + if (cluster == -1) + continue; + if (((vis[cluster>>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<BLOCK_WIDTH-w ; i++) + { + best2 = 0; + + for (j=0 ; j<w ; j++) + { + if (gl_lms.allocated[i+j] >= 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<w ; i++) + gl_lms.allocated[x + i] = best + h; + + return true; + } + + /* + ================ + GL_BuildPolygonFromSurface + ================ + */ + void GL_BuildPolygonFromSurface(msurface_t fa) + { + int i, lindex, lnumverts; + medge_t[] pedges; + medge_t r_pedge; + int vertpage; + float[] vec; + float s, t; + glpoly_t poly; + float[] total = {0, 0, 0}; + + // reconstruct the polygon + pedges = currentmodel.edges; + lnumverts = fa.numedges; + vertpage = 0; + + Math3D.VectorClear(total); + // + // draw texture + // + // poly = Hunk_Alloc (sizeof(glpoly_t) + (lnumverts-4) * VERTEXSIZE*sizeof(float)); + poly = new glpoly_t(lnumverts); + + poly.next = fa.polys; + poly.flags = fa.flags; + fa.polys = poly; + poly.numverts = lnumverts; + + for (i=0 ; i<lnumverts ; i++) + { + lindex = currentmodel.surfedges[fa.firstedge + i]; + + if (lindex > 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); + + } + +} |