Java tutorial
/* * Light.java * Copyright (C) 2003 * * $Id: Light.java,v 1.4 2005/05/07 17:21:42 cawe 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 org.free.jake2.render.lwjgl; import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.util.Arrays; import org.free.jake2.Defines; import org.free.jake2.Globals; import org.free.jake2.client.dlight_t; import org.free.jake2.game.cplane_t; import org.free.jake2.qcommon.Com; import org.free.jake2.render.mnode_t; import org.free.jake2.render.msurface_t; import org.free.jake2.render.mtexinfo_t; import org.free.jake2.util.Math3D; import org.free.jake2.util.Vec3Cache; import org.lwjgl.opengl.GL11; /** * Light * * @author cwei */ public abstract class Light extends Warp { // r_light.c int r_dlightframecount; static final int DLIGHT_CUTOFF = 64; /* ============================================================================= DYNAMIC LIGHTS BLEND RENDERING ============================================================================= */ // stack variable private final float[] v = { 0, 0, 0 }; /** * R_RenderDlight */ void R_RenderDlight(dlight_t light) { float rad = light.intensity * 0.35f; Math3D.VectorSubtract(light.origin, r_origin, v); GL11.glBegin(GL11.GL_TRIANGLE_FAN); GL11.glColor3f(light.color[0] * 0.2f, light.color[1] * 0.2f, light.color[2] * 0.2f); int i; for (i = 0; i < 3; i++) { v[i] = light.origin[i] - vpn[i] * rad; } GL11.glVertex3f(v[0], v[1], v[2]); GL11.glColor3f(0, 0, 0); int j; float a; for (i = 16; i >= 0; i--) { a = (float) (i / 16.0f * Math.PI * 2); for (j = 0; j < 3; j++) { v[j] = (float) (light.origin[j] + vright[j] * Math.cos(a) * rad + vup[j] * Math.sin(a) * rad); } GL11.glVertex3f(v[0], v[1], v[2]); } GL11.glEnd(); } /** * R_RenderDlights */ void R_RenderDlights() { if (gl_flashblend.value == 0) { return; } r_dlightframecount = r_framecount + 1; // because the count hasn't // advanced yet for this frame GL11.glDepthMask(false); GL11.glDisable(GL11.GL_TEXTURE_2D); GL11.glShadeModel(GL11.GL_SMOOTH); GL11.glEnable(GL11.GL_BLEND); GL11.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE); for (int i = 0; i < r_newrefdef.num_dlights; i++) { R_RenderDlight(r_newrefdef.dlights[i]); } GL11.glColor3f(1, 1, 1); GL11.glDisable(GL11.GL_BLEND); GL11.glEnable(GL11.GL_TEXTURE_2D); GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); GL11.glDepthMask(true); } /* ============================================================================= DYNAMIC LIGHTS ============================================================================= */ /** * R_MarkLights */ void R_MarkLights(dlight_t light, int bit, mnode_t node) { if (node.contents != -1) { return; } cplane_t splitplane = node.plane; float dist = Math3D.DotProduct(light.origin, splitplane.normal) - splitplane.dist; if (dist > light.intensity - DLIGHT_CUTOFF) { R_MarkLights(light, bit, node.children[0]); return; } if (dist < -light.intensity + DLIGHT_CUTOFF) { R_MarkLights(light, bit, node.children[1]); return; } // mark the polygons msurface_t surf; int sidebit; for (int i = 0; i < node.numsurfaces; i++) { surf = r_worldmodel.surfaces[node.firstsurface + i]; /* * cwei * bugfix for dlight behind the walls */ dist = Math3D.DotProduct(light.origin, surf.plane.normal) - surf.plane.dist; sidebit = (dist >= 0) ? 0 : Defines.SURF_PLANEBACK; if ((surf.flags & Defines.SURF_PLANEBACK) != sidebit) { continue; } /* * cwei * bugfix end */ if (surf.dlightframe != r_dlightframecount) { surf.dlightbits = 0; surf.dlightframe = r_dlightframecount; } surf.dlightbits |= bit; } R_MarkLights(light, bit, node.children[0]); R_MarkLights(light, bit, node.children[1]); } /** * R_PushDlights */ void R_PushDlights() { if (gl_flashblend.value != 0) { return; } r_dlightframecount = r_framecount + 1; // because the count hasn't // advanced yet for this frame dlight_t l; for (int i = 0; i < r_newrefdef.num_dlights; i++) { l = r_newrefdef.dlights[i]; R_MarkLights(l, 1 << i, r_worldmodel.nodes[0]); } } /* ============================================================================= LIGHT SAMPLING ============================================================================= */ float[] pointcolor = { 0, 0, 0 }; // vec3_t cplane_t lightplane; // used as shadow plane float[] lightspot = { 0, 0, 0 }; // vec3_t /** * RecursiveLightPoint * @param node * @param start * @param end * @return */ int RecursiveLightPoint(mnode_t node, float[] start, float[] end) { if (node.contents != -1) { return -1; // didn't hit anything } // calculate mid point // FIXME: optimize for axial cplane_t plane = node.plane; float front = Math3D.DotProduct(start, plane.normal) - plane.dist; float back = Math3D.DotProduct(end, plane.normal) - plane.dist; boolean side = (front < 0); int sideIndex = (side) ? 1 : 0; if ((back < 0) == side) { return RecursiveLightPoint(node.children[sideIndex], start, end); } float frac = front / (front - back); float[] mid = Vec3Cache.get(); mid[0] = start[0] + (end[0] - start[0]) * frac; mid[1] = start[1] + (end[1] - start[1]) * frac; mid[2] = start[2] + (end[2] - start[2]) * frac; // go down front side int r = RecursiveLightPoint(node.children[sideIndex], start, mid); if (r >= 0) { Vec3Cache.release(); // mid return r; // hit something } if ((back < 0) == side) { Vec3Cache.release(); // mid return -1; // didn't hit anuthing } // check for impact on this node Math3D.VectorCopy(mid, lightspot); lightplane = plane; int surfIndex = node.firstsurface; msurface_t surf; int s, t, ds, dt; mtexinfo_t tex1; ByteBuffer lightmap; int maps; for (int i = 0; i < node.numsurfaces; i++, surfIndex++) { surf = r_worldmodel.surfaces[surfIndex]; if ((surf.flags & (Defines.SURF_DRAWTURB | Defines.SURF_DRAWSKY)) != 0) { continue; // no lightmaps } tex1 = surf.texinfo; s = (int) (Math3D.DotProduct(mid, tex1.vecs[0]) + tex1.vecs[0][3]); t = (int) (Math3D.DotProduct(mid, tex1.vecs[1]) + tex1.vecs[1][3]); if (s < surf.texturemins[0] || t < surf.texturemins[1]) { continue; } ds = s - surf.texturemins[0]; dt = t - surf.texturemins[1]; if (ds > surf.extents[0] || dt > surf.extents[1]) { continue; } if (surf.samples == null) { return 0; } ds >>= 4; dt >>= 4; lightmap = surf.samples; int lightmapIndex = 0; Math3D.VectorCopy(Globals.vec3_origin, pointcolor); if (lightmap != null) { float[] rgb; lightmapIndex += 3 * (dt * ((surf.extents[0] >> 4) + 1) + ds); float scale0, scale1, scale2; for (maps = 0; maps < Defines.MAXLIGHTMAPS && surf.styles[maps] != (byte) 255; maps++) { rgb = r_newrefdef.lightstyles[surf.styles[maps] & 0xFF].rgb; scale0 = gl_modulate.value * rgb[0]; scale1 = gl_modulate.value * rgb[1]; scale2 = gl_modulate.value * rgb[2]; pointcolor[0] += (lightmap.get(lightmapIndex + 0) & 0xFF) * scale0 * (1.0f / 255); pointcolor[1] += (lightmap.get(lightmapIndex + 1) & 0xFF) * scale1 * (1.0f / 255); pointcolor[2] += (lightmap.get(lightmapIndex + 2) & 0xFF) * scale2 * (1.0f / 255); lightmapIndex += 3 * ((surf.extents[0] >> 4) + 1) * ((surf.extents[1] >> 4) + 1); } } Vec3Cache.release(); // mid return 1; } // go down back side r = RecursiveLightPoint(node.children[1 - sideIndex], mid, end); Vec3Cache.release(); // mid return r; } // stack variable private final float[] end = { 0, 0, 0 }; /** * R_LightPoint */ void R_LightPoint(float[] p, float[] color) { assert (p.length == 3) : "vec3_t bug"; assert (color.length == 3) : "rgb bug"; if (r_worldmodel.lightdata == null) { color[0] = color[1] = color[2] = 1.0f; return; } end[0] = p[0]; end[1] = p[1]; end[2] = p[2] - 2048; float r = RecursiveLightPoint(r_worldmodel.nodes[0], p, end); if (r == -1) { Math3D.VectorCopy(Globals.vec3_origin, color); } else { Math3D.VectorCopy(pointcolor, color); } // // add dynamic lights // dlight_t dl; float add; for (int lnum = 0; lnum < r_newrefdef.num_dlights; lnum++) { dl = r_newrefdef.dlights[lnum]; Math3D.VectorSubtract(currententity.origin, dl.origin, end); add = dl.intensity - Math3D.VectorLength(end); add *= (1.0f / 256); if (add > 0) { Math3D.VectorMA(color, add, dl.color, color); } } Math3D.VectorScale(color, gl_modulate.value, color); } // =================================================================== float[] s_blocklights = new float[34 * 34 * 3]; // TODO sync with jogl renderer. hoz private final float[] impact = { 0, 0, 0 }; /** * R_AddDynamicLights */ void R_AddDynamicLights(msurface_t surf) { int sd, td; float fdist, frad, fminlight; int s, t; dlight_t dl; float[] pfBL; float fsacc, ftacc; int smax = (surf.extents[0] >> 4) + 1; int tmax = (surf.extents[1] >> 4) + 1; mtexinfo_t tex1 = surf.texinfo; float local0, local1; for (int lnum = 0; lnum < r_newrefdef.num_dlights; lnum++) { if ((surf.dlightbits & (1 << lnum)) == 0) { continue; // not lit by this light } dl = r_newrefdef.dlights[lnum]; frad = dl.intensity; fdist = Math3D.DotProduct(dl.origin, surf.plane.normal) - surf.plane.dist; frad -= Math.abs(fdist); // rad is now the highest intensity on the plane fminlight = DLIGHT_CUTOFF; // FIXME: make configurable? if (frad < fminlight) { continue; } fminlight = frad - fminlight; for (int i = 0; i < 3; i++) { impact[i] = dl.origin[i] - surf.plane.normal[i] * fdist; } local0 = Math3D.DotProduct(impact, tex1.vecs[0]) + tex1.vecs[0][3] - surf.texturemins[0]; local1 = Math3D.DotProduct(impact, tex1.vecs[1]) + tex1.vecs[1][3] - surf.texturemins[1]; pfBL = s_blocklights; int pfBLindex = 0; for (t = 0, ftacc = 0; t < tmax; t++, ftacc += 16) { td = (int) (local1 - ftacc); if (td < 0) { td = -td; } for (s = 0, fsacc = 0; s < smax; s++, fsacc += 16, pfBLindex += 3) { sd = (int) (local0 - fsacc); if (sd < 0) { sd = -sd; } if (sd > td) { fdist = sd + (td >> 1); } else { fdist = td + (sd >> 1); } if (fdist < fminlight) { pfBL[pfBLindex + 0] += (frad - fdist) * dl.color[0]; pfBL[pfBLindex + 1] += (frad - fdist) * dl.color[1]; pfBL[pfBLindex + 2] += (frad - fdist) * dl.color[2]; } } } } } /** * R_SetCacheState */ void R_SetCacheState(msurface_t surf) { for (int maps = 0; maps < Defines.MAXLIGHTMAPS && surf.styles[maps] != (byte) 255; maps++) { surf.cached_light[maps] = r_newrefdef.lightstyles[surf.styles[maps] & 0xFF].white; } } private Throwable gotoStore = new Throwable(); // TODO sync with jogl renderer. hoz /** * R_BuildLightMap * * Combine and scale multiple lightmaps into the floating format in blocklights */ void R_BuildLightMap(msurface_t surf, IntBuffer dest, int stride) { int r, g, b, a, max; int i, j; int nummaps; float[] bl; //lightstyle_t style; if ((surf.texinfo.flags & (Defines.SURF_SKY | Defines.SURF_TRANS33 | Defines.SURF_TRANS66 | Defines.SURF_WARP)) != 0) { Com.Error(Defines.ERR_DROP, "R_BuildLightMap called for non-lit surface"); } int smax = (surf.extents[0] >> 4) + 1; int tmax = (surf.extents[1] >> 4) + 1; int size = smax * tmax; if (size > ((s_blocklights.length * Defines.SIZE_OF_FLOAT) >> 4)) { Com.Error(Defines.ERR_DROP, "Bad s_blocklights size"); } try { // set to full bright if no light data if (surf.samples == null) { int maps; for (i = 0; i < size * 3; i++) { s_blocklights[i] = 255; } // TODO useless? hoz // for (maps = 0 ; maps < Defines.MAXLIGHTMAPS && // surf.styles[maps] != (byte)255; maps++) // { // style = r_newrefdef.lightstyles[surf.styles[maps] & 0xFF]; // } // goto store; throw gotoStore; } // count the # of maps for (nummaps = 0; nummaps < Defines.MAXLIGHTMAPS && surf.styles[nummaps] != (byte) 255; nummaps++) ; ByteBuffer lightmap = surf.samples; int lightmapIndex = 0; // add all the lightmaps float scale0; float scale1; float scale2; if (nummaps == 1) { int maps; for (maps = 0; maps < Defines.MAXLIGHTMAPS && surf.styles[maps] != (byte) 255; maps++) { bl = s_blocklights; int blp = 0; // for (i = 0; i < 3; i++) // scale[i] = gl_modulate.value // * r_newrefdef.lightstyles[surf.styles[maps] & 0xFF].rgb[i]; scale0 = gl_modulate.value * r_newrefdef.lightstyles[surf.styles[maps] & 0xFF].rgb[0]; scale1 = gl_modulate.value * r_newrefdef.lightstyles[surf.styles[maps] & 0xFF].rgb[1]; scale2 = gl_modulate.value * r_newrefdef.lightstyles[surf.styles[maps] & 0xFF].rgb[2]; if (scale0 == 1.0F && scale1 == 1.0F && scale2 == 1.0F) { for (i = 0; i < size; i++) { bl[blp++] = lightmap.get(lightmapIndex++) & 0xFF; bl[blp++] = lightmap.get(lightmapIndex++) & 0xFF; bl[blp++] = lightmap.get(lightmapIndex++) & 0xFF; } } else { for (i = 0; i < size; i++) { bl[blp++] = (lightmap.get(lightmapIndex++) & 0xFF) * scale0; bl[blp++] = (lightmap.get(lightmapIndex++) & 0xFF) * scale1; bl[blp++] = (lightmap.get(lightmapIndex++) & 0xFF) * scale2; } } //lightmap += size*3; // skip to next lightmap } } else { int maps; // memset( s_blocklights, 0, sizeof( s_blocklights[0] ) * size * // 3 ); Arrays.fill(s_blocklights, 0, size * 3, 0.0f); for (maps = 0; maps < Defines.MAXLIGHTMAPS && surf.styles[maps] != (byte) 255; maps++) { bl = s_blocklights; int blp = 0; // for (i = 0; i < 3; i++) // scale[i] = gl_modulate.value // * r_newrefdef.lightstyles[surf.styles[maps] & 0xFF].rgb[i]; scale0 = gl_modulate.value * r_newrefdef.lightstyles[surf.styles[maps] & 0xFF].rgb[0]; scale1 = gl_modulate.value * r_newrefdef.lightstyles[surf.styles[maps] & 0xFF].rgb[1]; scale2 = gl_modulate.value * r_newrefdef.lightstyles[surf.styles[maps] & 0xFF].rgb[2]; if (scale0 == 1.0F && scale1 == 1.0F && scale2 == 1.0F) { for (i = 0; i < size; i++) { bl[blp++] += lightmap.get(lightmapIndex++) & 0xFF; bl[blp++] += lightmap.get(lightmapIndex++) & 0xFF; bl[blp++] += lightmap.get(lightmapIndex++) & 0xFF; } } else { for (i = 0; i < size; i++) { bl[blp++] += (lightmap.get(lightmapIndex++) & 0xFF) * scale0; bl[blp++] += (lightmap.get(lightmapIndex++) & 0xFF) * scale1; bl[blp++] += (lightmap.get(lightmapIndex++) & 0xFF) * scale2; } } //lightmap += size*3; // skip to next lightmap } } // add all the dynamic lights if (surf.dlightframe == r_framecount) { R_AddDynamicLights(surf); } // label store: } catch (Throwable store) { } // put into texture format stride -= smax; bl = s_blocklights; int blp = 0; int monolightmap = gl_monolightmap.string.charAt(0); int destp = 0; if (monolightmap == '0') { for (i = 0; i < tmax; i++, destp += stride) { //dest.position(destp); for (j = 0; j < smax; j++) { r = (int) bl[blp++]; g = (int) bl[blp++]; b = (int) bl[blp++]; // catch negative lights if (r < 0) { r = 0; } if (g < 0) { g = 0; } if (b < 0) { b = 0; } /* * * determine the brightest of the three color components */ if (r > g) { max = r; } else { max = g; } if (b > max) { max = b; } /* * * alpha is ONLY used for the mono lightmap case. For this * reason * we set it to the brightest of the color * components so that * things don't get too dim. */ a = max; /* * * rescale all the color components if the intensity of * the greatest * channel exceeds 1.0 */ if (max > 255) { float t = 255.0F / max; r = (int) (r * t); g = (int) (g * t); b = (int) (b * t); a = (int) (a * t); } //r &= 0xFF; g &= 0xFF; b &= 0xFF; a &= 0xFF; dest.put(destp++, (a << 24) | (b << 16) | (g << 8) | r); } } } else { for (i = 0; i < tmax; i++, destp += stride) { //dest.position(destp); for (j = 0; j < smax; j++) { r = (int) bl[blp++]; g = (int) bl[blp++]; b = (int) bl[blp++]; // catch negative lights if (r < 0) { r = 0; } if (g < 0) { g = 0; } if (b < 0) { b = 0; } /* * * determine the brightest of the three color components */ if (r > g) { max = r; } else { max = g; } if (b > max) { max = b; } /* * * alpha is ONLY used for the mono lightmap case. For this * reason * we set it to the brightest of the color * components so that * things don't get too dim. */ a = max; /* * * rescale all the color components if the intensity of * the greatest * channel exceeds 1.0 */ if (max > 255) { float t = 255.0F / max; r = (int) (r * t); g = (int) (g * t); b = (int) (b * t); a = (int) (a * t); } /* * * So if we are doing alpha lightmaps we need to set the * R, G, and B * components to 0 and we need to set alpha to * 1-alpha. */ switch (monolightmap) { case 'L': case 'I': r = a; g = b = 0; break; case 'C': // try faking colored lighting a = 255 - ((r + g + b) / 3); float af = a / 255.0f; r *= af; g *= af; b *= af; break; case 'A': default: r = g = b = 0; a = 255 - a; break; } //r &= 0xFF; g &= 0xFF; b &= 0xFF; a &= 0xFF; dest.put(destp++, (a << 24) | (b << 16) | (g << 8) | r); } } } } }