/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Modifications Copyright 2003-2004 Bytonic Software Copyright 2010 Google Inc. */ package com.googlecode.gwtquake.shared.render; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.util.Arrays; import com.googlecode.gwtquake.shared.client.EntityType; import com.googlecode.gwtquake.shared.client.Lightstyle; import com.googlecode.gwtquake.shared.common.Com; import com.googlecode.gwtquake.shared.common.Constants; import com.googlecode.gwtquake.shared.game.Plane; import com.googlecode.gwtquake.shared.util.Lib; import com.googlecode.gwtquake.shared.util.Math3D; import com.googlecode.gwtquake.shared.util.Vec3Cache; /** * Surf * * @author cwei */ public abstract class Surfaces { // GL_RSURF.C: surface-related refresh code static float[] modelorg = { 0, 0, 0 }; // relative to viewpoint static Surface 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; static int c_visible_lightmaps; static int c_visible_textures; static int staticBufferId; public static final int GL_LIGHTMAP_FORMAT = Gl1Context.GL_RGBA; public static class LightMapState { int internal_format; int current_lightmap_texture; public Surface[] lightmap_surfaces = new Surface[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]; IntBuffer lightmap_buffer = Lib.newIntBuffer(BLOCK_WIDTH * BLOCK_HEIGHT, ByteOrder.LITTLE_ENDIAN); public LightMapState() { for (int i = 0; i < MAX_LIGHTMAPS; i++) lightmap_surfaces[i] = new Surface(); } public void clearLightmapSurfaces() { for (int i = 0; i < MAX_LIGHTMAPS; i++) // TODO lightmap_surfaces[i].clear(); lightmap_surfaces[i] = new Surface(); } } public static LightMapState gl_lms = new LightMapState(); /* * ============================================================= * * BRUSH MODELS * * ============================================================= */ /** * R_DrawTriangleOutlines */ static void R_DrawTriangleOutlines() { if (GlConfig.gl_showtris.value == 0) return; GlState.gl.glDisable(Gl1Context.GL_TEXTURE_2D); GlState.gl.glDisable(Gl1Context.GL_DEPTH_TEST); GlState.gl.glColor4f(1, 1, 1, 1); Surface surf; Polygon p; int j; for (int i = 0; i < MAX_LIGHTMAPS; i++) { for (surf = gl_lms.lightmap_surfaces[i]; surf != null; surf = surf.lightmapchain) { for (p = surf.polys; p != null; p = p.chain) { for (j = 2; j < p.numverts; j++) { GlState.gl.glBegin(Gl1Context.GL_LINE_STRIP); GlState.gl.glVertex3f(p.getX(0), p.getY(0), p.getZ(0)); GlState.gl.glVertex3f(p.getX(j - 1), p.getY(j - 1), p.getZ(j - 1)); GlState.gl.glVertex3f(p.getX(j), p.getY(j), p.getZ(j)); GlState.gl.glVertex3f(p.getX(0), p.getY(0), p.getZ(0)); GlState.gl.glEnd(); } } } } GlState.gl.glEnable(Gl1Context.GL_DEPTH_TEST); GlState.gl.glEnable(Gl1Context.GL_TEXTURE_2D); } public static final IntBuffer temp2 = Lib.newIntBuffer(34 * 34, ByteOrder.LITTLE_ENDIAN); /** * 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. */ static void R_DrawAlphaSurfaces() { GlState.r_world_matrix.clear(); // // go back to the world matrix // GlState.gl.glLoadMatrix(GlState.r_world_matrix); GlState.gl.glEnable(Gl1Context.GL_BLEND); Images.GL_TexEnv(Gl1Context.GL_MODULATE); // the textures are prescaled up for a better lighting range, // so scale it back down float intens = GlConfig.gl_state.inverse_intensity; glInterleavedArraysT2F_V3F(Polygons.BYTE_STRIDE, globalPolygonInterleavedBuf, staticBufferId); for (Surface s = r_alpha_surfaces; s != null; s = s.texturechain) { Images.GL_Bind(s.texinfo.image.texnum); GlState.c_brush_polys++; if ((s.texinfo.flags & Constants.SURF_TRANS33) != 0) GlState.gl.glColor4f(intens, intens, intens, 0.33f); else if ((s.texinfo.flags & Constants.SURF_TRANS66) != 0) GlState.gl.glColor4f(intens, intens, intens, 0.66f); else GlState.gl.glColor4f(intens, intens, intens, 1); if ((s.flags & Constants.SURF_DRAWTURB) != 0) Surface.EmitWaterPolys(s); else if ((s.texinfo.flags & Constants.SURF_FLOWING) != 0) // PGM 9/16/98 s.polys.drawFlowing(); // PGM else s.polys.draw(); } Images.GL_TexEnv(Gl1Context.GL_REPLACE); GlState.gl.glColor4f(1, 1, 1, 1); GlState.gl.glDisable(Gl1Context.GL_BLEND); r_alpha_surfaces = null; } private static void glInterleavedArraysT2F_V3F(int byteStride, FloatBuffer buf) { int pos = buf.position(); GlState.gl.glTexCoordPointer(2, byteStride, buf); GlState.gl.glEnableClientState(Gl1Context.GL_TEXTURE_COORD_ARRAY); buf.position(pos + 2); GlState.gl.glVertexPointer(3, byteStride, buf); GlState.gl.glEnableClientState(Gl1Context.GL_VERTEX_ARRAY); buf.position(pos); } private static void glInterleavedArraysT2F_V3F(int byteStride, FloatBuffer buf, int staticDrawIdV) { GlState.gl.glEnableClientState(Gl1Context.GL_TEXTURE_COORD_ARRAY); GlState.gl.glVertexAttribPointer(Gl1Context.ARRAY_TEXCOORD_0, 2, Gl1Context.GL_FLOAT, false, byteStride, 0, buf, staticDrawIdV); GlState.gl.glEnableClientState(Gl1Context.GL_VERTEX_ARRAY); // gl.glVertexPointer(3, byteStride, buf); GlState.gl.glVertexAttribPointer(Gl1Context.ARRAY_POSITION, 3, Gl1Context.GL_FLOAT, false, byteStride, 8, buf, staticDrawIdV); } /** * DrawTextureChains */ static void DrawTextureChains() { c_visible_textures = 0; Surface s; Image image; int i; for (i = 0; i < Images.numgltextures; i++) { image = Images.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 & Constants.SURF_DRAWTURB) == 0) Surface.R_RenderBrushPoly(s); } } Images.GL_EnableMultitexture(false); for (i = 0; i < Images.numgltextures; i++) { image = Images.gltextures[i]; if (image.registration_sequence == 0) continue; s = image.texturechain; if (s == null) continue; for (; s != null; s = s.texturechain) { if ((s.flags & Constants.SURF_DRAWTURB) != 0) Surface.R_RenderBrushPoly(s); } image.texturechain = null; } Images.GL_TexEnv(Gl1Context.GL_REPLACE); } // direct buffer static final IntBuffer temp = Lib.newIntBuffer(128 * 128, ByteOrder.LITTLE_ENDIAN); /** * R_DrawInlineBModel */ static void R_DrawInlineBModel() { // calculate dynamic lighting for bmodel if (GlConfig.gl_flashblend.value == 0) { DynamicLight lt; for (int k = 0; k < GlState.r_newrefdef.num_dlights; k++) { lt = GlState.r_newrefdef.dlights[k]; lt.mark(1 << k, GlState.currentmodel.nodes[GlState.currentmodel.firstnode]); } } // psurf = ¤tmodel->surfaces[currentmodel->firstmodelsurface]; int psurfp = GlState.currentmodel.firstmodelsurface; Surface[] surfaces = GlState.currentmodel.surfaces; // psurf = surfaces[psurfp]; if ((GlState.currententity.flags & Constants.RF_TRANSLUCENT) != 0) { GlState.gl.glEnable(Gl1Context.GL_BLEND); GlState.gl.glColor4f(1, 1, 1, 0.25f); Images.GL_TexEnv(Gl1Context.GL_MODULATE); } // // draw texture // Surface psurf; Plane pplane; float dot; for (int i = 0; i < GlState.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 & Constants.SURF_PLANEBACK) != 0 && (dot < -GlConstants.BACKFACE_EPSILON)) || ((psurf.flags & Constants.SURF_PLANEBACK) == 0 && (dot > GlConstants.BACKFACE_EPSILON))) { if ((psurf.texinfo.flags & (Constants.SURF_TRANS33 | Constants.SURF_TRANS66)) != 0) { // add // to // the // translucent // chain psurf.texturechain = r_alpha_surfaces; r_alpha_surfaces = psurf; } else if ((psurf.flags & Constants.SURF_DRAWTURB) == 0) { Surface.GL_RenderLightmappedPoly(psurf); } else { Images.GL_EnableMultitexture(false); Surface.R_RenderBrushPoly(psurf); Images.GL_EnableMultitexture(true); } } } if ((GlState.currententity.flags & Constants.RF_TRANSLUCENT) != 0) { GlState.gl.glDisable(Gl1Context.GL_BLEND); GlState.gl.glColor4f(1, 1, 1, 1); Images.GL_TexEnv(Gl1Context.GL_REPLACE); } } // stack variable private static final float[] mins = { 0, 0, 0 }; private static final float[] maxs = { 0, 0, 0 }; private static final float[] org = { 0, 0, 0 }; private static final float[] forward = { 0, 0, 0 }; private static final float[] right = { 0, 0, 0 }; private static final float[] up = { 0, 0, 0 }; /** * R_DrawBrushModel */ static void R_DrawBrushModel(EntityType e) { if (GlState.currentmodel.nummodelsurfaces == 0) return; GlState.currententity = e; GlConfig.gl_state.currenttextures[0] = GlConfig.gl_state.currenttextures[1] = -1; boolean rotated; if (e.angles[0] != 0 || e.angles[1] != 0 || e.angles[2] != 0) { rotated = true; for (int i = 0; i < 3; i++) { mins[i] = e.origin[i] - GlState.currentmodel.radius; maxs[i] = e.origin[i] + GlState.currentmodel.radius; } } else { rotated = false; Math3D.VectorAdd(e.origin, GlState.currentmodel.mins, mins); Math3D.VectorAdd(e.origin, GlState.currentmodel.maxs, maxs); } if (Entities.R_CullBox(mins, maxs)) return; GlState.gl.glColor3f(1, 1, 1); // memset (gl_lms.lightmap_surfaces, 0, sizeof(gl_lms.lightmap_surfaces)); // TODO wird beim multitexturing nicht gebraucht // gl_lms.clearLightmapSurfaces(); Math3D.VectorSubtract(GlState.r_newrefdef.vieworg, e.origin, modelorg); if (rotated) { Math3D.VectorCopy(modelorg, org); Math3D.AngleVectors(e.angles, forward, right, up); modelorg[0] = Math3D.DotProduct(org, forward); modelorg[1] = -Math3D.DotProduct(org, right); modelorg[2] = Math3D.DotProduct(org, up); } GlState.gl.glPushMatrix(); e.angles[0] = -e.angles[0]; // stupid quake bug e.angles[2] = -e.angles[2]; // stupid quake bug Entities.rotateForEntity(e); e.angles[0] = -e.angles[0]; // stupid quake bug e.angles[2] = -e.angles[2]; // stupid quake bug Images.GL_EnableMultitexture(true); Images.GL_SelectTexture(Gl1Context.GL_TEXTURE0); Images.GL_TexEnv(Gl1Context.GL_REPLACE); glInterleavedArraysT2F_V3F(Polygons.BYTE_STRIDE, globalPolygonInterleavedBuf, staticBufferId); Images.GL_SelectTexture(Gl1Context.GL_TEXTURE1); Images.GL_TexEnv(Gl1Context.GL_MODULATE); // gl.glTexCoordPointer(2, Polygon.BYTE_STRIDE, globalPolygonTexCoord1Buf); GlState.gl.glEnableClientState(Gl1Context.GL_TEXTURE_COORD_ARRAY); GlState.gl.glVertexAttribPointer(Gl1Context.ARRAY_TEXCOORD_1, 2, Gl1Context.GL_FLOAT, false, Polygons.BYTE_STRIDE, 20, globalPolygonInterleavedBuf, staticBufferId); R_DrawInlineBModel(); GlState.gl.glClientActiveTexture(Gl1Context.GL_TEXTURE1); GlState.gl.glDisableClientState(Gl1Context.GL_TEXTURE_COORD_ARRAY); Images.GL_EnableMultitexture(false); GlState.gl.glPopMatrix(); } /* * ============================================================= * * WORLD MODEL * * ============================================================= */ private static final EntityType worldEntity = new EntityType(); /** * R_DrawWorld */ static void R_DrawWorld() { if (GlConfig.r_drawworld.value == 0) return; if ((GlState.r_newrefdef.rdflags & Constants.RDF_NOWORLDMODEL) != 0) return; GlState.currentmodel = GlState.r_worldmodel; Math3D.VectorCopy(GlState.r_newrefdef.vieworg, modelorg); EntityType ent = worldEntity; // auto cycle the world frame for texture animation ent.clear(); ent.frame = (int) (GlState.r_newrefdef.time * 2); GlState.currententity = ent; GlConfig.gl_state.currenttextures[0] = GlConfig.gl_state.currenttextures[1] = -1; GlState.gl.glColor3f(1, 1, 1); // memset (gl_lms.lightmap_surfaces, 0, sizeof(gl_lms.lightmap_surfaces)); // TODO wird bei multitexture nicht gebraucht // gl_lms.clearLightmapSurfaces(); SkyBox.R_ClearSkyBox(); Images.GL_EnableMultitexture(true); Images.GL_SelectTexture(Gl1Context.GL_TEXTURE0); Images.GL_TexEnv(Gl1Context.GL_REPLACE); // glInterleavedArraysT2F_V3F(Polygon.BYTE_STRIDE, // globalPolygonInterleavedBuf); glInterleavedArraysT2F_V3F(Polygons.BYTE_STRIDE, globalPolygonInterleavedBuf, staticBufferId); Images.GL_SelectTexture(Gl1Context.GL_TEXTURE1); GlState.gl.glEnableClientState(Gl1Context.GL_TEXTURE_COORD_ARRAY); GlState.gl.glVertexAttribPointer(Gl1Context.ARRAY_TEXCOORD_1, 2, Gl1Context.GL_FLOAT, false, Polygons.BYTE_STRIDE, 20, globalPolygonInterleavedBuf, staticBufferId); // gl.glTexCoordPointer(2, Polygon.BYTE_STRIDE, globalPolygonTexCoord1Buf); if (GlConfig.gl_lightmap.value != 0) Images.GL_TexEnv(Gl1Context.GL_REPLACE); else Images.GL_TexEnv(Gl1Context.GL_MODULATE); Node.R_RecursiveWorldNode(GlState.r_worldmodel.nodes[0]); // root node GlState.gl.glClientActiveTexture(Gl1Context.GL_TEXTURE1); GlState.gl.glDisableClientState(Gl1Context.GL_TEXTURE_COORD_ARRAY); Images.GL_EnableMultitexture(false); DrawTextureChains(); SkyBox.R_DrawSkyBox(); R_DrawTriangleOutlines(); } final static byte[] fatvis = new byte[Constants.MAX_MAP_LEAFS / 8]; /** * R_MarkLeaves Mark the leaves and nodes that are in the PVS for the current * cluster */ static void R_MarkLeaves() { if (GlState.r_oldviewcluster == GlState.r_viewcluster && GlState.r_oldviewcluster2 == GlState.r_viewcluster2 && GlConfig.r_novis.value == 0 && GlState.r_viewcluster != -1) return; // development aid to let you run around and see exactly where // the pvs ends if (GlConfig.gl_lockpvs.value != 0) return; GlState.r_visframecount++; GlState.r_oldviewcluster = GlState.r_viewcluster; GlState.r_oldviewcluster2 = GlState.r_viewcluster2; int i; if (GlConfig.r_novis.value != 0 || GlState.r_viewcluster == -1 || GlState.r_worldmodel.vis == null) { // mark everything for (i = 0; i < GlState.r_worldmodel.numleafs; i++) GlState.r_worldmodel.leafs[i].visframe = GlState.r_visframecount; for (i = 0; i < GlState.r_worldmodel.numnodes; i++) GlState.r_worldmodel.nodes[i].visframe = GlState.r_visframecount; return; } byte[] vis = Models.Mod_ClusterPVS(GlState.r_viewcluster, GlState.r_worldmodel); int c; // may have to combine two clusters because of solid water boundaries if (GlState.r_viewcluster2 != GlState.r_viewcluster) { // memcpy (fatvis, vis, (r_worldmodel.numleafs+7)/8); System.arraycopy(vis, 0, fatvis, 0, (GlState.r_worldmodel.numleafs + 7) >> 3); vis = Models.Mod_ClusterPVS(GlState.r_viewcluster2, GlState.r_worldmodel); c = (GlState.r_worldmodel.numleafs + 31) >> 5; c <<= 2; for (int k = 0; k < c; k += 4) { fatvis[k] |= vis[k]; fatvis[k + 1] |= vis[k + 1]; fatvis[k + 2] |= vis[k + 2]; fatvis[k + 3] |= vis[k + 3]; } vis = fatvis; } Node node; Leaf leaf; int cluster; for (i = 0; i < GlState.r_worldmodel.numleafs; i++) { leaf = GlState.r_worldmodel.leafs[i]; cluster = leaf.cluster; if (cluster == -1) continue; if (((vis[cluster >> 3] & 0xFF) & (1 << (cluster & 7))) != 0) { node = (Node) leaf; do { if (node.visframe == GlState.r_visframecount) break; node.visframe = GlState.r_visframecount; node = node.parent; } while (node != null); } } } /* * ============================================================================ * = * * LIGHTMAP ALLOCATION * * ============================================================================ * = */ /** * LM_InitBlock */ static void LM_InitBlock() { Arrays.fill(gl_lms.allocated, 0); } /** * LM_UploadBlock * * @param dynamic */ static void LM_UploadBlock(boolean dynamic) { int texture = (dynamic) ? 0 : gl_lms.current_lightmap_texture; Images.GL_Bind(GlConfig.gl_state.lightmap_textures + texture); GlState.gl.glTexParameterf(Gl1Context.GL_TEXTURE_2D, Gl1Context.GL_TEXTURE_MIN_FILTER, Gl1Context.GL_LINEAR); GlState.gl.glTexParameterf(Gl1Context.GL_TEXTURE_2D, Gl1Context.GL_TEXTURE_MAG_FILTER, Gl1Context.GL_LINEAR); gl_lms.lightmap_buffer.rewind(); if (dynamic) { int height = 0; for (int i = 0; i < BLOCK_WIDTH; i++) { if (gl_lms.allocated[i] > height) height = gl_lms.allocated[i]; } GlState.gl.glTexSubImage2D(Gl1Context.GL_TEXTURE_2D, 0, 0, 0, BLOCK_WIDTH, height, GL_LIGHTMAP_FORMAT, Gl1Context.GL_UNSIGNED_BYTE, gl_lms.lightmap_buffer); } else { GlState.gl.glTexImage2D(Gl1Context.GL_TEXTURE_2D, 0, GL_LIGHTMAP_FORMAT/* gl_lms.internal_format */, BLOCK_WIDTH, BLOCK_HEIGHT, 0, GL_LIGHTMAP_FORMAT, Gl1Context.GL_UNSIGNED_BYTE, gl_lms.lightmap_buffer); if (++gl_lms.current_lightmap_texture == MAX_LIGHTMAPS) Com.Error(Constants.ERR_DROP, "LM_UploadBlock() - MAX_LIGHTMAPS exceeded\n"); // debugLightmap(gl_lms.lightmap_buffer, 128, 128, 4); } } /** * LM_AllocBlock * * @param w * @param h * @param pos * @return a texture number and the position inside it */ static boolean LM_AllocBlock(int w, int h, Images.pos_t pos) { int best = BLOCK_HEIGHT; int x = pos.x; int best2; int i, j; 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 = best = best2; } } if (best + h > BLOCK_HEIGHT) return false; for (i = 0; i < w; i++) gl_lms.allocated[x + i] = best + h; return true; } static Lightstyle[] lightstyles; static IntBuffer dummy; /** * GL_EndBuildingLightmaps */ static void GL_EndBuildingLightmaps() { LM_UploadBlock(false); Images.GL_EnableMultitexture(false); } /* * new buffers for vertex array handling */ static FloatBuffer globalPolygonInterleavedBuf = Polygons.getRewoundBuffer(); static FloatBuffer globalPolygonTexCoord1Buf = null; static { globalPolygonInterleavedBuf.position(Polygons.STRIDE - 2); globalPolygonTexCoord1Buf = globalPolygonInterleavedBuf.slice(); globalPolygonInterleavedBuf.position(0); }; // 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); // // } protected static void debugLightmap(IntBuffer lightmapBuffer, int w, int h, float scale) { GlState.gl.log("debuglightmap"); } static float[] s_blocklights = new float[34 * 34 * 3]; /** * SubdividePolygon * * @param numverts * @param verts */ static void SubdividePolygon(Surface warp, int numverts, float[][] verts) { int i, j, k; float m; float[][] front = new float[64][3]; float[][] back = new float[64][3]; int f, b; float[] dist = new float[64]; float frac; if (numverts > 60) Com.Error(Constants.ERR_DROP, "numverts = " + numverts); float[] mins = Vec3Cache.get(); float[] maxs = Vec3Cache.get(); SkyBox.BoundPoly(numverts, verts, mins, maxs); float[] v; // x,y und z for (i = 0; i < 3; i++) { m = (mins[i] + maxs[i]) * 0.5f; m = GlConstants.SUBDIVIDE_SIZE * (float) Math.floor(m / GlConstants.SUBDIVIDE_SIZE + 0.5f); if (maxs[i] - m < 8) continue; if (m - mins[i] < 8) continue; // cut it for (j = 0; j < numverts; j++) { dist[j] = verts[j][i] - m; } // wrap cases dist[j] = dist[0]; Math3D.VectorCopy(verts[0], verts[numverts]); f = b = 0; for (j = 0; j < numverts; j++) { v = verts[j]; if (dist[j] >= 0) { Math3D.VectorCopy(v, front[f]); f++; } if (dist[j] <= 0) { Math3D.VectorCopy(v, back[b]); b++; } if (dist[j] == 0 || dist[j + 1] == 0) continue; if ((dist[j] > 0) != (dist[j + 1] > 0)) { // clip point frac = dist[j] / (dist[j] - dist[j + 1]); for (k = 0; k < 3; k++) front[f][k] = back[b][k] = v[k] + frac * (verts[j + 1][k] - v[k]); f++; b++; } } SubdividePolygon(warp, f, front); SubdividePolygon(warp, b, back); Vec3Cache.release(2); // mins, maxs return; } Vec3Cache.release(2); // mins, maxs // add a point in the center to help keep warp valid // wird im Konstruktor erschlagen // poly = Hunk_Alloc (sizeof(glpoly_t) + ((numverts-4)+2) * // VERTEXSIZE*sizeof(float)); // init polys Polygon poly = Polygons.create(numverts + 2); poly.next = warp.polys; warp.polys = poly; float[] total = Vec3Cache.get(); Math3D.VectorClear(total); float total_s = 0; float total_t = 0; float s, t; for (i = 0; i < numverts; i++) { poly.setX(i + 1, verts[i][0]); poly.setY(i + 1, verts[i][1]); poly.setZ(i + 1, verts[i][2]); s = Math3D.DotProduct(verts[i], warp.texinfo.vecs[0]); t = Math3D.DotProduct(verts[i], warp.texinfo.vecs[1]); total_s += s; total_t += t; Math3D.VectorAdd(total, verts[i], total); poly.setS1(i + 1, s); poly.setT1(i + 1, t); } float scale = 1.0f / numverts; poly.setX(0, total[0] * scale); poly.setY(0, total[1] * scale); poly.setZ(0, total[2] * scale); poly.setS1(0, total_s * scale); poly.setT1(0, total_t * scale); poly.setX(i + 1, poly.getX(1)); poly.setY(i + 1, poly.getY(1)); poly.setZ(i + 1, poly.getZ(1)); poly.setS1(i + 1, poly.getS1(1)); poly.setT1(i + 1, poly.getT1(1)); poly.setS2(i + 1, poly.getS2(1)); poly.setT2(i + 1, poly.getT2(1)); Vec3Cache.release(); // total } //static Surface warpface; }