/* 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 com.googlecode.gwtquake.shared.common.Com; import com.googlecode.gwtquake.shared.common.Constants; import com.googlecode.gwtquake.shared.common.Globals; import com.googlecode.gwtquake.shared.common.QuakeImage; import com.googlecode.gwtquake.shared.util.Math3D; /** * Warp * * @author cwei */ public class SkyBox { static float[] skyaxis = { 0, 0, 0 }; static String skyname; static float skyrotate; static Image[] sky_images = new Image[6]; static float[] dists = new float[GlConstants.MAX_CLIP_VERTS]; /** * BoundPoly * * @param numverts * @param verts * @param mins * @param maxs */ static void BoundPoly(int numverts, float[][] verts, float[] mins, float[] maxs) { mins[0] = mins[1] = mins[2] = 9999; maxs[0] = maxs[1] = maxs[2] = -9999; int j; float[] v; for (int i = 0; i < numverts; i++) { v = verts[i]; for (j = 0; j < 3; j++) { if (v[j] < mins[j]) { mins[j] = v[j]; } if (v[j] > maxs[j]) { maxs[j] = v[j]; } } } } // =================================================================== static float[][] skyclip = { { 1, 1, 0 }, { 1, -1, 0 }, { 0, -1, 1 }, { 0, 1, 1 }, { 1, 0, 1 }, { -1, 0, 1 } }; static int c_sky; // 1 = s, 2 = t, 3 = 2048 static int[][] st_to_vec = { { 3, -1, 2 }, { -3, 1, 2 }, { 1, 3, 2 }, { -1, -3, 2 }, { -2, -1, 3 }, // 0 degrees yaw, look straight up { 2, -1, -3 } // look straight down }; static int[][] vec_to_st = { { -2, 3, 1 }, { 2, 3, -1 }, { 1, 3, 2 }, { -1, 3, -2 }, { -2, -1, 3 }, { -2, 1, -3 } }; static float[][] skymins = new float[2][6]; static float[][] skymaxs = new float[2][6]; static float sky_min, sky_max; // stack variable static float[] v = { 0, 0, 0 }; static float[] av = { 0, 0, 0 }; /** * DrawSkyPolygon * * @param nump * @param vecs */ static void DrawSkyPolygon(int nump, float[][] vecs) { c_sky++; // decide which face it maps to Math3D.VectorCopy(Globals.vec3_origin, v); int i, axis; for (i = 0; i < nump; i++) { Math3D.VectorAdd(vecs[i], v, v); } av[0] = Math.abs(v[0]); av[1] = Math.abs(v[1]); av[2] = Math.abs(v[2]); if (av[0] > av[1] && av[0] > av[2]) { if (v[0] < 0) axis = 1; else axis = 0; } else if (av[1] > av[2] && av[1] > av[0]) { if (v[1] < 0) axis = 3; else axis = 2; } else { if (v[2] < 0) axis = 5; else axis = 4; } // project new texture coords float s, t, dv; int j; for (i = 0; i < nump; i++) { j = vec_to_st[axis][2]; if (j > 0) dv = vecs[i][j - 1]; else dv = -vecs[i][-j - 1]; if (dv < 0.001f) continue; // don't divide by zero j = vec_to_st[axis][0]; if (j < 0) s = -vecs[i][-j - 1] / dv; else s = vecs[i][j - 1] / dv; j = vec_to_st[axis][1]; if (j < 0) t = -vecs[i][-j - 1] / dv; else t = vecs[i][j - 1] / dv; if (s < skymins[0][axis]) skymins[0][axis] = s; if (t < skymins[1][axis]) skymins[1][axis] = t; if (s > skymaxs[0][axis]) skymaxs[0][axis] = s; if (t > skymaxs[1][axis]) skymaxs[1][axis] = t; } } static final int SIDE_BACK = 1; static final int SIDE_FRONT = 0; static final int SIDE_ON = 2; static int[] sides = new int[GlConstants.MAX_CLIP_VERTS]; static float[][][][] newv = new float[6][2][GlConstants.MAX_CLIP_VERTS][3]; /** * ClipSkyPolygon * * @param nump * @param vecs * @param stage */ static void ClipSkyPolygon(int nump, float[][] vecs, int stage) { if (nump > GlConstants.MAX_CLIP_VERTS - 2) Com.Error(Constants.ERR_DROP, "ClipSkyPolygon: MAX_CLIP_VERTS"); if (stage == 6) { // fully clipped, so draw it DrawSkyPolygon(nump, vecs); return; } boolean front = false; boolean back = false; float[] norm = skyclip[stage]; int i; float d; for (i = 0; i < nump; i++) { d = Math3D.DotProduct(vecs[i], norm); if (d > GlConstants.ON_EPSILON) { front = true; sides[i] = SIDE_FRONT; } else if (d < -GlConstants.ON_EPSILON) { back = true; sides[i] = SIDE_BACK; } else sides[i] = SIDE_ON; SkyBox.dists[i] = d; } if (!front || !back) { // not clipped ClipSkyPolygon(nump, vecs, stage + 1); return; } // clip it sides[i] = sides[0]; SkyBox.dists[i] = SkyBox.dists[0]; Math3D.VectorCopy(vecs[0], vecs[i]); int newc0 = 0; int newc1 = 0; float[] v; float e; int j; for (i = 0; i < nump; i++) { v = vecs[i]; switch (sides[i]) { case SIDE_FRONT: Math3D.VectorCopy(v, newv[stage][0][newc0]); newc0++; break; case SIDE_BACK: Math3D.VectorCopy(v, newv[stage][1][newc1]); newc1++; break; case SIDE_ON: Math3D.VectorCopy(v, newv[stage][0][newc0]); newc0++; Math3D.VectorCopy(v, newv[stage][1][newc1]); newc1++; break; } if (sides[i] == SIDE_ON || sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i]) continue; d = SkyBox.dists[i] / (SkyBox.dists[i] - SkyBox.dists[i + 1]); for (j = 0; j < 3; j++) { e = v[j] + d * (vecs[i + 1][j] - v[j]); newv[stage][0][newc0][j] = e; newv[stage][1][newc1][j] = e; } newc0++; newc1++; } // continue ClipSkyPolygon(newc0, newv[stage][0], stage + 1); ClipSkyPolygon(newc1, newv[stage][1], stage + 1); } static float[][] verts = new float[GlConstants.MAX_CLIP_VERTS][3]; /** * R_AddSkySurface */ static void R_AddSkySurface(Surface fa) { // calculate vertex values for sky box for (Polygon p = fa.polys; p != null; p = p.next) { for (int i = 0; i < p.numverts; i++) { verts[i][0] = p.getX(i) - GlState.r_origin[0]; verts[i][1] = p.getY(i) - GlState.r_origin[1]; verts[i][2] = p.getZ(i) - GlState.r_origin[2]; } ClipSkyPolygon(p.numverts, verts, 0); } } /** * R_ClearSkyBox */ static void R_ClearSkyBox() { float[] skymins0 = skymins[0]; float[] skymins1 = skymins[1]; float[] skymaxs0 = skymaxs[0]; float[] skymaxs1 = skymaxs[1]; for (int i = 0; i < 6; i++) { skymins0[i] = skymins1[i] = 9999; skymaxs0[i] = skymaxs1[i] = -9999; } } // stack variable static float[] v1 = { 0, 0, 0 }; static float[] b = { 0, 0, 0 }; /** * MakeSkyVec * * @param s * @param t * @param axis */ static void MakeSkyVec(float s, float t, int axis) { b[0] = s * 2300; b[1] = t * 2300; b[2] = 2300; int j, k; for (j = 0; j < 3; j++) { k = st_to_vec[axis][j]; if (k < 0) v1[j] = -b[-k - 1]; else v1[j] = b[k - 1]; } // avoid bilerp seam s = (s + 1) * 0.5f; t = (t + 1) * 0.5f; if (s < sky_min) s = sky_min; else if (s > sky_max) s = sky_max; if (t < sky_min) t = sky_min; else if (t > sky_max) t = sky_max; t = 1.0f - t; GlState.gl.glTexCoord2f(s, t); GlState.gl.glVertex3f(v1[0], v1[1], v1[2]); } static int[] skytexorder = { 0, 2, 1, 3, 4, 5 }; /** * R_DrawSkyBox */ static void R_DrawSkyBox() { int i; if (SkyBox.skyrotate != 0) { // check for no sky at all for (i = 0; i < 6; i++) if (skymins[0][i] < skymaxs[0][i] && skymins[1][i] < skymaxs[1][i]) break; if (i == 6) return; // nothing visible } GlState.gl.glPushMatrix(); GlState.gl.glTranslatef(GlState.r_origin[0], GlState.r_origin[1], GlState.r_origin[2]); GlState.gl.glRotatef(GlState.r_newrefdef.time * SkyBox.skyrotate, SkyBox.skyaxis[0], SkyBox.skyaxis[1], SkyBox.skyaxis[2]); for (i = 0; i < 6; i++) { if (SkyBox.skyrotate != 0) { // hack, forces full sky to draw when // rotating skymins[0][i] = -1; skymins[1][i] = -1; skymaxs[0][i] = 1; skymaxs[1][i] = 1; } if (skymins[0][i] >= skymaxs[0][i] || skymins[1][i] >= skymaxs[1][i]) continue; Images.GL_Bind(SkyBox.sky_images[skytexorder[i]].texnum); GlState.gl.glBegin(Gl1Context._GL_QUADS); MakeSkyVec(skymins[0][i], skymins[1][i], i); MakeSkyVec(skymins[0][i], skymaxs[1][i], i); MakeSkyVec(skymaxs[0][i], skymaxs[1][i], i); MakeSkyVec(skymaxs[0][i], skymins[1][i], i); GlState.gl.glEnd(); } GlState.gl.glPopMatrix(); } // 3dstudio environment map names static String[] suf = { "rt", "bk", "lf", "ft", "up", "dn" }; /** * R_SetSky * * @param name * @param rotate * @param axis */ static void R_SetSky(String name, float rotate, float[] axis) { assert (axis.length == 3) : "vec3_t bug"; String pathname; SkyBox.skyname = name; SkyBox.skyrotate = rotate; Math3D.VectorCopy(axis, SkyBox.skyaxis); for (int i = 0; i < 6; i++) { // chop down rotating skies for less memory if (GlConfig.gl_skymip.value != 0 || SkyBox.skyrotate != 0) GlConfig.gl_picmip.value++; // Com_sprintf (pathname, sizeof(pathname), "env/%s%s.tga", skyname, // suf[i]); pathname = "env/" + SkyBox.skyname + suf[i] + ".tga"; // gl.log("loadSky:" + pathname); SkyBox.sky_images[i] = Images.findTexture(pathname, QuakeImage.it_sky); if (SkyBox.sky_images[i] == null) SkyBox.sky_images[i] = GlState.r_notexture; if (GlConfig.gl_skymip.value != 0 || SkyBox.skyrotate != 0) { // take less // memory GlConfig.gl_picmip.value--; sky_min = 1.0f / 256; sky_max = 255.0f / 256; } else { sky_min = 1.0f / 512; sky_max = 511.0f / 512; } } } }