/*
* 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
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 ; 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);
}
}