diff options
Diffstat (limited to 'src/jake2/render/fast')
-rw-r--r-- | src/jake2/render/fast/Anorms.java | 219 | ||||
-rw-r--r-- | src/jake2/render/fast/Draw.java | 412 | ||||
-rw-r--r-- | src/jake2/render/fast/Image.java | 1677 | ||||
-rw-r--r-- | src/jake2/render/fast/Light.java | 752 | ||||
-rw-r--r-- | src/jake2/render/fast/Main.java | 1526 | ||||
-rw-r--r-- | src/jake2/render/fast/Mesh.java | 688 | ||||
-rw-r--r-- | src/jake2/render/fast/Misc.java | 282 | ||||
-rw-r--r-- | src/jake2/render/fast/Model.java | 1358 | ||||
-rw-r--r-- | src/jake2/render/fast/Polygon.java | 164 | ||||
-rw-r--r-- | src/jake2/render/fast/Surf.java | 1333 | ||||
-rw-r--r-- | src/jake2/render/fast/Warp.java | 716 |
11 files changed, 9127 insertions, 0 deletions
diff --git a/src/jake2/render/fast/Anorms.java b/src/jake2/render/fast/Anorms.java new file mode 100644 index 0000000..2391049 --- /dev/null +++ b/src/jake2/render/fast/Anorms.java @@ -0,0 +1,219 @@ +/* + * Anorms.java + * Copyright (C) 2003 + * + * $Id: Anorms.java,v 1.2 2006-11-21 00:50:46 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 jake2.render.fast; + +/** + * Anorms + * + * @author cwei + */ +public interface Anorms { + + final float[][] VERTEXNORMALS = { + {-0.525731f, 0.000000f, 0.850651f}, + {-0.442863f, 0.238856f, 0.864188f}, + {-0.295242f, 0.000000f, 0.955423f}, + {-0.309017f, 0.500000f, 0.809017f}, + {-0.162460f, 0.262866f, 0.951056f}, + {0.000000f, 0.000000f, 1.000000f}, + {0.000000f, 0.850651f, 0.525731f}, + {-0.147621f, 0.716567f, 0.681718f}, + {0.147621f, 0.716567f, 0.681718f}, + {0.000000f, 0.525731f, 0.850651f}, + {0.309017f, 0.500000f, 0.809017f}, + {0.525731f, 0.000000f, 0.850651f}, + {0.295242f, 0.000000f, 0.955423f}, + {0.442863f, 0.238856f, 0.864188f}, + {0.162460f, 0.262866f, 0.951056f}, + {-0.681718f, 0.147621f, 0.716567f}, + {-0.809017f, 0.309017f, 0.500000f}, + {-0.587785f, 0.425325f, 0.688191f}, + {-0.850651f, 0.525731f, 0.000000f}, + {-0.864188f, 0.442863f, 0.238856f}, + {-0.716567f, 0.681718f, 0.147621f}, + {-0.688191f, 0.587785f, 0.425325f}, + {-0.500000f, 0.809017f, 0.309017f}, + {-0.238856f, 0.864188f, 0.442863f}, + {-0.425325f, 0.688191f, 0.587785f}, + {-0.716567f, 0.681718f, -0.147621f}, + {-0.500000f, 0.809017f, -0.309017f}, + {-0.525731f, 0.850651f, 0.000000f}, + {0.000000f, 0.850651f, -0.525731f}, + {-0.238856f, 0.864188f, -0.442863f}, + {0.000000f, 0.955423f, -0.295242f}, + {-0.262866f, 0.951056f, -0.162460f}, + {0.000000f, 1.000000f, 0.000000f}, + {0.000000f, 0.955423f, 0.295242f}, + {-0.262866f, 0.951056f, 0.162460f}, + {0.238856f, 0.864188f, 0.442863f}, + {0.262866f, 0.951056f, 0.162460f}, + {0.500000f, 0.809017f, 0.309017f}, + {0.238856f, 0.864188f, -0.442863f}, + {0.262866f, 0.951056f, -0.162460f}, + {0.500000f, 0.809017f, -0.309017f}, + {0.850651f, 0.525731f, 0.000000f}, + {0.716567f, 0.681718f, 0.147621f}, + {0.716567f, 0.681718f, -0.147621f}, + {0.525731f, 0.850651f, 0.000000f}, + {0.425325f, 0.688191f, 0.587785f}, + {0.864188f, 0.442863f, 0.238856f}, + {0.688191f, 0.587785f, 0.425325f}, + {0.809017f, 0.309017f, 0.500000f}, + {0.681718f, 0.147621f, 0.716567f}, + {0.587785f, 0.425325f, 0.688191f}, + {0.955423f, 0.295242f, 0.000000f}, + {1.000000f, 0.000000f, 0.000000f}, + {0.951056f, 0.162460f, 0.262866f}, + {0.850651f, -0.525731f, 0.000000f}, + {0.955423f, -0.295242f, 0.000000f}, + {0.864188f, -0.442863f, 0.238856f}, + {0.951056f, -0.162460f, 0.262866f}, + {0.809017f, -0.309017f, 0.500000f}, + {0.681718f, -0.147621f, 0.716567f}, + {0.850651f, 0.000000f, 0.525731f}, + {0.864188f, 0.442863f, -0.238856f}, + {0.809017f, 0.309017f, -0.500000f}, + {0.951056f, 0.162460f, -0.262866f}, + {0.525731f, 0.000000f, -0.850651f}, + {0.681718f, 0.147621f, -0.716567f}, + {0.681718f, -0.147621f, -0.716567f}, + {0.850651f, 0.000000f, -0.525731f}, + {0.809017f, -0.309017f, -0.500000f}, + {0.864188f, -0.442863f, -0.238856f}, + {0.951056f, -0.162460f, -0.262866f}, + {0.147621f, 0.716567f, -0.681718f}, + {0.309017f, 0.500000f, -0.809017f}, + {0.425325f, 0.688191f, -0.587785f}, + {0.442863f, 0.238856f, -0.864188f}, + {0.587785f, 0.425325f, -0.688191f}, + {0.688191f, 0.587785f, -0.425325f}, + {-0.147621f, 0.716567f, -0.681718f}, + {-0.309017f, 0.500000f, -0.809017f}, + {0.000000f, 0.525731f, -0.850651f}, + {-0.525731f, 0.000000f, -0.850651f}, + {-0.442863f, 0.238856f, -0.864188f}, + {-0.295242f, 0.000000f, -0.955423f}, + {-0.162460f, 0.262866f, -0.951056f}, + {0.000000f, 0.000000f, -1.000000f}, + {0.295242f, 0.000000f, -0.955423f}, + {0.162460f, 0.262866f, -0.951056f}, + {-0.442863f, -0.238856f, -0.864188f}, + {-0.309017f, -0.500000f, -0.809017f}, + {-0.162460f, -0.262866f, -0.951056f}, + {0.000000f, -0.850651f, -0.525731f}, + {-0.147621f, -0.716567f, -0.681718f}, + {0.147621f, -0.716567f, -0.681718f}, + {0.000000f, -0.525731f, -0.850651f}, + {0.309017f, -0.500000f, -0.809017f}, + {0.442863f, -0.238856f, -0.864188f}, + {0.162460f, -0.262866f, -0.951056f}, + {0.238856f, -0.864188f, -0.442863f}, + {0.500000f, -0.809017f, -0.309017f}, + {0.425325f, -0.688191f, -0.587785f}, + {0.716567f, -0.681718f, -0.147621f}, + {0.688191f, -0.587785f, -0.425325f}, + {0.587785f, -0.425325f, -0.688191f}, + {0.000000f, -0.955423f, -0.295242f}, + {0.000000f, -1.000000f, 0.000000f}, + {0.262866f, -0.951056f, -0.162460f}, + {0.000000f, -0.850651f, 0.525731f}, + {0.000000f, -0.955423f, 0.295242f}, + {0.238856f, -0.864188f, 0.442863f}, + {0.262866f, -0.951056f, 0.162460f}, + {0.500000f, -0.809017f, 0.309017f}, + {0.716567f, -0.681718f, 0.147621f}, + {0.525731f, -0.850651f, 0.000000f}, + {-0.238856f, -0.864188f, -0.442863f}, + {-0.500000f, -0.809017f, -0.309017f}, + {-0.262866f, -0.951056f, -0.162460f}, + {-0.850651f, -0.525731f, 0.000000f}, + {-0.716567f, -0.681718f, -0.147621f}, + {-0.716567f, -0.681718f, 0.147621f}, + {-0.525731f, -0.850651f, 0.000000f}, + {-0.500000f, -0.809017f, 0.309017f}, + {-0.238856f, -0.864188f, 0.442863f}, + {-0.262866f, -0.951056f, 0.162460f}, + {-0.864188f, -0.442863f, 0.238856f}, + {-0.809017f, -0.309017f, 0.500000f}, + {-0.688191f, -0.587785f, 0.425325f}, + {-0.681718f, -0.147621f, 0.716567f}, + {-0.442863f, -0.238856f, 0.864188f}, + {-0.587785f, -0.425325f, 0.688191f}, + {-0.309017f, -0.500000f, 0.809017f}, + {-0.147621f, -0.716567f, 0.681718f}, + {-0.425325f, -0.688191f, 0.587785f}, + {-0.162460f, -0.262866f, 0.951056f}, + {0.442863f, -0.238856f, 0.864188f}, + {0.162460f, -0.262866f, 0.951056f}, + {0.309017f, -0.500000f, 0.809017f}, + {0.147621f, -0.716567f, 0.681718f}, + {0.000000f, -0.525731f, 0.850651f}, + {0.425325f, -0.688191f, 0.587785f}, + {0.587785f, -0.425325f, 0.688191f}, + {0.688191f, -0.587785f, 0.425325f}, + {-0.955423f, 0.295242f, 0.000000f}, + {-0.951056f, 0.162460f, 0.262866f}, + {-1.000000f, 0.000000f, 0.000000f}, + {-0.850651f, 0.000000f, 0.525731f}, + {-0.955423f, -0.295242f, 0.000000f}, + {-0.951056f, -0.162460f, 0.262866f}, + {-0.864188f, 0.442863f, -0.238856f}, + {-0.951056f, 0.162460f, -0.262866f}, + {-0.809017f, 0.309017f, -0.500000f}, + {-0.864188f, -0.442863f, -0.238856f}, + {-0.951056f, -0.162460f, -0.262866f}, + {-0.809017f, -0.309017f, -0.500000f}, + {-0.681718f, 0.147621f, -0.716567f}, + {-0.681718f, -0.147621f, -0.716567f}, + {-0.850651f, 0.000000f, -0.525731f}, + {-0.688191f, 0.587785f, -0.425325f}, + {-0.587785f, 0.425325f, -0.688191f}, + {-0.425325f, 0.688191f, -0.587785f}, + {-0.425325f, -0.688191f, -0.587785f}, + {-0.587785f, -0.425325f, -0.688191f}, + {-0.688191f, -0.587785f, -0.425325f} + }; + + final float[][] VERTEXNORMAL_DOTS = { + {1.23f,1.30f,1.47f,1.35f,1.56f,1.71f,1.37f,1.38f,1.59f,1.60f,1.79f,1.97f,1.88f,1.92f,1.79f,1.02f,0.93f,1.07f,0.82f,0.87f,0.88f,0.94f,0.96f,1.14f,1.11f,0.82f,0.83f,0.89f,0.89f,0.86f,0.94f,0.91f,1.00f,1.21f,0.98f,1.48f,1.30f,1.57f,0.96f,1.07f,1.14f,1.60f,1.61f,1.40f,1.37f,1.72f,1.78f,1.79f,1.93f,1.99f,1.90f,1.68f,1.71f,1.86f,1.60f,1.68f,1.78f,1.86f,1.93f,1.99f,1.97f,1.44f,1.22f,1.49f,0.93f,0.99f,0.99f,1.23f,1.22f,1.44f,1.49f,0.89f,0.89f,0.97f,0.91f,0.98f,1.19f,0.82f,0.76f,0.82f,0.71f,0.72f,0.73f,0.76f,0.79f,0.86f,0.83f,0.72f,0.76f,0.76f,0.89f,0.82f,0.89f,0.82f,0.89f,0.91f,0.83f,0.96f,1.14f,0.97f,1.40f,1.19f,0.98f,0.94f,1.00f,1.07f,1.37f,1.21f,1.48f,1.30f,1.57f,1.61f,1.37f,0.86f,0.83f,0.91f,0.82f,0.82f,0.88f,0.89f,0.96f,1.14f,0.98f,0.87f,0.93f,0.94f,1.02f,1.30f,1.07f,1.35f,1.38f,1.11f,1.56f,1.92f,1.79f,1.79f,1.59f,1.60f,1.72f,1.90f,1.79f,0.80f,0.85f,0.79f,0.93f,0.80f,0.85f,0.77f,0.74f,0.72f,0.77f,0.74f,0.72f,0.70f,0.70f,0.71f,0.76f,0.73f,0.79f,0.79f,0.73f,0.76f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f}, + {1.26f,1.26f,1.48f,1.23f,1.50f,1.71f,1.14f,1.19f,1.38f,1.46f,1.64f,1.94f,1.87f,1.84f,1.71f,1.02f,0.92f,1.00f,0.79f,0.85f,0.84f,0.91f,0.90f,0.98f,0.99f,0.77f,0.77f,0.83f,0.82f,0.79f,0.86f,0.84f,0.92f,0.99f,0.91f,1.24f,1.03f,1.33f,0.88f,0.94f,0.97f,1.41f,1.39f,1.18f,1.11f,1.51f,1.61f,1.59f,1.80f,1.91f,1.76f,1.54f,1.65f,1.76f,1.70f,1.70f,1.85f,1.85f,1.97f,1.99f,1.93f,1.28f,1.09f,1.39f,0.92f,0.97f,0.99f,1.18f,1.26f,1.52f,1.48f,0.83f,0.85f,0.90f,0.88f,0.93f,1.00f,0.77f,0.73f,0.78f,0.72f,0.71f,0.74f,0.75f,0.79f,0.86f,0.81f,0.75f,0.81f,0.79f,0.96f,0.88f,0.94f,0.86f,0.93f,0.92f,0.85f,1.08f,1.33f,1.05f,1.55f,1.31f,1.01f,1.05f,1.27f,1.31f,1.60f,1.47f,1.70f,1.54f,1.76f,1.76f,1.57f,0.93f,0.90f,0.99f,0.88f,0.88f,0.95f,0.97f,1.11f,1.39f,1.20f,0.92f,0.97f,1.01f,1.10f,1.39f,1.22f,1.51f,1.58f,1.32f,1.64f,1.97f,1.85f,1.91f,1.77f,1.74f,1.88f,1.99f,1.91f,0.79f,0.86f,0.80f,0.94f,0.84f,0.88f,0.74f,0.74f,0.71f,0.82f,0.77f,0.76f,0.70f,0.73f,0.72f,0.73f,0.70f,0.74f,0.85f,0.77f,0.82f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f}, + {1.34f,1.27f,1.53f,1.17f,1.46f,1.71f,0.98f,1.05f,1.20f,1.34f,1.48f,1.86f,1.82f,1.71f,1.62f,1.09f,0.94f,0.99f,0.79f,0.85f,0.82f,0.90f,0.87f,0.93f,0.96f,0.76f,0.74f,0.79f,0.76f,0.74f,0.79f,0.78f,0.85f,0.92f,0.85f,1.00f,0.93f,1.06f,0.81f,0.86f,0.89f,1.16f,1.12f,0.97f,0.95f,1.28f,1.38f,1.35f,1.60f,1.77f,1.57f,1.33f,1.50f,1.58f,1.69f,1.63f,1.82f,1.74f,1.91f,1.92f,1.80f,1.04f,0.97f,1.21f,0.90f,0.93f,0.97f,1.05f,1.21f,1.48f,1.37f,0.77f,0.80f,0.84f,0.85f,0.88f,0.92f,0.73f,0.71f,0.74f,0.74f,0.71f,0.75f,0.73f,0.79f,0.84f,0.78f,0.79f,0.86f,0.81f,1.05f,0.94f,0.99f,0.90f,0.95f,0.92f,0.86f,1.24f,1.44f,1.14f,1.59f,1.34f,1.02f,1.27f,1.50f,1.49f,1.80f,1.69f,1.86f,1.72f,1.87f,1.80f,1.69f,1.00f,0.98f,1.23f,0.95f,0.96f,1.09f,1.16f,1.37f,1.63f,1.46f,0.99f,1.10f,1.25f,1.24f,1.51f,1.41f,1.67f,1.77f,1.55f,1.72f,1.95f,1.89f,1.98f,1.91f,1.86f,1.97f,1.99f,1.94f,0.81f,0.89f,0.85f,0.98f,0.90f,0.94f,0.75f,0.78f,0.73f,0.89f,0.83f,0.82f,0.72f,0.77f,0.76f,0.72f,0.70f,0.71f,0.91f,0.83f,0.89f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f}, + {1.46f,1.34f,1.60f,1.16f,1.46f,1.71f,0.94f,0.99f,1.05f,1.26f,1.33f,1.74f,1.76f,1.57f,1.54f,1.23f,0.98f,1.05f,0.83f,0.89f,0.84f,0.92f,0.87f,0.91f,0.96f,0.78f,0.74f,0.79f,0.72f,0.72f,0.75f,0.76f,0.80f,0.88f,0.83f,0.94f,0.87f,0.95f,0.76f,0.80f,0.82f,0.97f,0.96f,0.89f,0.88f,1.08f,1.11f,1.10f,1.37f,1.59f,1.37f,1.07f,1.27f,1.34f,1.57f,1.45f,1.69f,1.55f,1.77f,1.79f,1.60f,0.93f,0.90f,0.99f,0.86f,0.87f,0.93f,0.96f,1.07f,1.35f,1.18f,0.73f,0.76f,0.77f,0.81f,0.82f,0.85f,0.70f,0.71f,0.72f,0.78f,0.73f,0.77f,0.73f,0.79f,0.82f,0.76f,0.83f,0.90f,0.84f,1.18f,0.98f,1.03f,0.92f,0.95f,0.90f,0.86f,1.32f,1.45f,1.15f,1.53f,1.27f,0.99f,1.42f,1.65f,1.58f,1.93f,1.83f,1.94f,1.81f,1.88f,1.74f,1.70f,1.19f,1.17f,1.44f,1.11f,1.15f,1.36f,1.41f,1.61f,1.81f,1.67f,1.22f,1.34f,1.50f,1.42f,1.65f,1.61f,1.82f,1.91f,1.75f,1.80f,1.89f,1.89f,1.98f,1.99f,1.94f,1.98f,1.92f,1.87f,0.86f,0.95f,0.92f,1.14f,0.98f,1.03f,0.79f,0.84f,0.77f,0.97f,0.90f,0.89f,0.76f,0.82f,0.82f,0.74f,0.72f,0.71f,0.98f,0.89f,0.97f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f}, + {1.60f,1.44f,1.68f,1.22f,1.49f,1.71f,0.93f,0.99f,0.99f,1.23f,1.22f,1.60f,1.68f,1.44f,1.49f,1.40f,1.14f,1.19f,0.89f,0.96f,0.89f,0.97f,0.89f,0.91f,0.98f,0.82f,0.76f,0.82f,0.71f,0.72f,0.73f,0.76f,0.79f,0.86f,0.83f,0.91f,0.83f,0.89f,0.72f,0.76f,0.76f,0.89f,0.89f,0.82f,0.82f,0.98f,0.96f,0.97f,1.14f,1.40f,1.19f,0.94f,1.00f,1.07f,1.37f,1.21f,1.48f,1.30f,1.57f,1.61f,1.37f,0.86f,0.83f,0.91f,0.82f,0.82f,0.88f,0.89f,0.96f,1.14f,0.98f,0.70f,0.72f,0.73f,0.77f,0.76f,0.79f,0.70f,0.72f,0.71f,0.82f,0.77f,0.80f,0.74f,0.79f,0.80f,0.74f,0.87f,0.93f,0.85f,1.23f,1.02f,1.02f,0.93f,0.93f,0.87f,0.85f,1.30f,1.35f,1.07f,1.38f,1.11f,0.94f,1.47f,1.71f,1.56f,1.97f,1.88f,1.92f,1.79f,1.79f,1.59f,1.60f,1.30f,1.35f,1.56f,1.37f,1.38f,1.59f,1.60f,1.79f,1.92f,1.79f,1.48f,1.57f,1.72f,1.61f,1.78f,1.79f,1.93f,1.99f,1.90f,1.86f,1.78f,1.86f,1.93f,1.99f,1.97f,1.90f,1.79f,1.72f,0.94f,1.07f,1.00f,1.37f,1.21f,1.30f,0.86f,0.91f,0.83f,1.14f,0.98f,0.96f,0.82f,0.88f,0.89f,0.79f,0.76f,0.73f,1.07f,0.94f,1.11f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f}, + {1.74f,1.57f,1.76f,1.33f,1.54f,1.71f,0.94f,1.05f,0.99f,1.26f,1.16f,1.46f,1.60f,1.34f,1.46f,1.59f,1.37f,1.37f,0.97f,1.11f,0.96f,1.10f,0.95f,0.94f,1.08f,0.89f,0.82f,0.88f,0.72f,0.76f,0.75f,0.80f,0.80f,0.88f,0.87f,0.91f,0.83f,0.87f,0.72f,0.76f,0.74f,0.83f,0.84f,0.78f,0.79f,0.96f,0.89f,0.92f,0.98f,1.23f,1.05f,0.86f,0.92f,0.95f,1.11f,0.98f,1.22f,1.03f,1.34f,1.42f,1.14f,0.79f,0.77f,0.84f,0.78f,0.76f,0.82f,0.82f,0.89f,0.97f,0.90f,0.70f,0.71f,0.71f,0.73f,0.72f,0.74f,0.73f,0.76f,0.72f,0.86f,0.81f,0.82f,0.76f,0.79f,0.77f,0.73f,0.90f,0.95f,0.86f,1.18f,1.03f,0.98f,0.92f,0.90f,0.83f,0.84f,1.19f,1.17f,0.98f,1.15f,0.97f,0.89f,1.42f,1.65f,1.44f,1.93f,1.83f,1.81f,1.67f,1.61f,1.36f,1.41f,1.32f,1.45f,1.58f,1.57f,1.53f,1.74f,1.70f,1.88f,1.94f,1.81f,1.69f,1.77f,1.87f,1.79f,1.89f,1.92f,1.98f,1.99f,1.98f,1.89f,1.65f,1.80f,1.82f,1.91f,1.94f,1.75f,1.61f,1.50f,1.07f,1.34f,1.27f,1.60f,1.45f,1.55f,0.93f,0.99f,0.90f,1.35f,1.18f,1.07f,0.87f,0.93f,0.96f,0.85f,0.82f,0.77f,1.15f,0.99f,1.27f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f}, + {1.86f,1.71f,1.82f,1.48f,1.62f,1.71f,0.98f,1.20f,1.05f,1.34f,1.17f,1.34f,1.53f,1.27f,1.46f,1.77f,1.60f,1.57f,1.16f,1.38f,1.12f,1.35f,1.06f,1.00f,1.28f,0.97f,0.89f,0.95f,0.76f,0.81f,0.79f,0.86f,0.85f,0.92f,0.93f,0.93f,0.85f,0.87f,0.74f,0.78f,0.74f,0.79f,0.82f,0.76f,0.79f,0.96f,0.85f,0.90f,0.94f,1.09f,0.99f,0.81f,0.85f,0.89f,0.95f,0.90f,0.99f,0.94f,1.10f,1.24f,0.98f,0.75f,0.73f,0.78f,0.74f,0.72f,0.77f,0.76f,0.82f,0.89f,0.83f,0.73f,0.71f,0.71f,0.71f,0.70f,0.72f,0.77f,0.80f,0.74f,0.90f,0.85f,0.84f,0.78f,0.79f,0.75f,0.73f,0.92f,0.95f,0.86f,1.05f,0.99f,0.94f,0.90f,0.86f,0.79f,0.81f,1.00f,0.98f,0.91f,0.96f,0.89f,0.83f,1.27f,1.50f,1.23f,1.80f,1.69f,1.63f,1.46f,1.37f,1.09f,1.16f,1.24f,1.44f,1.49f,1.69f,1.59f,1.80f,1.69f,1.87f,1.86f,1.72f,1.82f,1.91f,1.94f,1.92f,1.95f,1.99f,1.98f,1.91f,1.97f,1.89f,1.51f,1.72f,1.67f,1.77f,1.86f,1.55f,1.41f,1.25f,1.33f,1.58f,1.50f,1.80f,1.63f,1.74f,1.04f,1.21f,0.97f,1.48f,1.37f,1.21f,0.93f,0.97f,1.05f,0.92f,0.88f,0.84f,1.14f,1.02f,1.34f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f}, + {1.94f,1.84f,1.87f,1.64f,1.71f,1.71f,1.14f,1.38f,1.19f,1.46f,1.23f,1.26f,1.48f,1.26f,1.50f,1.91f,1.80f,1.76f,1.41f,1.61f,1.39f,1.59f,1.33f,1.24f,1.51f,1.18f,0.97f,1.11f,0.82f,0.88f,0.86f,0.94f,0.92f,0.99f,1.03f,0.98f,0.91f,0.90f,0.79f,0.84f,0.77f,0.79f,0.84f,0.77f,0.83f,0.99f,0.85f,0.91f,0.92f,1.02f,1.00f,0.79f,0.80f,0.86f,0.88f,0.84f,0.92f,0.88f,0.97f,1.10f,0.94f,0.74f,0.71f,0.74f,0.72f,0.70f,0.73f,0.72f,0.76f,0.82f,0.77f,0.77f,0.73f,0.74f,0.71f,0.70f,0.73f,0.83f,0.85f,0.78f,0.92f,0.88f,0.86f,0.81f,0.79f,0.74f,0.75f,0.92f,0.93f,0.85f,0.96f,0.94f,0.88f,0.86f,0.81f,0.75f,0.79f,0.93f,0.90f,0.85f,0.88f,0.82f,0.77f,1.05f,1.27f,0.99f,1.60f,1.47f,1.39f,1.20f,1.11f,0.95f,0.97f,1.08f,1.33f,1.31f,1.70f,1.55f,1.76f,1.57f,1.76f,1.70f,1.54f,1.85f,1.97f,1.91f,1.99f,1.97f,1.99f,1.91f,1.77f,1.88f,1.85f,1.39f,1.64f,1.51f,1.58f,1.74f,1.32f,1.22f,1.01f,1.54f,1.76f,1.65f,1.93f,1.70f,1.85f,1.28f,1.39f,1.09f,1.52f,1.48f,1.26f,0.97f,0.99f,1.18f,1.00f,0.93f,0.90f,1.05f,1.01f,1.31f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f}, + {1.97f,1.92f,1.88f,1.79f,1.79f,1.71f,1.37f,1.59f,1.38f,1.60f,1.35f,1.23f,1.47f,1.30f,1.56f,1.99f,1.93f,1.90f,1.60f,1.78f,1.61f,1.79f,1.57f,1.48f,1.72f,1.40f,1.14f,1.37f,0.89f,0.96f,0.94f,1.07f,1.00f,1.21f,1.30f,1.14f,0.98f,0.96f,0.86f,0.91f,0.83f,0.82f,0.88f,0.82f,0.89f,1.11f,0.87f,0.94f,0.93f,1.02f,1.07f,0.80f,0.79f,0.85f,0.82f,0.80f,0.87f,0.85f,0.93f,1.02f,0.93f,0.77f,0.72f,0.74f,0.71f,0.70f,0.70f,0.71f,0.72f,0.77f,0.74f,0.82f,0.76f,0.79f,0.72f,0.73f,0.76f,0.89f,0.89f,0.82f,0.93f,0.91f,0.86f,0.83f,0.79f,0.73f,0.76f,0.91f,0.89f,0.83f,0.89f,0.89f,0.82f,0.82f,0.76f,0.72f,0.76f,0.86f,0.83f,0.79f,0.82f,0.76f,0.73f,0.94f,1.00f,0.91f,1.37f,1.21f,1.14f,0.98f,0.96f,0.88f,0.89f,0.96f,1.14f,1.07f,1.60f,1.40f,1.61f,1.37f,1.57f,1.48f,1.30f,1.78f,1.93f,1.79f,1.99f,1.92f,1.90f,1.79f,1.59f,1.72f,1.79f,1.30f,1.56f,1.35f,1.38f,1.60f,1.11f,1.07f,0.94f,1.68f,1.86f,1.71f,1.97f,1.68f,1.86f,1.44f,1.49f,1.22f,1.44f,1.49f,1.22f,0.99f,0.99f,1.23f,1.19f,0.98f,0.97f,0.97f,0.98f,1.19f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f}, + {1.94f,1.97f,1.87f,1.91f,1.85f,1.71f,1.60f,1.77f,1.58f,1.74f,1.51f,1.26f,1.48f,1.39f,1.64f,1.99f,1.97f,1.99f,1.70f,1.85f,1.76f,1.91f,1.76f,1.70f,1.88f,1.55f,1.33f,1.57f,0.96f,1.08f,1.05f,1.31f,1.27f,1.47f,1.54f,1.39f,1.20f,1.11f,0.93f,0.99f,0.90f,0.88f,0.95f,0.88f,0.97f,1.32f,0.92f,1.01f,0.97f,1.10f,1.22f,0.84f,0.80f,0.88f,0.79f,0.79f,0.85f,0.86f,0.92f,1.02f,0.94f,0.82f,0.76f,0.77f,0.72f,0.73f,0.70f,0.72f,0.71f,0.74f,0.74f,0.88f,0.81f,0.85f,0.75f,0.77f,0.82f,0.94f,0.93f,0.86f,0.92f,0.92f,0.86f,0.85f,0.79f,0.74f,0.79f,0.88f,0.85f,0.81f,0.82f,0.83f,0.77f,0.78f,0.73f,0.71f,0.75f,0.79f,0.77f,0.74f,0.77f,0.73f,0.70f,0.86f,0.92f,0.84f,1.14f,0.99f,0.98f,0.91f,0.90f,0.84f,0.83f,0.88f,0.97f,0.94f,1.41f,1.18f,1.39f,1.11f,1.33f,1.24f,1.03f,1.61f,1.80f,1.59f,1.91f,1.84f,1.76f,1.64f,1.38f,1.51f,1.71f,1.26f,1.50f,1.23f,1.19f,1.46f,0.99f,1.00f,0.91f,1.70f,1.85f,1.65f,1.93f,1.54f,1.76f,1.52f,1.48f,1.26f,1.28f,1.39f,1.09f,0.99f,0.97f,1.18f,1.31f,1.01f,1.05f,0.90f,0.93f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f}, + {1.86f,1.95f,1.82f,1.98f,1.89f,1.71f,1.80f,1.91f,1.77f,1.86f,1.67f,1.34f,1.53f,1.51f,1.72f,1.92f,1.91f,1.99f,1.69f,1.82f,1.80f,1.94f,1.87f,1.86f,1.97f,1.59f,1.44f,1.69f,1.05f,1.24f,1.27f,1.49f,1.50f,1.69f,1.72f,1.63f,1.46f,1.37f,1.00f,1.23f,0.98f,0.95f,1.09f,0.96f,1.16f,1.55f,0.99f,1.25f,1.10f,1.24f,1.41f,0.90f,0.85f,0.94f,0.79f,0.81f,0.85f,0.89f,0.94f,1.09f,0.98f,0.89f,0.82f,0.83f,0.74f,0.77f,0.72f,0.76f,0.73f,0.75f,0.78f,0.94f,0.86f,0.91f,0.79f,0.83f,0.89f,0.99f,0.95f,0.90f,0.90f,0.92f,0.84f,0.86f,0.79f,0.75f,0.81f,0.85f,0.80f,0.78f,0.76f,0.77f,0.73f,0.74f,0.71f,0.71f,0.73f,0.74f,0.74f,0.71f,0.76f,0.72f,0.70f,0.79f,0.85f,0.78f,0.98f,0.92f,0.93f,0.85f,0.87f,0.82f,0.79f,0.81f,0.89f,0.86f,1.16f,0.97f,1.12f,0.95f,1.06f,1.00f,0.93f,1.38f,1.60f,1.35f,1.77f,1.71f,1.57f,1.48f,1.20f,1.28f,1.62f,1.27f,1.46f,1.17f,1.05f,1.34f,0.96f,0.99f,0.90f,1.63f,1.74f,1.50f,1.80f,1.33f,1.58f,1.48f,1.37f,1.21f,1.04f,1.21f,0.97f,0.97f,0.93f,1.05f,1.34f,1.02f,1.14f,0.84f,0.88f,0.92f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f}, + {1.74f,1.89f,1.76f,1.98f,1.89f,1.71f,1.93f,1.99f,1.91f,1.94f,1.82f,1.46f,1.60f,1.65f,1.80f,1.79f,1.77f,1.92f,1.57f,1.69f,1.74f,1.87f,1.88f,1.94f,1.98f,1.53f,1.45f,1.70f,1.18f,1.32f,1.42f,1.58f,1.65f,1.83f,1.81f,1.81f,1.67f,1.61f,1.19f,1.44f,1.17f,1.11f,1.36f,1.15f,1.41f,1.75f,1.22f,1.50f,1.34f,1.42f,1.61f,0.98f,0.92f,1.03f,0.83f,0.86f,0.89f,0.95f,0.98f,1.23f,1.14f,0.97f,0.89f,0.90f,0.78f,0.82f,0.76f,0.82f,0.77f,0.79f,0.84f,0.98f,0.90f,0.98f,0.83f,0.89f,0.97f,1.03f,0.95f,0.92f,0.86f,0.90f,0.82f,0.86f,0.79f,0.77f,0.84f,0.81f,0.76f,0.76f,0.72f,0.73f,0.70f,0.72f,0.71f,0.73f,0.73f,0.72f,0.74f,0.71f,0.78f,0.74f,0.72f,0.75f,0.80f,0.76f,0.94f,0.88f,0.91f,0.83f,0.87f,0.84f,0.79f,0.76f,0.82f,0.80f,0.97f,0.89f,0.96f,0.88f,0.95f,0.94f,0.87f,1.11f,1.37f,1.10f,1.59f,1.57f,1.37f,1.33f,1.05f,1.08f,1.54f,1.34f,1.46f,1.16f,0.99f,1.26f,0.96f,1.05f,0.92f,1.45f,1.55f,1.27f,1.60f,1.07f,1.34f,1.35f,1.18f,1.07f,0.93f,0.99f,0.90f,0.93f,0.87f,0.96f,1.27f,0.99f,1.15f,0.77f,0.82f,0.85f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f}, + {1.60f,1.78f,1.68f,1.93f,1.86f,1.71f,1.97f,1.99f,1.99f,1.97f,1.93f,1.60f,1.68f,1.78f,1.86f,1.61f,1.57f,1.79f,1.37f,1.48f,1.59f,1.72f,1.79f,1.92f,1.90f,1.38f,1.35f,1.60f,1.23f,1.30f,1.47f,1.56f,1.71f,1.88f,1.79f,1.92f,1.79f,1.79f,1.30f,1.56f,1.35f,1.37f,1.59f,1.38f,1.60f,1.90f,1.48f,1.72f,1.57f,1.61f,1.79f,1.21f,1.00f,1.30f,0.89f,0.94f,0.96f,1.07f,1.14f,1.40f,1.37f,1.14f,0.96f,0.98f,0.82f,0.88f,0.82f,0.89f,0.83f,0.86f,0.91f,1.02f,0.93f,1.07f,0.87f,0.94f,1.11f,1.02f,0.93f,0.93f,0.82f,0.87f,0.80f,0.85f,0.79f,0.80f,0.85f,0.77f,0.72f,0.74f,0.71f,0.70f,0.70f,0.71f,0.72f,0.77f,0.74f,0.72f,0.76f,0.73f,0.82f,0.79f,0.76f,0.73f,0.79f,0.76f,0.93f,0.86f,0.91f,0.83f,0.89f,0.89f,0.82f,0.72f,0.76f,0.76f,0.89f,0.82f,0.89f,0.82f,0.89f,0.91f,0.83f,0.96f,1.14f,0.97f,1.40f,1.44f,1.19f,1.22f,0.99f,0.98f,1.49f,1.44f,1.49f,1.22f,0.99f,1.23f,0.98f,1.19f,0.97f,1.21f,1.30f,1.00f,1.37f,0.94f,1.07f,1.14f,0.98f,0.96f,0.86f,0.91f,0.83f,0.88f,0.82f,0.89f,1.11f,0.94f,1.07f,0.73f,0.76f,0.79f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f}, + {1.46f,1.65f,1.60f,1.82f,1.80f,1.71f,1.93f,1.91f,1.99f,1.94f,1.98f,1.74f,1.76f,1.89f,1.89f,1.42f,1.34f,1.61f,1.11f,1.22f,1.36f,1.50f,1.61f,1.81f,1.75f,1.15f,1.17f,1.41f,1.18f,1.19f,1.42f,1.44f,1.65f,1.83f,1.67f,1.94f,1.81f,1.88f,1.32f,1.58f,1.45f,1.57f,1.74f,1.53f,1.70f,1.98f,1.69f,1.87f,1.77f,1.79f,1.92f,1.45f,1.27f,1.55f,0.97f,1.07f,1.11f,1.34f,1.37f,1.59f,1.60f,1.35f,1.07f,1.18f,0.86f,0.93f,0.87f,0.96f,0.90f,0.93f,0.99f,1.03f,0.95f,1.15f,0.90f,0.99f,1.27f,0.98f,0.90f,0.92f,0.78f,0.83f,0.77f,0.84f,0.79f,0.82f,0.86f,0.73f,0.71f,0.73f,0.72f,0.70f,0.73f,0.72f,0.76f,0.81f,0.76f,0.76f,0.82f,0.77f,0.89f,0.85f,0.82f,0.75f,0.80f,0.80f,0.94f,0.88f,0.94f,0.87f,0.95f,0.96f,0.88f,0.72f,0.74f,0.76f,0.83f,0.78f,0.84f,0.79f,0.87f,0.91f,0.83f,0.89f,0.98f,0.92f,1.23f,1.34f,1.05f,1.16f,0.99f,0.96f,1.46f,1.57f,1.54f,1.33f,1.05f,1.26f,1.08f,1.37f,1.10f,0.98f,1.03f,0.92f,1.14f,0.86f,0.95f,0.97f,0.90f,0.89f,0.79f,0.84f,0.77f,0.82f,0.76f,0.82f,0.97f,0.89f,0.98f,0.71f,0.72f,0.74f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f}, + {1.34f,1.51f,1.53f,1.67f,1.72f,1.71f,1.80f,1.77f,1.91f,1.86f,1.98f,1.86f,1.82f,1.95f,1.89f,1.24f,1.10f,1.41f,0.95f,0.99f,1.09f,1.25f,1.37f,1.63f,1.55f,0.96f,0.98f,1.16f,1.05f,1.00f,1.27f,1.23f,1.50f,1.69f,1.46f,1.86f,1.72f,1.87f,1.24f,1.49f,1.44f,1.69f,1.80f,1.59f,1.69f,1.97f,1.82f,1.94f,1.91f,1.92f,1.99f,1.63f,1.50f,1.74f,1.16f,1.33f,1.38f,1.58f,1.60f,1.77f,1.80f,1.48f,1.21f,1.37f,0.90f,0.97f,0.93f,1.05f,0.97f,1.04f,1.21f,0.99f,0.95f,1.14f,0.92f,1.02f,1.34f,0.94f,0.86f,0.90f,0.74f,0.79f,0.75f,0.81f,0.79f,0.84f,0.86f,0.71f,0.71f,0.73f,0.76f,0.73f,0.77f,0.74f,0.80f,0.85f,0.78f,0.81f,0.89f,0.84f,0.97f,0.92f,0.88f,0.79f,0.85f,0.86f,0.98f,0.92f,1.00f,0.93f,1.06f,1.12f,0.95f,0.74f,0.74f,0.78f,0.79f,0.76f,0.82f,0.79f,0.87f,0.93f,0.85f,0.85f,0.94f,0.90f,1.09f,1.27f,0.99f,1.17f,1.05f,0.96f,1.46f,1.71f,1.62f,1.48f,1.20f,1.34f,1.28f,1.57f,1.35f,0.90f,0.94f,0.85f,0.98f,0.81f,0.89f,0.89f,0.83f,0.82f,0.75f,0.78f,0.73f,0.77f,0.72f,0.76f,0.89f,0.83f,0.91f,0.71f,0.70f,0.72f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f}, + {1.26f,1.39f,1.48f,1.51f,1.64f,1.71f,1.60f,1.58f,1.77f,1.74f,1.91f,1.94f,1.87f,1.97f,1.85f,1.10f,0.97f,1.22f,0.88f,0.92f,0.95f,1.01f,1.11f,1.39f,1.32f,0.88f,0.90f,0.97f,0.96f,0.93f,1.05f,0.99f,1.27f,1.47f,1.20f,1.70f,1.54f,1.76f,1.08f,1.31f,1.33f,1.70f,1.76f,1.55f,1.57f,1.88f,1.85f,1.91f,1.97f,1.99f,1.99f,1.70f,1.65f,1.85f,1.41f,1.54f,1.61f,1.76f,1.80f,1.91f,1.93f,1.52f,1.26f,1.48f,0.92f,0.99f,0.97f,1.18f,1.09f,1.28f,1.39f,0.94f,0.93f,1.05f,0.92f,1.01f,1.31f,0.88f,0.81f,0.86f,0.72f,0.75f,0.74f,0.79f,0.79f,0.86f,0.85f,0.71f,0.73f,0.75f,0.82f,0.77f,0.83f,0.78f,0.85f,0.88f,0.81f,0.88f,0.97f,0.90f,1.18f,1.00f,0.93f,0.86f,0.92f,0.94f,1.14f,0.99f,1.24f,1.03f,1.33f,1.39f,1.11f,0.79f,0.77f,0.84f,0.79f,0.77f,0.84f,0.83f,0.90f,0.98f,0.91f,0.85f,0.92f,0.91f,1.02f,1.26f,1.00f,1.23f,1.19f,0.99f,1.50f,1.84f,1.71f,1.64f,1.38f,1.46f,1.51f,1.76f,1.59f,0.84f,0.88f,0.80f,0.94f,0.79f,0.86f,0.82f,0.77f,0.76f,0.74f,0.74f,0.71f,0.73f,0.70f,0.72f,0.82f,0.77f,0.85f,0.74f,0.70f,0.73f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f,1.00f} + }; + +} diff --git a/src/jake2/render/fast/Draw.java b/src/jake2/render/fast/Draw.java new file mode 100644 index 0000000..b13891d --- /dev/null +++ b/src/jake2/render/fast/Draw.java @@ -0,0 +1,412 @@ +/* + * Draw.java + * Copyright (C) 2003 + * + * $Id: Draw.java,v 1.2 2006-11-21 00:50:46 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 jake2.render.fast; + +import jake2.Defines; +import jake2.client.VID; +import jake2.qcommon.Com; +import jake2.render.image_t; +import jake2.util.Lib; + +import java.awt.Dimension; +import java.nio.ByteBuffer; +import java.nio.IntBuffer; + +/** + * Draw + * (gl_draw.c) + * + * @author cwei + */ +public abstract class Draw extends Image { + + /* + =============== + Draw_InitLocal + =============== + */ + void Draw_InitLocal() { + // load console characters (don't bilerp characters) + draw_chars = GL_FindImage("pics/conchars.pcx", it_pic); + GL_Bind(draw_chars.texnum); + gl.glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gl.glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + + /* + ================ + Draw_Char + + Draws one 8*8 graphics character with 0 being transparent. + It can be clipped to the top of the screen to allow the console to be + smoothly scrolled off. + ================ + */ + public void Draw_Char(int x, int y, int num) { + + num &= 255; + + if ( (num&127) == 32 ) return; // space + + if (y <= -8) return; // totally off screen + + int row = num>>4; + int col = num&15; + + float frow = row*0.0625f; + float fcol = col*0.0625f; + float size = 0.0625f; + + GL_Bind(draw_chars.texnum); + + gl.glBegin (GL_QUADS); + gl.glTexCoord2f (fcol, frow); + gl.glVertex2f (x, y); + gl.glTexCoord2f (fcol + size, frow); + gl.glVertex2f (x+8, y); + gl.glTexCoord2f (fcol + size, frow + size); + gl.glVertex2f (x+8, y+8); + gl.glTexCoord2f (fcol, frow + size); + gl.glVertex2f (x, y+8); + gl.glEnd (); + } + + + /* + ============= + Draw_FindPic + ============= + */ + public image_t Draw_FindPic(String name) { + image_t image = null; + String fullname; + + if (!name.startsWith("/") && !name.startsWith("\\")) + { + fullname = "pics/" + name + ".pcx"; + image = GL_FindImage(fullname, it_pic); + } else { + image = GL_FindImage(name.substring(1), it_pic); + } + return image; + } + + + /* + ============= + Draw_GetPicSize + ============= + */ + public void Draw_GetPicSize(Dimension dim, String pic) { + + image_t image = Draw_FindPic(pic); + dim.width = (image != null) ? image.width : -1; + dim.height = (image != null) ? image.height : -1; + } + + /* + ============= + Draw_StretchPic + ============= + */ + public void Draw_StretchPic (int x, int y, int w, int h, String pic) { + + image_t image; + + image = Draw_FindPic(pic); + if (image == null) + { + VID.Printf (Defines.PRINT_ALL, "Can't find pic: " + pic +'\n'); + return; + } + + if (scrap_dirty) + Scrap_Upload(); + + if ( ( ( gl_config.renderer == GL_RENDERER_MCD ) || ( (gl_config.renderer & GL_RENDERER_RENDITION) != 0) ) && !image.has_alpha) + gl.glDisable(GL_ALPHA_TEST); + + GL_Bind(image.texnum); + gl.glBegin (GL_QUADS); + gl.glTexCoord2f (image.sl, image.tl); + gl.glVertex2f (x, y); + gl.glTexCoord2f (image.sh, image.tl); + gl.glVertex2f (x+w, y); + gl.glTexCoord2f (image.sh, image.th); + gl.glVertex2f (x+w, y+h); + gl.glTexCoord2f (image.sl, image.th); + gl.glVertex2f (x, y+h); + gl.glEnd (); + + if ( ( ( gl_config.renderer == GL_RENDERER_MCD ) || ( (gl_config.renderer & GL_RENDERER_RENDITION) !=0 ) ) && !image.has_alpha) + gl.glEnable(GL_ALPHA_TEST); + } + + + /* + ============= + Draw_Pic + ============= + */ + public void Draw_Pic(int x, int y, String pic) + { + image_t image; + + image = Draw_FindPic(pic); + if (image == null) + { + VID.Printf(Defines.PRINT_ALL, "Can't find pic: " +pic + '\n'); + return; + } + if (scrap_dirty) + Scrap_Upload(); + + if ( ( ( gl_config.renderer == GL_RENDERER_MCD ) || ( (gl_config.renderer & GL_RENDERER_RENDITION) != 0 ) ) && !image.has_alpha) + gl.glDisable (GL_ALPHA_TEST); + + GL_Bind(image.texnum); + + gl.glBegin (GL_QUADS); + gl.glTexCoord2f (image.sl, image.tl); + gl.glVertex2f (x, y); + gl.glTexCoord2f (image.sh, image.tl); + gl.glVertex2f (x+image.width, y); + gl.glTexCoord2f (image.sh, image.th); + gl.glVertex2f (x+image.width, y+image.height); + gl.glTexCoord2f (image.sl, image.th); + gl.glVertex2f (x, y+image.height); + gl.glEnd (); + + if ( ( ( gl_config.renderer == GL_RENDERER_MCD ) || ( (gl_config.renderer & GL_RENDERER_RENDITION) != 0 ) ) && !image.has_alpha) + gl.glEnable (GL_ALPHA_TEST); + } + + /* + ============= + Draw_TileClear + + This repeats a 64*64 tile graphic to fill the screen around a sized down + refresh window. + ============= + */ + public void Draw_TileClear(int x, int y, int w, int h, String pic) { + image_t image; + + image = Draw_FindPic(pic); + if (image == null) + { + VID.Printf(Defines.PRINT_ALL, "Can't find pic: " + pic + '\n'); + return; + } + + if ( ( ( gl_config.renderer == GL_RENDERER_MCD ) || ( (gl_config.renderer & GL_RENDERER_RENDITION) != 0 ) ) && !image.has_alpha) + gl.glDisable(GL_ALPHA_TEST); + + GL_Bind(image.texnum); + gl.glBegin (GL_QUADS); + gl.glTexCoord2f(x/64.0f, y/64.0f); + gl.glVertex2f (x, y); + gl.glTexCoord2f( (x+w)/64.0f, y/64.0f); + gl.glVertex2f(x+w, y); + gl.glTexCoord2f( (x+w)/64.0f, (y+h)/64.0f); + gl.glVertex2f(x+w, y+h); + gl.glTexCoord2f( x/64.0f, (y+h)/64.0f ); + gl.glVertex2f (x, y+h); + gl.glEnd (); + + if ( ( ( gl_config.renderer == GL_RENDERER_MCD ) || ( (gl_config.renderer & GL_RENDERER_RENDITION) != 0 ) ) && !image.has_alpha) + gl.glEnable(GL_ALPHA_TEST); + } + + + /* + ============= + Draw_Fill + + Fills a box of pixels with a single color + ============= + */ + public void Draw_Fill(int x, int y, int w, int h, int colorIndex) { + + if ( colorIndex > 255) + Com.Error(Defines.ERR_FATAL, "Draw_Fill: bad color"); + + gl.glDisable(GL_TEXTURE_2D); + + int color = d_8to24table[colorIndex]; + + gl.glColor3ub( + (byte)((color >> 0) & 0xff), // r + (byte)((color >> 8) & 0xff), // g + (byte)((color >> 16) & 0xff) // b + ); + + gl.glBegin (GL_QUADS); + + gl.glVertex2f(x,y); + gl.glVertex2f(x+w, y); + gl.glVertex2f(x+w, y+h); + gl.glVertex2f(x, y+h); + + gl.glEnd(); + gl.glColor3f(1,1,1); + gl.glEnable(GL_TEXTURE_2D); + } + + //============================================================================= + + /* + ================ + Draw_FadeScreen + ================ + */ + public void Draw_FadeScreen() { + gl.glEnable(GL_BLEND); + gl.glDisable(GL_TEXTURE_2D); + gl.glColor4f(0, 0, 0, 0.8f); + gl.glBegin(GL_QUADS); + + gl.glVertex2f(0,0); + gl.glVertex2f(vid.width, 0); + gl.glVertex2f(vid.width, vid.height); + gl.glVertex2f(0, vid.height); + + gl.glEnd(); + gl.glColor4f(1,1,1,1); + gl.glEnable(GL_TEXTURE_2D); + gl.glDisable(GL_BLEND); + } + +// ==================================================================== + + IntBuffer image32=Lib.newIntBuffer(256*256); + ByteBuffer image8=Lib.newByteBuffer(256*256); + + + /* + ============= + Draw_StretchRaw + ============= + */ + public void Draw_StretchRaw (int x, int y, int w, int h, int cols, int rows, byte[] data) + { + int i, j, trows; + int sourceIndex; + int frac, fracstep; + float hscale; + int row; + float t; + + GL_Bind(0); + + if (rows<=256) + { + hscale = 1; + trows = rows; + } + else + { + hscale = rows/256.0f; + trows = 256; + } + t = rows*hscale / 256; + + if ( !qglColorTableEXT ) + { + //int[] image32 = new int[256*256]; + image32.clear(); + int destIndex = 0; + + for (i=0 ; i<trows ; i++) + { + row = (int)(i*hscale); + if (row > rows) + break; + sourceIndex = cols*row; + destIndex = i*256; + fracstep = cols*0x10000/256; + frac = fracstep >> 1; + for (j=0 ; j<256 ; j++) + { + image32.put(destIndex + j, r_rawpalette[data[sourceIndex + (frac>>16)] & 0xff]); + frac += fracstep; + } + } + gl.glTexImage2D (GL_TEXTURE_2D, 0, gl_tex_solid_format, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, image32); + } + else + { + //byte[] image8 = new byte[256*256]; + image8.clear(); + int destIndex = 0;; + + for (i=0 ; i<trows ; i++) + { + row = (int)(i*hscale); + if (row > rows) + break; + sourceIndex = cols*row; + destIndex = i*256; + fracstep = cols*0x10000/256; + frac = fracstep >> 1; + for (j=0 ; j<256 ; j++) + { + image8.put(destIndex + j, data[sourceIndex + (frac>>16)]); + frac += fracstep; + } + } + + gl.glTexImage2D( GL_TEXTURE_2D, + 0, + GL_COLOR_INDEX8_EXT, + 256, 256, + 0, + GL_COLOR_INDEX, + GL_UNSIGNED_BYTE, + image8 ); + } + gl.glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl.glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + if ( ( gl_config.renderer == GL_RENDERER_MCD ) || ( (gl_config.renderer & GL_RENDERER_RENDITION) != 0 ) ) + gl.glDisable (GL_ALPHA_TEST); + + gl.glBegin (GL_QUADS); + gl.glTexCoord2f (0, 0); + gl.glVertex2f (x, y); + gl.glTexCoord2f (1, 0); + gl.glVertex2f (x+w, y); + gl.glTexCoord2f (1, t); + gl.glVertex2f (x+w, y+h); + gl.glTexCoord2f (0, t); + gl.glVertex2f (x, y+h); + gl.glEnd (); + + if ( ( gl_config.renderer == GL_RENDERER_MCD ) || ( (gl_config.renderer & GL_RENDERER_RENDITION) != 0 ) ) + gl.glEnable (GL_ALPHA_TEST); + } + +} diff --git a/src/jake2/render/fast/Image.java b/src/jake2/render/fast/Image.java new file mode 100644 index 0000000..a724cb7 --- /dev/null +++ b/src/jake2/render/fast/Image.java @@ -0,0 +1,1677 @@ +/* + * Image.java + * Copyright (C) 2003 + * + * $Id: Image.java,v 1.2 2006-11-21 00:50:46 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 jake2.render.fast; + +import jake2.Defines; +import jake2.client.VID; +import jake2.client.particle_t; +import jake2.game.cvar_t; +import jake2.qcommon.*; +import jake2.render.image_t; +import jake2.util.Lib; +import jake2.util.Vargs; + +import java.awt.Dimension; +import java.awt.geom.AffineTransform; +import java.awt.image.AffineTransformOp; +import java.awt.image.BufferedImage; +import java.nio.*; +import java.util.*; + +/** + * Image + * + * @author cwei + */ +public abstract class Image extends Main { + + image_t draw_chars; + + image_t[] gltextures = new image_t[MAX_GLTEXTURES]; + //Map gltextures = new Hashtable(MAX_GLTEXTURES); // image_t + int numgltextures; + int base_textureid; // gltextures[i] = base_textureid+i + + byte[] intensitytable = new byte[256]; + byte[] gammatable = new byte[256]; + + cvar_t intensity; + + // + // qboolean GL_Upload8 (byte *data, int width, int height, qboolean mipmap, qboolean is_sky ); + // qboolean GL_Upload32 (unsigned *data, int width, int height, qboolean mipmap); + // + + int gl_solid_format = 3; + int gl_alpha_format = 4; + + int gl_tex_solid_format = 3; + int gl_tex_alpha_format = 4; + + int gl_filter_min = GL_LINEAR_MIPMAP_NEAREST; + int gl_filter_max = GL_LINEAR; + + Image() { + // init the texture cache + for (int i = 0; i < gltextures.length; i++) + { + gltextures[i] = new image_t(i); + } + numgltextures = 0; + } + + void GL_SetTexturePalette(int[] palette) { + + assert(palette != null && palette.length == 256) : "int palette[256] bug"; + + int i; + //byte[] temptable = new byte[768]; + + if (qglColorTableEXT && gl_ext_palettedtexture.value != 0.0f) + { + ByteBuffer temptable = Lib.newByteBuffer(768); + for (i = 0; i < 256; i++) { + temptable.put(i * 3 + 0, (byte) ((palette[i] >> 0) & 0xff)); + temptable.put(i * 3 + 1, (byte) ((palette[i] >> 8) & 0xff)); + temptable.put(i * 3 + 2, (byte) ((palette[i] >> 16) & 0xff)); + } + + gl.glColorTable(GL_SHARED_TEXTURE_PALETTE_EXT, GL_RGB, 256, GL_RGB, GL_UNSIGNED_BYTE, temptable); + } + } + + void GL_EnableMultitexture(boolean enable) { + if (enable) { + GL_SelectTexture(TEXTURE1); + gl.glEnable(GL_TEXTURE_2D); + GL_TexEnv(GL_REPLACE); + } + else { + GL_SelectTexture(TEXTURE1); + gl.glDisable(GL_TEXTURE_2D); + GL_TexEnv(GL_REPLACE); + } + GL_SelectTexture(TEXTURE0); + GL_TexEnv(GL_REPLACE); + } + + void GL_SelectTexture(int texture /* GLenum */) { + int tmu; + + tmu = (texture == TEXTURE0) ? 0 : 1; + + if (tmu == gl_state.currenttmu) { + return; + } + + gl_state.currenttmu = tmu; + + gl.glActiveTextureARB(texture); + gl.glClientActiveTextureARB(texture); + } + + int[] lastmodes = { -1, -1 }; + + void GL_TexEnv(int mode /* GLenum */ + ) { + + if (mode != lastmodes[gl_state.currenttmu]) { + gl.glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, mode); + lastmodes[gl_state.currenttmu] = mode; + } + } + + void GL_Bind(int texnum) { + + if ((gl_nobind.value != 0) && (draw_chars != null)) { + // performance evaluation option + texnum = draw_chars.texnum; + } + if (gl_state.currenttextures[gl_state.currenttmu] == texnum) + return; + + gl_state.currenttextures[gl_state.currenttmu] = texnum; + gl.glBindTexture(GL_TEXTURE_2D, texnum); + } + + void GL_MBind(int target /* GLenum */, int texnum) { + GL_SelectTexture(target); + if (target == TEXTURE0) { + if (gl_state.currenttextures[0] == texnum) + return; + } + else { + if (gl_state.currenttextures[1] == texnum) + return; + } + GL_Bind(texnum); + } + + // glmode_t + static class glmode_t { + String name; + int minimize, maximize; + + glmode_t(String name, int minimize, int maximze) { + this.name = name; + this.minimize = minimize; + this.maximize = maximze; + } + } + + static final glmode_t modes[] = + { + new glmode_t("GL_NEAREST", GL_NEAREST, GL_NEAREST), + new glmode_t("GL_LINEAR", GL_LINEAR, GL_LINEAR), + new glmode_t("GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST), + new glmode_t("GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR), + new glmode_t("GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST), + new glmode_t("GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR)}; + + static final int NUM_GL_MODES = modes.length; + + // gltmode_t + static class gltmode_t { + String name; + int mode; + + gltmode_t(String name, int mode) { + this.name = name; + this.mode = mode; + } + } + + static final gltmode_t[] gl_alpha_modes = + { + new gltmode_t("default", 4), + new gltmode_t("GL_RGBA", GL_RGBA), + new gltmode_t("GL_RGBA8", GL_RGBA8), + new gltmode_t("GL_RGB5_A1", GL_RGB5_A1), + new gltmode_t("GL_RGBA4", GL_RGBA4), + new gltmode_t("GL_RGBA2", GL_RGBA2), + }; + + static final int NUM_GL_ALPHA_MODES = gl_alpha_modes.length; + + static final gltmode_t[] gl_solid_modes = + { + new gltmode_t("default", 3), + new gltmode_t("GL_RGB", GL_RGB), + new gltmode_t("GL_RGB8", GL_RGB8), + new gltmode_t("GL_RGB5", GL_RGB5), + new gltmode_t("GL_RGB4", GL_RGB4), + new gltmode_t("GL_R3_G3_B2", GL_R3_G3_B2), + }; + + static final int NUM_GL_SOLID_MODES = gl_solid_modes.length; + + /* + =============== + GL_TextureMode + =============== + */ + void GL_TextureMode(String string) { + + int i; + for (i = 0; i < NUM_GL_MODES; i++) { + if (modes[i].name.equalsIgnoreCase(string)) + break; + } + + if (i == NUM_GL_MODES) { + VID.Printf(Defines.PRINT_ALL, "bad filter name: [" + string + "]\n"); + return; + } + + gl_filter_min = modes[i].minimize; + gl_filter_max = modes[i].maximize; + + image_t glt; + // change all the existing mipmap texture objects + for (i = 0; i < numgltextures; i++) { + glt = gltextures[i]; + + if (glt.type != it_pic && glt.type != it_sky) { + GL_Bind(glt.texnum); + gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); + gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + } + } + } + + /* + =============== + GL_TextureAlphaMode + =============== + */ + void GL_TextureAlphaMode(String string) { + + int i; + for (i = 0; i < NUM_GL_ALPHA_MODES; i++) { + if (gl_alpha_modes[i].name.equalsIgnoreCase(string)) + break; + } + + if (i == NUM_GL_ALPHA_MODES) { + VID.Printf(Defines.PRINT_ALL, "bad alpha texture mode name: [" + string + "]\n"); + return; + } + + gl_tex_alpha_format = gl_alpha_modes[i].mode; + } + + /* + =============== + GL_TextureSolidMode + =============== + */ + void GL_TextureSolidMode(String string) { + int i; + for (i = 0; i < NUM_GL_SOLID_MODES; i++) { + if (gl_solid_modes[i].name.equalsIgnoreCase(string)) + break; + } + + if (i == NUM_GL_SOLID_MODES) { + VID.Printf(Defines.PRINT_ALL, "bad solid texture mode name: [" + string + "]\n"); + return; + } + + gl_tex_solid_format = gl_solid_modes[i].mode; + } + + /* + =============== + GL_ImageList_f + =============== + */ + void GL_ImageList_f() { + + image_t image; + int texels; + final String[] palstrings = { "RGB", "PAL" }; + + VID.Printf(Defines.PRINT_ALL, "------------------\n"); + texels = 0; + + for (int i = 0; i < numgltextures; i++) { + image = gltextures[i]; + if (image.texnum <= 0) + continue; + + texels += image.upload_width * image.upload_height; + switch (image.type) { + case it_skin : + VID.Printf(Defines.PRINT_ALL, "M"); + break; + case it_sprite : + VID.Printf(Defines.PRINT_ALL, "S"); + break; + case it_wall : + VID.Printf(Defines.PRINT_ALL, "W"); + break; + case it_pic : + VID.Printf(Defines.PRINT_ALL, "P"); + break; + default : + VID.Printf(Defines.PRINT_ALL, " "); + break; + } + + VID.Printf( + Defines.PRINT_ALL, + " %3i %3i %s: %s\n", + new Vargs(4).add(image.upload_width).add(image.upload_height).add(palstrings[(image.paletted) ? 1 : 0]).add( + image.name)); + } + VID.Printf(Defines.PRINT_ALL, "Total texel count (not counting mipmaps): " + texels + '\n'); + } + + /* + ============================================================================= + + scrap allocation + + Allocate all the little status bar objects into a single texture + to crutch up inefficient hardware / drivers + + ============================================================================= + */ + + static final int MAX_SCRAPS = 1; + static final int BLOCK_WIDTH = 256; + static final int BLOCK_HEIGHT = 256; + + int[][] scrap_allocated = new int[MAX_SCRAPS][BLOCK_WIDTH]; + byte[][] scrap_texels = new byte[MAX_SCRAPS][BLOCK_WIDTH * BLOCK_HEIGHT]; + boolean scrap_dirty; + + static class pos_t { + int x, y; + + pos_t(int x, int y) { + this.x = x; + this.y = y; + } + } + + // returns a texture number and the position inside it + int Scrap_AllocBlock(int w, int h, pos_t pos) { + int i, j; + int best, best2; + int texnum; + + for (texnum = 0; texnum < MAX_SCRAPS; texnum++) { + best = BLOCK_HEIGHT; + + for (i = 0; i < BLOCK_WIDTH - w; i++) { + best2 = 0; + + for (j = 0; j < w; j++) { + if (scrap_allocated[texnum][i + j] >= best) + break; + if (scrap_allocated[texnum][i + j] > best2) + best2 = scrap_allocated[texnum][i + j]; + } + if (j == w) { // this is a valid spot + pos.x = i; + pos.y = best = best2; + } + } + + if (best + h > BLOCK_HEIGHT) + continue; + + for (i = 0; i < w; i++) + scrap_allocated[texnum][pos.x + i] = best + h; + + return texnum; + } + + return -1; + // Sys_Error ("Scrap_AllocBlock: full"); + } + + int scrap_uploads = 0; + + void Scrap_Upload() { + scrap_uploads++; + GL_Bind(TEXNUM_SCRAPS); + GL_Upload8(scrap_texels[0], BLOCK_WIDTH, BLOCK_HEIGHT, false, false); + scrap_dirty = false; + } + + /* + ================================================================= + + PCX LOADING + + ================================================================= + */ + + /* + ============== + LoadPCX + ============== + */ + byte[] LoadPCX(String filename, byte[][] palette, Dimension dim) { + qfiles.pcx_t pcx; + + // + // load the file + // + byte[] raw = FS.LoadFile(filename); + + if (raw == null) { + VID.Printf(Defines.PRINT_DEVELOPER, "Bad pcx file " + filename + '\n'); + return null; + } + + // + // parse the PCX file + // + pcx = new qfiles.pcx_t(raw); + + if (pcx.manufacturer != 0x0a + || pcx.version != 5 + || pcx.encoding != 1 + || pcx.bits_per_pixel != 8 + || pcx.xmax >= 640 + || pcx.ymax >= 480) { + + VID.Printf(Defines.PRINT_ALL, "Bad pcx file " + filename + '\n'); + return null; + } + + int width = pcx.xmax - pcx.xmin + 1; + int height = pcx.ymax - pcx.ymin + 1; + + byte[] pix = new byte[width * height]; + + if (palette != null) { + palette[0] = new byte[768]; + System.arraycopy(raw, raw.length - 768, palette[0], 0, 768); + } + + if (dim != null) { + dim.width = width; + dim.height = height; + } + + // + // decode pcx + // + int count = 0; + byte dataByte = 0; + int runLength = 0; + int x, y; + + for (y = 0; y < height; y++) { + for (x = 0; x < width;) { + + dataByte = pcx.data.get(); + + if ((dataByte & 0xC0) == 0xC0) { + runLength = dataByte & 0x3F; + dataByte = pcx.data.get(); + // write runLength pixel + while (runLength-- > 0) { + pix[count++] = dataByte; + x++; + } + } + else { + // write one pixel + pix[count++] = dataByte; + x++; + } + } + } + return pix; + } + + private Throwable gotoBreakOut = new Throwable(); + private Throwable gotoDone = gotoBreakOut; + + // /* + // ========================================================= + // + // TARGA LOADING + // + // ========================================================= + // */ + /* + ============= + LoadTGA + ============= + */ + byte[] LoadTGA(String name, Dimension dim) { + int columns, rows, numPixels; + int pixbuf; // index into pic + int row, column; + byte[] raw; + ByteBuffer buf_p; + int length; + qfiles.tga_t targa_header; + byte[] pic = null; + + // + // load the file + // + raw = FS.LoadFile(name); + + if (raw == null) + { + VID.Printf(Defines.PRINT_DEVELOPER, "Bad tga file "+ name +'\n'); + return null; + } + + targa_header = new qfiles.tga_t(raw); + + if (targa_header.image_type != 2 && targa_header.image_type != 10) + Com.Error(Defines.ERR_DROP, "LoadTGA: Only type 2 and 10 targa RGB images supported\n"); + + if (targa_header.colormap_type != 0 || (targa_header.pixel_size != 32 && targa_header.pixel_size != 24)) + Com.Error (Defines.ERR_DROP, "LoadTGA: Only 32 or 24 bit images supported (no colormaps)\n"); + + columns = targa_header.width; + rows = targa_header.height; + numPixels = columns * rows; + + if (dim != null) { + dim.width = columns; + dim.height = rows; + } + + pic = new byte[numPixels * 4]; // targa_rgba; + + if (targa_header.id_length != 0) + targa_header.data.position(targa_header.id_length); // skip TARGA image comment + + buf_p = targa_header.data; + + byte red,green,blue,alphabyte; + red = green = blue = alphabyte = 0; + int packetHeader, packetSize, j; + + if (targa_header.image_type==2) { // Uncompressed, RGB images + for(row=rows-1; row>=0; row--) { + + pixbuf = row * columns * 4; + + for(column=0; column<columns; column++) { + switch (targa_header.pixel_size) { + case 24: + + blue = buf_p.get(); + green = buf_p.get(); + red = buf_p.get(); + pic[pixbuf++] = red; + pic[pixbuf++] = green; + pic[pixbuf++] = blue; + pic[pixbuf++] = (byte)255; + break; + case 32: + blue = buf_p.get(); + green = buf_p.get(); + red = buf_p.get(); + alphabyte = buf_p.get(); + pic[pixbuf++] = red; + pic[pixbuf++] = green; + pic[pixbuf++] = blue; + pic[pixbuf++] = alphabyte; + break; + } + } + } + } + else if (targa_header.image_type==10) { // Runlength encoded RGB images + for(row=rows-1; row>=0; row--) { + + pixbuf = row * columns * 4; + try { + + for(column=0; column<columns; ) { + + packetHeader= buf_p.get() & 0xFF; + packetSize = 1 + (packetHeader & 0x7f); + + if ((packetHeader & 0x80) != 0) { // run-length packet + switch (targa_header.pixel_size) { + case 24: + blue = buf_p.get(); + green = buf_p.get(); + red = buf_p.get(); + alphabyte = (byte)255; + break; + case 32: + blue = buf_p.get(); + green = buf_p.get(); + red = buf_p.get(); + alphabyte = buf_p.get(); + break; + } + + for(j=0;j<packetSize;j++) { + pic[pixbuf++]=red; + pic[pixbuf++]=green; + pic[pixbuf++]=blue; + pic[pixbuf++]=alphabyte; + column++; + if (column==columns) { // run spans across rows + column=0; + if (row>0) + row--; + else + // goto label breakOut; + throw gotoBreakOut; + + pixbuf = row * columns * 4; + } + } + } + else { // non run-length packet + for(j=0;j<packetSize;j++) { + switch (targa_header.pixel_size) { + case 24: + blue = buf_p.get(); + green = buf_p.get(); + red = buf_p.get(); + pic[pixbuf++] = red; + pic[pixbuf++] = green; + pic[pixbuf++] = blue; + pic[pixbuf++] = (byte)255; + break; + case 32: + blue = buf_p.get(); + green = buf_p.get(); + red = buf_p.get(); + alphabyte = buf_p.get(); + pic[pixbuf++] = red; + pic[pixbuf++] = green; + pic[pixbuf++] = blue; + pic[pixbuf++] = alphabyte; + break; + } + column++; + if (column==columns) { // pixel packet run spans across rows + column=0; + if (row>0) + row--; + else + // goto label breakOut; + throw gotoBreakOut; + + pixbuf = row * columns * 4; + } + } + } + } + } catch (Throwable e){ + // label breakOut: + } + } + } + return pic; + } + + /* + ==================================================================== + + IMAGE FLOOD FILLING + + ==================================================================== + */ + + /* + ================= + Mod_FloodFillSkin + + Fill background pixels so mipmapping doesn't have haloes + ================= + */ + + static class floodfill_t { + short x, y; + } + + // must be a power of 2 + static final int FLOODFILL_FIFO_SIZE = 0x1000; + static final int FLOODFILL_FIFO_MASK = FLOODFILL_FIFO_SIZE - 1; + // + // #define FLOODFILL_STEP( off, dx, dy ) \ + // { \ + // if (pos[off] == fillcolor) \ + // { \ + // pos[off] = 255; \ + // fifo[inpt].x = x + (dx), fifo[inpt].y = y + (dy); \ + // inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; \ + // } \ + // else if (pos[off] != 255) fdc = pos[off]; \ + // } + + // void FLOODFILL_STEP( int off, int dx, int dy ) + // { + // if (pos[off] == fillcolor) + // { + // pos[off] = 255; + // fifo[inpt].x = x + dx; fifo[inpt].y = y + dy; + // inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; + // } + // else if (pos[off] != 255) fdc = pos[off]; + // } + static floodfill_t[] fifo = new floodfill_t[FLOODFILL_FIFO_SIZE]; + static { + for (int j = 0; j < fifo.length; j++) { + fifo[j] = new floodfill_t(); + } + } + // TODO check this: R_FloodFillSkin( byte[] skin, int skinwidth, int skinheight) + void R_FloodFillSkin(byte[] skin, int skinwidth, int skinheight) { + // byte fillcolor = *skin; // assume this is the pixel to fill + int fillcolor = skin[0] & 0xff; +// floodfill_t[] fifo = new floodfill_t[FLOODFILL_FIFO_SIZE]; + int inpt = 0, outpt = 0; + int filledcolor = -1; + int i; + +// for (int j = 0; j < fifo.length; j++) { +// fifo[j] = new floodfill_t(); +// } + + if (filledcolor == -1) { + filledcolor = 0; + // attempt to find opaque black + for (i = 0; i < 256; ++i) + // TODO check this + if (d_8to24table[i] == 0xFF000000) { // alpha 1.0 + //if (d_8to24table[i] == (255 << 0)) // alpha 1.0 + filledcolor = i; + break; + } + } + + // can't fill to filled color or to transparent color (used as visited marker) + if ((fillcolor == filledcolor) || (fillcolor == 255)) { + return; + } + + fifo[inpt].x = 0; + fifo[inpt].y = 0; + inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; + + while (outpt != inpt) { + int x = fifo[outpt].x; + int y = fifo[outpt].y; + int fdc = filledcolor; + // byte *pos = &skin[x + skinwidth * y]; + int pos = x + skinwidth * y; + // + outpt = (outpt + 1) & FLOODFILL_FIFO_MASK; + + int off, dx, dy; + + if (x > 0) { + // FLOODFILL_STEP( -1, -1, 0 ); + off = -1; + dx = -1; + dy = 0; + if (skin[pos + off] == (byte) fillcolor) { + skin[pos + off] = (byte) 255; + fifo[inpt].x = (short) (x + dx); + fifo[inpt].y = (short) (y + dy); + inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; + } + else if (skin[pos + off] != (byte) 255) + fdc = skin[pos + off] & 0xff; + } + + if (x < skinwidth - 1) { + // FLOODFILL_STEP( 1, 1, 0 ); + off = 1; + dx = 1; + dy = 0; + if (skin[pos + off] == (byte) fillcolor) { + skin[pos + off] = (byte) 255; + fifo[inpt].x = (short) (x + dx); + fifo[inpt].y = (short) (y + dy); + inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; + } + else if (skin[pos + off] != (byte) 255) + fdc = skin[pos + off] & 0xff; + } + + if (y > 0) { + // FLOODFILL_STEP( -skinwidth, 0, -1 ); + off = -skinwidth; + dx = 0; + dy = -1; + if (skin[pos + off] == (byte) fillcolor) { + skin[pos + off] = (byte) 255; + fifo[inpt].x = (short) (x + dx); + fifo[inpt].y = (short) (y + dy); + inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; + } + else if (skin[pos + off] != (byte) 255) + fdc = skin[pos + off] & 0xff; + } + + if (y < skinheight - 1) { + // FLOODFILL_STEP( skinwidth, 0, 1 ); + off = skinwidth; + dx = 0; + dy = 1; + if (skin[pos + off] == (byte) fillcolor) { + skin[pos + off] = (byte) 255; + fifo[inpt].x = (short) (x + dx); + fifo[inpt].y = (short) (y + dy); + inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; + } + else if (skin[pos + off] != (byte) 255) + fdc = skin[pos + off] & 0xff; + + } + + skin[x + skinwidth * y] = (byte) fdc; + } + } + + // ======================================================= + + /* + ================ + GL_ResampleTexture + ================ + */ + // cwei :-) + void GL_ResampleTexture(int[] in, int inwidth, int inheight, int[] out, int outwidth, int outheight) { + // int i, j; + // unsigned *inrow, *inrow2; + // int frac, fracstep; + // int[] p1 = new int[1024]; + // int[] p2 = new int[1024]; + // + + // *** this source do the same *** + BufferedImage image = new BufferedImage(inwidth, inheight, BufferedImage.TYPE_INT_ARGB); + + image.setRGB(0, 0, inwidth, inheight, in, 0, inwidth); + + AffineTransformOp op = + new AffineTransformOp( + AffineTransform.getScaleInstance(outwidth * 1.0 / inwidth, outheight * 1.0 / inheight), + AffineTransformOp.TYPE_NEAREST_NEIGHBOR); + BufferedImage tmp = op.filter(image, null); + + tmp.getRGB(0, 0, outwidth, outheight, out, 0, outwidth); + + // *** end *** + + // byte *pix1, *pix2, *pix3, *pix4; + // + // fracstep = inwidth*0x10000/outwidth; + // + // frac = fracstep>>2; + // for (i=0 ; i<outwidth ; i++) + // { + // p1[i] = 4*(frac>>16); + // frac += fracstep; + // } + // frac = 3*(fracstep>>2); + // for (i=0 ; i<outwidth ; i++) + // { + // p2[i] = 4*(frac>>16); + // frac += fracstep; + // } + // + // for (i=0 ; i<outheight ; i++, out += outwidth) + // { + // inrow = in + inwidth*(int)((i+0.25)*inheight/outheight); + // inrow2 = in + inwidth*(int)((i+0.75)*inheight/outheight); + // frac = fracstep >> 1; + // for (j=0 ; j<outwidth ; j++) + // { + // pix1 = (byte *)inrow + p1[j]; + // pix2 = (byte *)inrow + p2[j]; + // pix3 = (byte *)inrow2 + p1[j]; + // pix4 = (byte *)inrow2 + p2[j]; + // ((byte *)(out+j))[0] = (pix1[0] + pix2[0] + pix3[0] + pix4[0])>>2; + // ((byte *)(out+j))[1] = (pix1[1] + pix2[1] + pix3[1] + pix4[1])>>2; + // ((byte *)(out+j))[2] = (pix1[2] + pix2[2] + pix3[2] + pix4[2])>>2; + // ((byte *)(out+j))[3] = (pix1[3] + pix2[3] + pix3[3] + pix4[3])>>2; + // } + // } + } + + /* + ================ + GL_LightScaleTexture + + Scale up the pixel values in a texture to increase the + lighting range + ================ + */ + void GL_LightScaleTexture(int[] in, int inwidth, int inheight, boolean only_gamma) { + if (only_gamma) { + int i, c; + int r, g, b, color; + + c = inwidth * inheight; + for (i = 0; i < c; i++) { + color = in[i]; + r = (color >> 0) & 0xFF; + g = (color >> 8) & 0xFF; + b = (color >> 16) & 0xFF; + + r = gammatable[r] & 0xFF; + g = gammatable[g] & 0xFF; + b = gammatable[b] & 0xFF; + + in[i] = (r << 0) | (g << 8) | (b << 16) | (color & 0xFF000000); + } + } + else { + int i, c; + int r, g, b, color; + + c = inwidth * inheight; + for (i = 0; i < c; i++) { + color = in[i]; + r = (color >> 0) & 0xFF; + g = (color >> 8) & 0xFF; + b = (color >> 16) & 0xFF; + + r = gammatable[intensitytable[r] & 0xFF] & 0xFF; + g = gammatable[intensitytable[g] & 0xFF] & 0xFF; + b = gammatable[intensitytable[b] & 0xFF] & 0xFF; + + in[i] = (r << 0) | (g << 8) | (b << 16) | (color & 0xFF000000); + } + + } + } + + /* + ================ + GL_MipMap + + Operates in place, quartering the size of the texture + ================ + */ + void GL_MipMap(int[] in, int width, int height) { + int i, j; + int[] out; + + out = in; + + int inIndex = 0; + int outIndex = 0; + + int r, g, b, a; + int p1, p2, p3, p4; + + for (i = 0; i < height; i += 2, inIndex += width) { + for (j = 0; j < width; j += 2, outIndex += 1, inIndex += 2) { + + p1 = in[inIndex + 0]; + p2 = in[inIndex + 1]; + p3 = in[inIndex + width + 0]; + p4 = in[inIndex + width + 1]; + + r = (((p1 >> 0) & 0xFF) + ((p2 >> 0) & 0xFF) + ((p3 >> 0) & 0xFF) + ((p4 >> 0) & 0xFF)) >> 2; + g = (((p1 >> 8) & 0xFF) + ((p2 >> 8) & 0xFF) + ((p3 >> 8) & 0xFF) + ((p4 >> 8) & 0xFF)) >> 2; + b = (((p1 >> 16) & 0xFF) + ((p2 >> 16) & 0xFF) + ((p3 >> 16) & 0xFF) + ((p4 >> 16) & 0xFF)) >> 2; + a = (((p1 >> 24) & 0xFF) + ((p2 >> 24) & 0xFF) + ((p3 >> 24) & 0xFF) + ((p4 >> 24) & 0xFF)) >> 2; + + out[outIndex] = (r << 0) | (g << 8) | (b << 16) | (a << 24); + } + } + } + + /* + =============== + GL_Upload32 + + Returns has_alpha + =============== + */ + void GL_BuildPalettedTexture(ByteBuffer paletted_texture, int[] scaled, int scaled_width, int scaled_height) { + + int r, g, b, c; + int size = scaled_width * scaled_height; + + for (int i = 0; i < size; i++) { + + r = (scaled[i] >> 3) & 31; + g = (scaled[i] >> 10) & 63; + b = (scaled[i] >> 19) & 31; + + c = r | (g << 5) | (b << 11); + + paletted_texture.put(i, gl_state.d_16to8table[c]); + } + } + + int upload_width, upload_height; + boolean uploaded_paletted; + + /* + =============== + GL_Upload32 + + Returns has_alpha + =============== + */ + int[] scaled = new int[256 * 256]; + //byte[] paletted_texture = new byte[256 * 256]; + ByteBuffer paletted_texture = Lib.newByteBuffer(256*256); + IntBuffer tex = Lib.newIntBuffer(512 * 256, ByteOrder.LITTLE_ENDIAN); + + boolean GL_Upload32(int[] data, int width, int height, boolean mipmap) { + int samples; + int scaled_width, scaled_height; + int i, c; + int comp; + + Arrays.fill(scaled, 0); + // Arrays.fill(paletted_texture, (byte)0); + paletted_texture.clear(); + for (int j=0; j<256*256; j++) paletted_texture.put(j,(byte)0); + + uploaded_paletted = false; + + for (scaled_width = 1; scaled_width < width; scaled_width <<= 1); + if (gl_round_down.value > 0.0f && scaled_width > width && mipmap) + scaled_width >>= 1; + for (scaled_height = 1; scaled_height < height; scaled_height <<= 1); + if (gl_round_down.value > 0.0f && scaled_height > height && mipmap) + scaled_height >>= 1; + + // let people sample down the world textures for speed + if (mipmap) { + scaled_width >>= (int) gl_picmip.value; + scaled_height >>= (int) gl_picmip.value; + } + + // don't ever bother with >256 textures + if (scaled_width > 256) + scaled_width = 256; + if (scaled_height > 256) + scaled_height = 256; + + if (scaled_width < 1) + scaled_width = 1; + if (scaled_height < 1) + scaled_height = 1; + + upload_width = scaled_width; + upload_height = scaled_height; + + if (scaled_width * scaled_height > 256 * 256) + Com.Error(Defines.ERR_DROP, "GL_Upload32: too big"); + + // scan the texture for any non-255 alpha + c = width * height; + samples = gl_solid_format; + + for (i = 0; i < c; i++) { + if ((data[i] & 0xff000000) != 0xff000000) { + samples = gl_alpha_format; + break; + } + } + + if (samples == gl_solid_format) + comp = gl_tex_solid_format; + else if (samples == gl_alpha_format) + comp = gl_tex_alpha_format; + else { + VID.Printf(Defines.PRINT_ALL, "Unknown number of texture components " + samples + '\n'); + comp = samples; + } + + // simulates a goto + try { + if (scaled_width == width && scaled_height == height) { + if (!mipmap) { + if (qglColorTableEXT && gl_ext_palettedtexture.value != 0.0f && samples == gl_solid_format) { + uploaded_paletted = true; + GL_BuildPalettedTexture(paletted_texture, data, scaled_width, scaled_height); + gl.glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_COLOR_INDEX8_EXT, + scaled_width, + scaled_height, + 0, + GL_COLOR_INDEX, + GL_UNSIGNED_BYTE, + paletted_texture); + } + else { + tex.rewind(); tex.put(data); tex.rewind(); + gl.glTexImage2D( + GL_TEXTURE_2D, + 0, + comp, + scaled_width, + scaled_height, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + tex); + } + //goto done; + throw gotoDone; + } + //memcpy (scaled, data, width*height*4); were bytes + System.arraycopy(data, 0, scaled, 0, width * height); + } + else + GL_ResampleTexture(data, width, height, scaled, scaled_width, scaled_height); + + GL_LightScaleTexture(scaled, scaled_width, scaled_height, !mipmap); + + if (qglColorTableEXT && gl_ext_palettedtexture.value != 0.0f && (samples == gl_solid_format)) { + uploaded_paletted = true; + GL_BuildPalettedTexture(paletted_texture, scaled, scaled_width, scaled_height); + gl.glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_COLOR_INDEX8_EXT, + scaled_width, + scaled_height, + 0, + GL_COLOR_INDEX, + GL_UNSIGNED_BYTE, + paletted_texture); + } + else { + tex.rewind(); tex.put(scaled); tex.rewind(); + gl.glTexImage2D(GL_TEXTURE_2D, 0, comp, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, tex); + } + + if (mipmap) { + int miplevel; + miplevel = 0; + while (scaled_width > 1 || scaled_height > 1) { + GL_MipMap(scaled, scaled_width, scaled_height); + scaled_width >>= 1; + scaled_height >>= 1; + if (scaled_width < 1) + scaled_width = 1; + if (scaled_height < 1) + scaled_height = 1; + + miplevel++; + if (qglColorTableEXT && gl_ext_palettedtexture.value != 0.0f && samples == gl_solid_format) { + uploaded_paletted = true; + GL_BuildPalettedTexture(paletted_texture, scaled, scaled_width, scaled_height); + gl.glTexImage2D( + GL_TEXTURE_2D, + miplevel, + GL_COLOR_INDEX8_EXT, + scaled_width, + scaled_height, + 0, + GL_COLOR_INDEX, + GL_UNSIGNED_BYTE, + paletted_texture); + } + else { + tex.rewind(); tex.put(scaled); tex.rewind(); + gl.glTexImage2D( + GL_TEXTURE_2D, + miplevel, + comp, + scaled_width, + scaled_height, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + tex); + } + } + } + // label done: + } + catch (Throwable e) { + // replaces label done + } + + if (mipmap) { + gl.glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); + gl.glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + } + else { + gl.glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max); + gl.glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + } + + return (samples == gl_alpha_format); + } + + /* + =============== + GL_Upload8 + + Returns has_alpha + =============== + */ + + int[] trans = new int[512 * 256]; + + boolean GL_Upload8(byte[] data, int width, int height, boolean mipmap, boolean is_sky) { + + Arrays.fill(trans, 0); + + int s = width * height; + + if (s > trans.length) + Com.Error(Defines.ERR_DROP, "GL_Upload8: too large"); + + if (qglColorTableEXT && gl_ext_palettedtexture.value != 0.0f && is_sky) { + gl.glTexImage2D(GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, width, height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, ByteBuffer.wrap(data)); + + gl.glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max); + gl.glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + + // TODO check this + return false; + } + else { + int p; + int rgb; + for (int i = 0; i < s; i++) { + p = data[i] & 0xff; + trans[i] = d_8to24table[p]; + + if (p == 255) { // transparent, so scan around for another color + // to avoid alpha fringes + // FIXME: do a full flood fill so mips work... + if (i > width && (data[i - width] & 0xff) != 255) + p = data[i - width] & 0xff; + else if (i < s - width && (data[i + width] & 0xff) != 255) + p = data[i + width] & 0xff; + else if (i > 0 && (data[i - 1] & 0xff) != 255) + p = data[i - 1] & 0xff; + else if (i < s - 1 && (data[i + 1] & 0xff) != 255) + p = data[i + 1] & 0xff; + else + p = 0; + // copy rgb components + + // ((byte *)&trans[i])[0] = ((byte *)&d_8to24table[p])[0]; + // ((byte *)&trans[i])[1] = ((byte *)&d_8to24table[p])[1]; + // ((byte *)&trans[i])[2] = ((byte *)&d_8to24table[p])[2]; + + trans[i] = d_8to24table[p] & 0x00FFFFFF; // only rgb + } + } + + return GL_Upload32(trans, width, height, mipmap); + } + } + + /* + ================ + GL_LoadPic + + This is also used as an entry point for the generated r_notexture + ================ + */ + image_t GL_LoadPic(String name, byte[] pic, int width, int height, int type, int bits) { + image_t image; + int i; + + // find a free image_t + for (i = 0; i<numgltextures ; i++) + { + image = gltextures[i]; + if (image.texnum == 0) + break; + } + + if (i == numgltextures) + { + if (numgltextures == MAX_GLTEXTURES) + Com.Error (Defines.ERR_DROP, "MAX_GLTEXTURES"); + + numgltextures++; + } + image = gltextures[i]; + + if (name.length() > Defines.MAX_QPATH) + Com.Error(Defines.ERR_DROP, "Draw_LoadPic: \"" + name + "\" is too long"); + + image.name = name; + image.registration_sequence = registration_sequence; + + image.width = width; + image.height = height; + image.type = type; + + + if (type == it_skin && bits == 8) + R_FloodFillSkin(pic, width, height); + + // load little pics into the scrap + if (image.type == it_pic && bits == 8 && image.width < 64 && image.height < 64) { + pos_t pos = new pos_t(0, 0); + int j, k; + + int texnum = Scrap_AllocBlock(image.width, image.height, pos); + + if (texnum == -1) { + // replace goto nonscrap + + image.scrap = false; + + image.texnum = TEXNUM_IMAGES + image.getId(); // image pos in array + GL_Bind(image.texnum); + + if (bits == 8) { + image.has_alpha = + GL_Upload8(pic, width, height, (image.type != it_pic && image.type != it_sky), image.type == it_sky); + } + else { + int[] tmp = new int[pic.length / 4]; + + for (i = 0; i < tmp.length; i++) { + tmp[i] = ((pic[4 * i + 0] & 0xFF) << 0); // & 0x000000FF; + tmp[i] |= ((pic[4 * i + 1] & 0xFF) << 8); // & 0x0000FF00; + tmp[i] |= ((pic[4 * i + 2] & 0xFF) << 16); // & 0x00FF0000; + tmp[i] |= ((pic[4 * i + 3] & 0xFF) << 24); // & 0xFF000000; + } + + image.has_alpha = GL_Upload32(tmp, width, height, (image.type != it_pic && image.type != it_sky)); + } + + image.upload_width = upload_width; // after power of 2 and scales + image.upload_height = upload_height; + image.paletted = uploaded_paletted; + image.sl = 0; + image.sh = 1; + image.tl = 0; + image.th = 1; + + return image; + } + + scrap_dirty = true; + + // copy the texels into the scrap block + k = 0; + for (i = 0; i < image.height; i++) + for (j = 0; j < image.width; j++, k++) + scrap_texels[texnum][(pos.y + i) * BLOCK_WIDTH + pos.x + j] = pic[k]; + + image.texnum = TEXNUM_SCRAPS + texnum; + image.scrap = true; + image.has_alpha = true; + image.sl = (pos.x + 0.01f) / (float) BLOCK_WIDTH; + image.sh = (pos.x + image.width - 0.01f) / (float) BLOCK_WIDTH; + image.tl = (pos.y + 0.01f) / (float) BLOCK_WIDTH; + image.th = (pos.y + image.height - 0.01f) / (float) BLOCK_WIDTH; + + } + else { + // this was label nonscrap + + image.scrap = false; + + image.texnum = TEXNUM_IMAGES + image.getId(); //image pos in array + GL_Bind(image.texnum); + + if (bits == 8) { + image.has_alpha = GL_Upload8(pic, width, height, (image.type != it_pic && image.type != it_sky), image.type == it_sky); + } + else { + int[] tmp = new int[pic.length / 4]; + + for (i = 0; i < tmp.length; i++) { + tmp[i] = ((pic[4 * i + 0] & 0xFF) << 0); // & 0x000000FF; + tmp[i] |= ((pic[4 * i + 1] & 0xFF) << 8); // & 0x0000FF00; + tmp[i] |= ((pic[4 * i + 2] & 0xFF) << 16); // & 0x00FF0000; + tmp[i] |= ((pic[4 * i + 3] & 0xFF) << 24); // & 0xFF000000; + } + + image.has_alpha = GL_Upload32(tmp, width, height, (image.type != it_pic && image.type != it_sky)); + } + image.upload_width = upload_width; // after power of 2 and scales + image.upload_height = upload_height; + image.paletted = uploaded_paletted; + image.sl = 0; + image.sh = 1; + image.tl = 0; + image.th = 1; + } + return image; + } + + /* + ================ + GL_LoadWal + ================ + */ + image_t GL_LoadWal(String name) { + + image_t image = null; + + byte[] raw = FS.LoadFile(name); + if (raw == null) { + VID.Printf(Defines.PRINT_ALL, "GL_FindImage: can't load " + name + '\n'); + return r_notexture; + } + + qfiles.miptex_t mt = new qfiles.miptex_t(raw); + + byte[] pix = new byte[mt.width * mt.height]; + System.arraycopy(raw, mt.offsets[0], pix, 0, pix.length); + + image = GL_LoadPic(name, pix, mt.width, mt.height, it_wall, 8); + + return image; + } + + Map imageCache = new HashMap(MAX_GLTEXTURES); + + /* + =============== + GL_FindImage + + Finds or loads the given image + =============== + */ + image_t GL_FindImage(String name, int type) { + + if (name == null || name.length() < 5) + return null; + + // look for it + image_t image = (image_t) imageCache.get(name); + if (image != null) { + image.registration_sequence = registration_sequence; + return image; + } + + // + // load the pic from disk + // + image = null; + byte[] pic = null; + Dimension dim = new Dimension(); + + if (name.endsWith(".pcx")) { + + pic = LoadPCX(name, null, dim); + if (pic == null) + return null; + image = GL_LoadPic(name, pic, dim.width, dim.height, type, 8); + + } + else if (name.endsWith(".wal")) { + + image = GL_LoadWal(name); + + } + else if (name.endsWith(".tga")) { + + pic = LoadTGA(name, dim); + + if (pic == null) + return null; + + image = GL_LoadPic(name, pic, dim.width, dim.height, type, 32); + + } + + imageCache.put(image.name, image); + return image; + } + + /* + =============== + R_RegisterSkin + =============== + */ + public image_t R_RegisterSkin(String name) { + return GL_FindImage(name, it_skin); + } + + IntBuffer texnumBuffer=Lib.newIntBuffer(1); + + /* + ================ + GL_FreeUnusedImages + + Any image that was not touched on this registration sequence + will be freed. + ================ + */ + void GL_FreeUnusedImages() { + + // never free r_notexture or particle texture + r_notexture.registration_sequence = registration_sequence; + r_particletexture.registration_sequence = registration_sequence; + + image_t image = null; + + for (int i = 0; i < numgltextures; i++) { + image = gltextures[i]; + // used this sequence + if (image.registration_sequence == registration_sequence) + continue; + // free image_t slot + if (image.registration_sequence == 0) + continue; + // don't free pics + if (image.type == it_pic) + continue; + + // free it + texnumBuffer.clear(); + texnumBuffer.put(0,image.texnum); + gl.glDeleteTextures(texnumBuffer); + + imageCache.remove(image.name); + image.clear(); + } + } + + /* + =============== + Draw_GetPalette + =============== + */ + protected void Draw_GetPalette() { + int r, g, b; + byte[][] palette = new byte[1][]; //new byte[768]; + + // get the palette + + LoadPCX("pics/colormap.pcx", palette, null); + + if (palette[0] == null || palette[0].length != 768) + Com.Error(Defines.ERR_FATAL, "Couldn't load pics/colormap.pcx"); + + byte[] pal = palette[0]; + + int j = 0; + for (int i = 0; i < 256; i++) { + r = pal[j++] & 0xFF; + g = pal[j++] & 0xFF; + b = pal[j++] & 0xFF; + + d_8to24table[i] = (255 << 24) | (b << 16) | (g << 8) | (r << 0); + } + + d_8to24table[255] &= 0x00FFFFFF; // 255 is transparent + + particle_t.setColorPalette(d_8to24table); + } + + /* + =============== + GL_InitImages + =============== + */ + void GL_InitImages() { + int i, j; + float g = vid_gamma.value; + + registration_sequence = 1; + + // init intensity conversions + intensity = Cvar.Get("intensity", "2", 0); + + if (intensity.value <= 1) + Cvar.Set("intensity", "1"); + + gl_state.inverse_intensity = 1 / intensity.value; + + Draw_GetPalette(); + + if (qglColorTableEXT) { + gl_state.d_16to8table = FS.LoadFile("pics/16to8.dat"); + if (gl_state.d_16to8table == null) + Com.Error(Defines.ERR_FATAL, "Couldn't load pics/16to8.pcx"); + } + + if ((gl_config.renderer & (GL_RENDERER_VOODOO | GL_RENDERER_VOODOO2)) != 0) { + g = 1.0F; + } + + for (i = 0; i < 256; i++) { + + if (g == 1.0f) { + gammatable[i] = (byte) i; + } + else { + + int inf = (int) (255.0f * Math.pow((i + 0.5) / 255.5, g) + 0.5); + if (inf < 0) + inf = 0; + if (inf > 255) + inf = 255; + gammatable[i] = (byte) inf; + } + } + + for (i = 0; i < 256; i++) { + j = (int) (i * intensity.value); + if (j > 255) + j = 255; + intensitytable[i] = (byte) j; + } + } + + /* + =============== + GL_ShutdownImages + =============== + */ + void GL_ShutdownImages() { + image_t image; + + for (int i=0; i < numgltextures ; i++) + { + image = gltextures[i]; + + if (image.registration_sequence == 0) + continue; // free image_t slot + + // free it + texnumBuffer.clear(); + texnumBuffer.put(0,image.texnum); + gl.glDeleteTextures(texnumBuffer); + + imageCache.remove(image.name); + image.clear(); + } + } + +} diff --git a/src/jake2/render/fast/Light.java b/src/jake2/render/fast/Light.java new file mode 100644 index 0000000..c0d0a32 --- /dev/null +++ b/src/jake2/render/fast/Light.java @@ -0,0 +1,752 @@ +/* + * Light.java + * Copyright (C) 2003 + * + * $Id: Light.java,v 1.2 2006-11-21 00:50:46 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 jake2.render.fast; + +import jake2.Defines; +import jake2.Globals; +import jake2.client.dlight_t; +import jake2.game.cplane_t; +import jake2.qcommon.Com; +import jake2.render.*; +import jake2.util.Math3D; +import jake2.util.Vec3Cache; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.util.Arrays; + +/** + * 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); + + gl.glBegin (GL_TRIANGLE_FAN); + gl.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; + + gl.glVertex3f(v[0], v[1], v[2]); + gl.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); + gl.glVertex3f(v[0], v[1], v[2]); + } + gl.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 + gl.glDepthMask(false); + gl.glDisable(GL_TEXTURE_2D); + gl.glShadeModel (GL_SMOOTH); + gl.glEnable (GL_BLEND); + gl.glBlendFunc (GL_ONE, GL_ONE); + + for (int i=0 ; i<r_newrefdef.num_dlights ; i++) + { + R_RenderDlight(r_newrefdef.dlights[i]); + } + + gl.glColor3f (1,1,1); + gl.glDisable(GL_BLEND); + gl.glEnable(GL_TEXTURE_2D); + gl.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + gl.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 tex; + 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 + + tex = surf.texinfo; + + s = (int)(Math3D.DotProduct (mid, tex.vecs[0]) + tex.vecs[0][3]); + t = (int)(Math3D.DotProduct (mid, tex.vecs[1]) + tex.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 tex = 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, tex.vecs[0]) + tex.vecs[0][3] - surf.texturemins[0]; + local1 = Math3D.DotProduct (impact, tex.vecs[1]) + tex.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) { + 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) { + 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); + } + } + } + } + +}
\ No newline at end of file diff --git a/src/jake2/render/fast/Main.java b/src/jake2/render/fast/Main.java new file mode 100644 index 0000000..9a4cacf --- /dev/null +++ b/src/jake2/render/fast/Main.java @@ -0,0 +1,1526 @@ +/* + * Main.java + * Copyright (C) 2003 + * + * $Id: Main.java,v 1.2 2006-11-21 00:50:46 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 jake2.render.fast; + +import jake2.Defines; +import jake2.Globals; +import jake2.client.*; +import jake2.game.*; +import jake2.qcommon.*; +import jake2.render.*; +import jake2.util.*; + +import java.awt.Dimension; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; + +/** + * Main + * + * @author cwei + */ +public abstract class Main extends Base { + + public static int[] d_8to24table = new int[256]; + + int c_visible_lightmaps; + int c_visible_textures; + + int registration_sequence; + + // this a hack for function pointer test + // default disabled + boolean qglColorTableEXT = false; + boolean qglActiveTextureARB = false; + boolean qglPointParameterfEXT = false; + boolean qglLockArraysEXT = false; + boolean qwglSwapIntervalEXT = false; + + // ================= + // abstract methods + // ================= + protected abstract void Draw_GetPalette(); + + abstract void GL_ImageList_f(); + abstract void GL_ScreenShot_f(); + abstract void GL_SetTexturePalette(int[] palette); + abstract void GL_Strings_f(); + + abstract void Mod_Modellist_f(); + abstract mleaf_t Mod_PointInLeaf(float[] point, model_t model); + + abstract void GL_SetDefaultState(); + + abstract void GL_InitImages(); + abstract void Mod_Init(); // Model.java + abstract void R_InitParticleTexture(); // MIsc.java + abstract void R_DrawAliasModel(entity_t e); // Mesh.java + abstract void R_DrawBrushModel(entity_t e); // Surf.java + abstract void Draw_InitLocal(); + abstract void R_LightPoint(float[] p, float[] color); + abstract void R_PushDlights(); + abstract void R_MarkLeaves(); + abstract void R_DrawWorld(); + abstract void R_RenderDlights(); + abstract void R_DrawAlphaSurfaces(); + + abstract void Mod_FreeAll(); + + abstract void GL_ShutdownImages(); + abstract void GL_Bind(int texnum); + abstract void GL_TexEnv(int mode); + abstract void GL_TextureMode(String string); + abstract void GL_TextureAlphaMode(String string); + abstract void GL_TextureSolidMode(String string); + abstract void GL_UpdateSwapInterval(); + + /* + ==================================================================== + + from gl_rmain.c + + ==================================================================== + */ + + int TEXTURE0 = GL_TEXTURE0; + int TEXTURE1 = GL_TEXTURE1; + + model_t r_worldmodel; + + float gldepthmin, gldepthmax; + + glconfig_t gl_config = new glconfig_t(); + glstate_t gl_state = new glstate_t(); + + image_t r_notexture; // use for bad textures + image_t r_particletexture; // little dot for particles + + entity_t currententity; + model_t currentmodel; + + cplane_t frustum[] = { new cplane_t(), new cplane_t(), new cplane_t(), new cplane_t()}; + + int r_visframecount; // bumped when going to a new PVS + int r_framecount; // used for dlight push checking + + int c_brush_polys, c_alias_polys; + + float v_blend[] = { 0, 0, 0, 0 }; // final blending color + + // + // view origin + // + float[] vup = { 0, 0, 0 }; + float[] vpn = { 0, 0, 0 }; + float[] vright = { 0, 0, 0 }; + float[] r_origin = { 0, 0, 0 }; + + //float r_world_matrix[] = new float[16]; + FloatBuffer r_world_matrix = Lib.newFloatBuffer(16); + + float r_base_world_matrix[] = new float[16]; + + // + // screen size info + // + refdef_t r_newrefdef = new refdef_t(); + + int r_viewcluster, r_viewcluster2, r_oldviewcluster, r_oldviewcluster2; + + cvar_t r_norefresh; + cvar_t r_drawentities; + cvar_t r_drawworld; + cvar_t r_speeds; + cvar_t r_fullbright; + cvar_t r_novis; + cvar_t r_nocull; + cvar_t r_lerpmodels; + cvar_t r_lefthand; + + cvar_t r_lightlevel; + // FIXME: This is a HACK to get the client's light level + + cvar_t gl_nosubimage; + cvar_t gl_allow_software; + + cvar_t gl_vertex_arrays; + + cvar_t gl_particle_min_size; + cvar_t gl_particle_max_size; + cvar_t gl_particle_size; + cvar_t gl_particle_att_a; + cvar_t gl_particle_att_b; + cvar_t gl_particle_att_c; + + cvar_t gl_ext_swapinterval; + cvar_t gl_ext_palettedtexture; + cvar_t gl_ext_multitexture; + cvar_t gl_ext_pointparameters; + cvar_t gl_ext_compiled_vertex_array; + + cvar_t gl_log; + cvar_t gl_bitdepth; + cvar_t gl_drawbuffer; + cvar_t gl_driver; + cvar_t gl_lightmap; + cvar_t gl_shadows; + cvar_t gl_mode; + cvar_t gl_dynamic; + cvar_t gl_monolightmap; + cvar_t gl_modulate; + cvar_t gl_nobind; + cvar_t gl_round_down; + cvar_t gl_picmip; + cvar_t gl_skymip; + cvar_t gl_showtris; + cvar_t gl_ztrick; + cvar_t gl_finish; + cvar_t gl_clear; + cvar_t gl_cull; + cvar_t gl_polyblend; + cvar_t gl_flashblend; + cvar_t gl_playermip; + cvar_t gl_saturatelighting; + cvar_t gl_swapinterval; + cvar_t gl_texturemode; + cvar_t gl_texturealphamode; + cvar_t gl_texturesolidmode; + cvar_t gl_lockpvs; + + cvar_t gl_3dlabs_broken; + + cvar_t vid_gamma; + cvar_t vid_ref; + + // ============================================================================ + // to port from gl_rmain.c, ... + // ============================================================================ + + /** + * R_CullBox + * Returns true if the box is completely outside the frustum + */ + final boolean R_CullBox(float[] mins, float[] maxs) { + assert(mins.length == 3 && maxs.length == 3) : "vec3_t bug"; + + if (r_nocull.value != 0) + return false; + + for (int i = 0; i < 4; i++) { + if (Math3D.BoxOnPlaneSide(mins, maxs, frustum[i]) == 2) + return true; + } + return false; + } + + /** + * R_RotateForEntity + */ + final void R_RotateForEntity(entity_t e) { + gl.glTranslatef(e.origin[0], e.origin[1], e.origin[2]); + + gl.glRotatef(e.angles[1], 0, 0, 1); + gl.glRotatef(-e.angles[0], 0, 1, 0); + gl.glRotatef(-e.angles[2], 1, 0, 0); + } + + /* + ============================================================= + + SPRITE MODELS + + ============================================================= + */ + + // stack variable + private final float[] point = { 0, 0, 0 }; + /** + * R_DrawSpriteModel + */ + void R_DrawSpriteModel(entity_t e) { + float alpha = 1.0F; + + qfiles.dsprframe_t frame; + qfiles.dsprite_t psprite; + + // don't even bother culling, because it's just a single + // polygon without a surface cache + + psprite = (qfiles.dsprite_t) currentmodel.extradata; + + e.frame %= psprite.numframes; + + frame = psprite.frames[e.frame]; + + if ((e.flags & Defines.RF_TRANSLUCENT) != 0) + alpha = e.alpha; + + if (alpha != 1.0F) + gl.glEnable(GL_BLEND); + + gl.glColor4f(1, 1, 1, alpha); + + GL_Bind(currentmodel.skins[e.frame].texnum); + + GL_TexEnv(GL_MODULATE); + + if (alpha == 1.0) + gl.glEnable(GL_ALPHA_TEST); + else + gl.glDisable(GL_ALPHA_TEST); + + gl.glBegin(GL_QUADS); + + gl.glTexCoord2f(0, 1); + Math3D.VectorMA(e.origin, -frame.origin_y, vup, point); + Math3D.VectorMA(point, -frame.origin_x, vright, point); + gl.glVertex3f(point[0], point[1], point[2]); + + gl.glTexCoord2f(0, 0); + Math3D.VectorMA(e.origin, frame.height - frame.origin_y, vup, point); + Math3D.VectorMA(point, -frame.origin_x, vright, point); + gl.glVertex3f(point[0], point[1], point[2]); + + gl.glTexCoord2f(1, 0); + Math3D.VectorMA(e.origin, frame.height - frame.origin_y, vup, point); + Math3D.VectorMA(point, frame.width - frame.origin_x, vright, point); + gl.glVertex3f(point[0], point[1], point[2]); + + gl.glTexCoord2f(1, 1); + Math3D.VectorMA(e.origin, -frame.origin_y, vup, point); + Math3D.VectorMA(point, frame.width - frame.origin_x, vright, point); + gl.glVertex3f(point[0], point[1], point[2]); + + gl.glEnd(); + + gl.glDisable(GL_ALPHA_TEST); + GL_TexEnv(GL_REPLACE); + + if (alpha != 1.0F) + gl.glDisable(GL_BLEND); + + gl.glColor4f(1, 1, 1, 1); + } + + // ================================================================================== + + // stack variable + private final float[] shadelight = { 0, 0, 0 }; + /** + * R_DrawNullModel + */ + void R_DrawNullModel() { + if ((currententity.flags & Defines.RF_FULLBRIGHT) != 0) { + // cwei wollte blau: shadelight[0] = shadelight[1] = shadelight[2] = 1.0F; + shadelight[0] = shadelight[1] = shadelight[2] = 0.0F; + shadelight[2] = 0.8F; + } + else { + R_LightPoint(currententity.origin, shadelight); + } + + gl.glPushMatrix(); + R_RotateForEntity(currententity); + + gl.glDisable(GL_TEXTURE_2D); + gl.glColor3f(shadelight[0], shadelight[1], shadelight[2]); + + // this replaces the TRIANGLE_FAN + //glut.glutWireCube(gl, 20); + + gl.glBegin(GL_TRIANGLE_FAN); + gl.glVertex3f(0, 0, -16); + int i; + for (i=0 ; i<=4 ; i++) { + gl.glVertex3f((float)(16.0f * Math.cos(i * Math.PI / 2)), (float)(16.0f * Math.sin(i * Math.PI / 2)), 0.0f); + } + gl.glEnd(); + + gl.glBegin(GL_TRIANGLE_FAN); + gl.glVertex3f (0, 0, 16); + for (i=4 ; i>=0 ; i--) { + gl.glVertex3f((float)(16.0f * Math.cos(i * Math.PI / 2)), (float)(16.0f * Math.sin(i * Math.PI / 2)), 0.0f); + } + gl.glEnd(); + + + gl.glColor3f(1, 1, 1); + gl.glPopMatrix(); + gl.glEnable(GL_TEXTURE_2D); + } + + /** + * R_DrawEntitiesOnList + */ + void R_DrawEntitiesOnList() { + if (r_drawentities.value == 0.0f) + return; + + // draw non-transparent first + int i; + for (i = 0; i < r_newrefdef.num_entities; i++) { + currententity = r_newrefdef.entities[i]; + if ((currententity.flags & Defines.RF_TRANSLUCENT) != 0) + continue; // solid + + if ((currententity.flags & Defines.RF_BEAM) != 0) { + R_DrawBeam(currententity); + } + else { + currentmodel = currententity.model; + if (currentmodel == null) { + R_DrawNullModel(); + continue; + } + switch (currentmodel.type) { + case mod_alias : + R_DrawAliasModel(currententity); + break; + case mod_brush : + R_DrawBrushModel(currententity); + break; + case mod_sprite : + R_DrawSpriteModel(currententity); + break; + default : + Com.Error(Defines.ERR_DROP, "Bad modeltype"); + break; + } + } + } + // draw transparent entities + // we could sort these if it ever becomes a problem... + gl.glDepthMask(false); // no z writes + for (i = 0; i < r_newrefdef.num_entities; i++) { + currententity = r_newrefdef.entities[i]; + if ((currententity.flags & Defines.RF_TRANSLUCENT) == 0) + continue; // solid + + if ((currententity.flags & Defines.RF_BEAM) != 0) { + R_DrawBeam(currententity); + } + else { + currentmodel = currententity.model; + + if (currentmodel == null) { + R_DrawNullModel(); + continue; + } + switch (currentmodel.type) { + case mod_alias : + R_DrawAliasModel(currententity); + break; + case mod_brush : + R_DrawBrushModel(currententity); + break; + case mod_sprite : + R_DrawSpriteModel(currententity); + break; + default : + Com.Error(Defines.ERR_DROP, "Bad modeltype"); + break; + } + } + } + gl.glDepthMask(true); // back to writing + } + + // stack variable + private final float[] up = { 0, 0, 0 }; + private final float[] right = { 0, 0, 0 }; + /** + * GL_DrawParticles + */ + void GL_DrawParticles(int num_particles) { + float origin_x, origin_y, origin_z; + + Math3D.VectorScale(vup, 1.5f, up); + Math3D.VectorScale(vright, 1.5f, right); + + GL_Bind(r_particletexture.texnum); + gl.glDepthMask(false); // no z buffering + gl.glEnable(GL_BLEND); + GL_TexEnv(GL_MODULATE); + + gl.glBegin(GL_TRIANGLES); + + FloatBuffer sourceVertices = particle_t.vertexArray; + IntBuffer sourceColors = particle_t.colorArray; + float scale; + int color; + for (int j = 0, i = 0; i < num_particles; i++) { + origin_x = sourceVertices.get(j++); + origin_y = sourceVertices.get(j++); + origin_z = sourceVertices.get(j++); + + // hack a scale up to keep particles from disapearing + scale = + (origin_x - r_origin[0]) * vpn[0] + + (origin_y - r_origin[1]) * vpn[1] + + (origin_z - r_origin[2]) * vpn[2]; + + scale = (scale < 20) ? 1 : 1 + scale * 0.004f; + + color = sourceColors.get(i); + + gl.glColor4ub( + (byte)((color) & 0xFF), + (byte)((color >> 8) & 0xFF), + (byte)((color >> 16) & 0xFF), + (byte)((color >>> 24)) + ); + // first vertex + gl.glTexCoord2f(0.0625f, 0.0625f); + gl.glVertex3f(origin_x, origin_y, origin_z); + // second vertex + gl.glTexCoord2f(1.0625f, 0.0625f); + gl.glVertex3f(origin_x + up[0] * scale, origin_y + up[1] * scale, origin_z + up[2] * scale); + // third vertex + gl.glTexCoord2f(0.0625f, 1.0625f); + gl.glVertex3f(origin_x + right[0] * scale, origin_y + right[1] * scale, origin_z + right[2] * scale); + } + gl.glEnd(); + + gl.glDisable(GL_BLEND); + gl.glColor4f(1, 1, 1, 1); + gl.glDepthMask(true); // back to normal Z buffering + GL_TexEnv(GL_REPLACE); + } + + /** + * R_DrawParticles + */ + void R_DrawParticles() { + + if (gl_ext_pointparameters.value != 0.0f && qglPointParameterfEXT) { + + //gl.gl.glEnableClientState(GL_VERTEX_ARRAY); + gl.glVertexPointer(3, 0, particle_t.vertexArray); + gl.glEnableClientState(GL_COLOR_ARRAY); + gl.glColorPointer(4, true, 0, particle_t.getColorAsByteBuffer()); + + gl.glDepthMask(false); + gl.glEnable(GL_BLEND); + gl.glDisable(GL_TEXTURE_2D); + gl.glPointSize(gl_particle_size.value); + + gl.glDrawArrays(GL_POINTS, 0, r_newrefdef.num_particles); + + gl.glDisableClientState(GL_COLOR_ARRAY); + //gl.gl.glDisableClientState(GL_VERTEX_ARRAY); + + gl.glDisable(GL_BLEND); + gl.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); + gl.glDepthMask(true); + gl.glEnable(GL_TEXTURE_2D); + + } + else { + GL_DrawParticles(r_newrefdef.num_particles); + } + } + + /** + * R_PolyBlend + */ + void R_PolyBlend() { + if (gl_polyblend.value == 0.0f) + return; + + if (v_blend[3] == 0.0f) + return; + + gl.glDisable(GL_ALPHA_TEST); + gl.glEnable(GL_BLEND); + gl.glDisable(GL_DEPTH_TEST); + gl.glDisable(GL_TEXTURE_2D); + + gl.glLoadIdentity(); + + // FIXME: get rid of these + gl.glRotatef(-90, 1, 0, 0); // put Z going up + gl.glRotatef(90, 0, 0, 1); // put Z going up + + gl.glColor4f(v_blend[0], v_blend[1], v_blend[2], v_blend[3]); + + gl.glBegin(GL_QUADS); + + gl.glVertex3f(10, 100, 100); + gl.glVertex3f(10, -100, 100); + gl.glVertex3f(10, -100, -100); + gl.glVertex3f(10, 100, -100); + gl.glEnd(); + + gl.glDisable(GL_BLEND); + gl.glEnable(GL_TEXTURE_2D); + gl.glEnable(GL_ALPHA_TEST); + + gl.glColor4f(1, 1, 1, 1); + } + + // ======================================================================= + + /** + * SignbitsForPlane + */ + int SignbitsForPlane(cplane_t out) { + // for fast box on planeside test + int bits = 0; + for (int j = 0; j < 3; j++) { + if (out.normal[j] < 0) + bits |= (1 << j); + } + return bits; + } + + /** + * R_SetFrustum + */ + void R_SetFrustum() { + // rotate VPN right by FOV_X/2 degrees + Math3D.RotatePointAroundVector(frustum[0].normal, vup, vpn, - (90f - r_newrefdef.fov_x / 2f)); + // rotate VPN left by FOV_X/2 degrees + Math3D.RotatePointAroundVector(frustum[1].normal, vup, vpn, 90f - r_newrefdef.fov_x / 2f); + // rotate VPN up by FOV_X/2 degrees + Math3D.RotatePointAroundVector(frustum[2].normal, vright, vpn, 90f - r_newrefdef.fov_y / 2f); + // rotate VPN down by FOV_X/2 degrees + Math3D.RotatePointAroundVector(frustum[3].normal, vright, vpn, - (90f - r_newrefdef.fov_y / 2f)); + + for (int i = 0; i < 4; i++) { + frustum[i].type = Defines.PLANE_ANYZ; + frustum[i].dist = Math3D.DotProduct(r_origin, frustum[i].normal); + frustum[i].signbits = (byte) SignbitsForPlane(frustum[i]); + } + } + + // ======================================================================= + + // stack variable + private final float[] temp = {0, 0, 0}; + /** + * R_SetupFrame + */ + void R_SetupFrame() { + r_framecount++; + + // build the transformation matrix for the given view angles + Math3D.VectorCopy(r_newrefdef.vieworg, r_origin); + + Math3D.AngleVectors(r_newrefdef.viewangles, vpn, vright, vup); + + // current viewcluster + mleaf_t leaf; + if ((r_newrefdef.rdflags & Defines.RDF_NOWORLDMODEL) == 0) { + r_oldviewcluster = r_viewcluster; + r_oldviewcluster2 = r_viewcluster2; + leaf = Mod_PointInLeaf(r_origin, r_worldmodel); + r_viewcluster = r_viewcluster2 = leaf.cluster; + + // check above and below so crossing solid water doesn't draw wrong + if (leaf.contents == 0) { // look down a bit + Math3D.VectorCopy(r_origin, temp); + temp[2] -= 16; + leaf = Mod_PointInLeaf(temp, r_worldmodel); + if ((leaf.contents & Defines.CONTENTS_SOLID) == 0 && (leaf.cluster != r_viewcluster2)) + r_viewcluster2 = leaf.cluster; + } + else { // look up a bit + Math3D.VectorCopy(r_origin, temp); + temp[2] += 16; + leaf = Mod_PointInLeaf(temp, r_worldmodel); + if ((leaf.contents & Defines.CONTENTS_SOLID) == 0 && (leaf.cluster != r_viewcluster2)) + r_viewcluster2 = leaf.cluster; + } + } + + for (int i = 0; i < 4; i++) + v_blend[i] = r_newrefdef.blend[i]; + + c_brush_polys = 0; + c_alias_polys = 0; + + // clear out the portion of the screen that the NOWORLDMODEL defines + if ((r_newrefdef.rdflags & Defines.RDF_NOWORLDMODEL) != 0) { + gl.glEnable(GL_SCISSOR_TEST); + gl.glClearColor(0.3f, 0.3f, 0.3f, 1.0f); + gl.glScissor( + r_newrefdef.x, + vid.height - r_newrefdef.height - r_newrefdef.y, + r_newrefdef.width, + r_newrefdef.height); + gl.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + gl.glClearColor(1.0f, 0.0f, 0.5f, 0.5f); + gl.glDisable(GL_SCISSOR_TEST); + } + } + + /** + * MYgluPerspective + * + * @param fovy + * @param aspect + * @param zNear + * @param zFar + */ + void MYgluPerspective(double fovy, double aspect, double zNear, double zFar) { + double ymax = zNear * Math.tan(fovy * Math.PI / 360.0); + double ymin = -ymax; + + double xmin = ymin * aspect; + double xmax = ymax * aspect; + + xmin += - (2 * gl_state.camera_separation) / zNear; + xmax += - (2 * gl_state.camera_separation) / zNear; + + gl.glFrustum(xmin, xmax, ymin, ymax, zNear, zFar); + } + + /** + * R_SetupGL + */ + void R_SetupGL() { + + // + // set up viewport + // + //int x = (int) Math.floor(r_newrefdef.x * vid.width / vid.width); + int x = r_newrefdef.x; + //int x2 = (int) Math.ceil((r_newrefdef.x + r_newrefdef.width) * vid.width / vid.width); + int x2 = r_newrefdef.x + r_newrefdef.width; + //int y = (int) Math.floor(vid.height - r_newrefdef.y * vid.height / vid.height); + int y = vid.height - r_newrefdef.y; + //int y2 = (int) Math.ceil(vid.height - (r_newrefdef.y + r_newrefdef.height) * vid.height / vid.height); + int y2 = vid.height - (r_newrefdef.y + r_newrefdef.height); + + int w = x2 - x; + int h = y - y2; + + gl.glViewport(x, y2, w, h); + + // + // set up projection matrix + // + float screenaspect = (float) r_newrefdef.width / r_newrefdef.height; + gl.glMatrixMode(GL_PROJECTION); + gl.glLoadIdentity(); + MYgluPerspective(r_newrefdef.fov_y, screenaspect, 4, 4096); + + gl.glCullFace(GL_FRONT); + + gl.glMatrixMode(GL_MODELVIEW); + gl.glLoadIdentity(); + + gl.glRotatef(-90, 1, 0, 0); // put Z going up + gl.glRotatef(90, 0, 0, 1); // put Z going up + gl.glRotatef(-r_newrefdef.viewangles[2], 1, 0, 0); + gl.glRotatef(-r_newrefdef.viewangles[0], 0, 1, 0); + gl.glRotatef(-r_newrefdef.viewangles[1], 0, 0, 1); + gl.glTranslatef(-r_newrefdef.vieworg[0], -r_newrefdef.vieworg[1], -r_newrefdef.vieworg[2]); + + gl.glGetFloat(GL_MODELVIEW_MATRIX, r_world_matrix); + r_world_matrix.clear(); + + // + // set drawing parms + // + if (gl_cull.value != 0.0f) + gl.glEnable(GL_CULL_FACE); + else + gl.glDisable(GL_CULL_FACE); + + gl.glDisable(GL_BLEND); + gl.glDisable(GL_ALPHA_TEST); + gl.glEnable(GL_DEPTH_TEST); + } + + int trickframe = 0; + + /** + * R_Clear + */ + void R_Clear() { + if (gl_ztrick.value != 0.0f) { + + if (gl_clear.value != 0.0f) { + gl.glClear(GL_COLOR_BUFFER_BIT); + } + + trickframe++; + if ((trickframe & 1) != 0) { + gldepthmin = 0; + gldepthmax = 0.49999f; + gl.glDepthFunc(GL_LEQUAL); + } + else { + gldepthmin = 1; + gldepthmax = 0.5f; + gl.glDepthFunc(GL_GEQUAL); + } + } + else { + if (gl_clear.value != 0.0f) + gl.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + else + gl.glClear(GL_DEPTH_BUFFER_BIT); + + gldepthmin = 0; + gldepthmax = 1; + gl.glDepthFunc(GL_LEQUAL); + } + gl.glDepthRange(gldepthmin, gldepthmax); + } + + /** + * R_Flash + */ + void R_Flash() { + R_PolyBlend(); + } + + /** + * R_RenderView + * r_newrefdef must be set before the first call + */ + void R_RenderView(refdef_t fd) { + + if (r_norefresh.value != 0.0f) + return; + + r_newrefdef = fd; + + // included by cwei + if (r_newrefdef == null) { + Com.Error(Defines.ERR_DROP, "R_RenderView: refdef_t fd is null"); + } + + if (r_worldmodel == null && (r_newrefdef.rdflags & Defines.RDF_NOWORLDMODEL) == 0) + Com.Error(Defines.ERR_DROP, "R_RenderView: NULL worldmodel"); + + if (r_speeds.value != 0.0f) { + c_brush_polys = 0; + c_alias_polys = 0; + } + + R_PushDlights(); + + if (gl_finish.value != 0.0f) + gl.glFinish(); + + R_SetupFrame(); + + R_SetFrustum(); + + R_SetupGL(); + + R_MarkLeaves(); // done here so we know if we're in water + + R_DrawWorld(); + + R_DrawEntitiesOnList(); + + R_RenderDlights(); + + R_DrawParticles(); + + R_DrawAlphaSurfaces(); + + R_Flash(); + + if (r_speeds.value != 0.0f) { + VID.Printf( + Defines.PRINT_ALL, + "%4i wpoly %4i epoly %i tex %i lmaps\n", + new Vargs(4).add(c_brush_polys).add(c_alias_polys).add(c_visible_textures).add(c_visible_lightmaps)); + } + } + + /** + * R_SetGL2D + */ + void R_SetGL2D() { + // set 2D virtual screen size + gl.glViewport(0, 0, vid.width, vid.height); + gl.glMatrixMode(GL_PROJECTION); + gl.glLoadIdentity(); + gl.glOrtho(0, vid.width, vid.height, 0, -99999, 99999); + gl.glMatrixMode(GL_MODELVIEW); + gl.glLoadIdentity(); + gl.glDisable(GL_DEPTH_TEST); + gl.glDisable(GL_CULL_FACE); + gl.glDisable(GL_BLEND); + gl.glEnable(GL_ALPHA_TEST); + gl.glColor4f(1, 1, 1, 1); + } + + // stack variable + private final float[] light = { 0, 0, 0 }; + /** + * R_SetLightLevel + */ + void R_SetLightLevel() { + if ((r_newrefdef.rdflags & Defines.RDF_NOWORLDMODEL) != 0) + return; + + // save off light value for server to look at (BIG HACK!) + + R_LightPoint(r_newrefdef.vieworg, light); + + // pick the greatest component, which should be the same + // as the mono value returned by software + if (light[0] > light[1]) { + if (light[0] > light[2]) + r_lightlevel.value = 150 * light[0]; + else + r_lightlevel.value = 150 * light[2]; + } + else { + if (light[1] > light[2]) + r_lightlevel.value = 150 * light[1]; + else + r_lightlevel.value = 150 * light[2]; + } + } + + /** + * R_RenderFrame + */ + public void R_RenderFrame(refdef_t fd) { + R_RenderView(fd); + R_SetLightLevel(); + R_SetGL2D(); + } + + /** + * R_Register + */ + protected void R_Register() { + r_lefthand = Cvar.Get("hand", "0", Defines.CVAR_USERINFO | Defines.CVAR_ARCHIVE); + r_norefresh = Cvar.Get("r_norefresh", "0", 0); + r_fullbright = Cvar.Get("r_fullbright", "0", 0); + r_drawentities = Cvar.Get("r_drawentities", "1", 0); + r_drawworld = Cvar.Get("r_drawworld", "1", 0); + r_novis = Cvar.Get("r_novis", "0", 0); + r_nocull = Cvar.Get("r_nocull", "0", 0); + r_lerpmodels = Cvar.Get("r_lerpmodels", "1", 0); + r_speeds = Cvar.Get("r_speeds", "0", 0); + + r_lightlevel = Cvar.Get("r_lightlevel", "1", 0); + + gl_nosubimage = Cvar.Get("gl_nosubimage", "0", 0); + gl_allow_software = Cvar.Get("gl_allow_software", "0", 0); + + gl_particle_min_size = Cvar.Get("gl_particle_min_size", "2", Defines.CVAR_ARCHIVE); + gl_particle_max_size = Cvar.Get("gl_particle_max_size", "40", Defines.CVAR_ARCHIVE); + gl_particle_size = Cvar.Get("gl_particle_size", "40", Defines.CVAR_ARCHIVE); + gl_particle_att_a = Cvar.Get("gl_particle_att_a", "0.01", Defines.CVAR_ARCHIVE); + gl_particle_att_b = Cvar.Get("gl_particle_att_b", "0.0", Defines.CVAR_ARCHIVE); + gl_particle_att_c = Cvar.Get("gl_particle_att_c", "0.01", Defines.CVAR_ARCHIVE); + + gl_modulate = Cvar.Get("gl_modulate", "1.5", Defines.CVAR_ARCHIVE); + gl_log = Cvar.Get("gl_log", "0", 0); + gl_bitdepth = Cvar.Get("gl_bitdepth", "0", 0); + gl_mode = Cvar.Get("gl_mode", "3", Defines.CVAR_ARCHIVE); // 640x480 + gl_lightmap = Cvar.Get("gl_lightmap", "0", 0); + gl_shadows = Cvar.Get("gl_shadows", "0", Defines.CVAR_ARCHIVE); + gl_dynamic = Cvar.Get("gl_dynamic", "1", 0); + gl_nobind = Cvar.Get("gl_nobind", "0", 0); + gl_round_down = Cvar.Get("gl_round_down", "1", 0); + gl_picmip = Cvar.Get("gl_picmip", "0", 0); + gl_skymip = Cvar.Get("gl_skymip", "0", 0); + gl_showtris = Cvar.Get("gl_showtris", "0", 0); + gl_ztrick = Cvar.Get("gl_ztrick", "0", 0); + gl_finish = Cvar.Get("gl_finish", "0", Defines.CVAR_ARCHIVE); + gl_clear = Cvar.Get("gl_clear", "0", 0); + gl_cull = Cvar.Get("gl_cull", "1", 0); + gl_polyblend = Cvar.Get("gl_polyblend", "1", 0); + gl_flashblend = Cvar.Get("gl_flashblend", "0", 0); + gl_playermip = Cvar.Get("gl_playermip", "0", 0); + gl_monolightmap = Cvar.Get("gl_monolightmap", "0", 0); + gl_driver = Cvar.Get("gl_driver", "opengl32", Defines.CVAR_ARCHIVE); + gl_texturemode = Cvar.Get("gl_texturemode", "GL_LINEAR_MIPMAP_NEAREST", Defines.CVAR_ARCHIVE); + gl_texturealphamode = Cvar.Get("gl_texturealphamode", "default", Defines.CVAR_ARCHIVE); + gl_texturesolidmode = Cvar.Get("gl_texturesolidmode", "default", Defines.CVAR_ARCHIVE); + gl_lockpvs = Cvar.Get("gl_lockpvs", "0", 0); + + gl_vertex_arrays = Cvar.Get("gl_vertex_arrays", "1", Defines.CVAR_ARCHIVE); + + gl_ext_swapinterval = Cvar.Get("gl_ext_swapinterval", "1", Defines.CVAR_ARCHIVE); + gl_ext_palettedtexture = Cvar.Get("gl_ext_palettedtexture", "0", Defines.CVAR_ARCHIVE); + gl_ext_multitexture = Cvar.Get("gl_ext_multitexture", "1", Defines.CVAR_ARCHIVE); + gl_ext_pointparameters = Cvar.Get("gl_ext_pointparameters", "1", Defines.CVAR_ARCHIVE); + gl_ext_compiled_vertex_array = Cvar.Get("gl_ext_compiled_vertex_array", "1", Defines.CVAR_ARCHIVE); + + gl_drawbuffer = Cvar.Get("gl_drawbuffer", "GL_BACK", 0); + gl_swapinterval = Cvar.Get("gl_swapinterval", "0", Defines.CVAR_ARCHIVE); + + gl_saturatelighting = Cvar.Get("gl_saturatelighting", "0", 0); + + gl_3dlabs_broken = Cvar.Get("gl_3dlabs_broken", "1", Defines.CVAR_ARCHIVE); + + vid_fullscreen = Cvar.Get("vid_fullscreen", "0", Defines.CVAR_ARCHIVE); + vid_gamma = Cvar.Get("vid_gamma", "1.0", Defines.CVAR_ARCHIVE); + vid_ref = Cvar.Get("vid_ref", "lwjgl", Defines.CVAR_ARCHIVE); + + Cmd.AddCommand("imagelist", new xcommand_t() { + public void execute() { + GL_ImageList_f(); + } + }); + + Cmd.AddCommand("screenshot", new xcommand_t() { + public void execute() { + GL_ScreenShot_f(); + } + }); + Cmd.AddCommand("modellist", new xcommand_t() { + public void execute() { + Mod_Modellist_f(); + } + }); + Cmd.AddCommand("gl_strings", new xcommand_t() { + public void execute() { + GL_Strings_f(); + } + }); + } + + /** + * R_SetMode + */ + protected boolean R_SetMode() { + boolean fullscreen = (vid_fullscreen.value > 0.0f); + + vid_fullscreen.modified = false; + gl_mode.modified = false; + + Dimension dim = new Dimension(vid.width, vid.height); + + int err; // enum rserr_t + if ((err = glImpl.setMode(dim, (int) gl_mode.value, fullscreen)) == rserr_ok) { + gl_state.prev_mode = (int) gl_mode.value; + } + else { + if (err == rserr_invalid_fullscreen) { + Cvar.SetValue("vid_fullscreen", 0); + vid_fullscreen.modified = false; + VID.Printf(Defines.PRINT_ALL, "ref_gl::R_SetMode() - fullscreen unavailable in this mode\n"); + if ((err = glImpl.setMode(dim, (int) gl_mode.value, false)) == rserr_ok) + return true; + } + else if (err == rserr_invalid_mode) { + Cvar.SetValue("gl_mode", gl_state.prev_mode); + gl_mode.modified = false; + VID.Printf(Defines.PRINT_ALL, "ref_gl::R_SetMode() - invalid mode\n"); + } + + // try setting it back to something safe + if ((err = glImpl.setMode(dim, gl_state.prev_mode, false)) != rserr_ok) { + VID.Printf(Defines.PRINT_ALL, "ref_gl::R_SetMode() - could not revert to safe mode\n"); + return false; + } + } + return true; + } + + float[] r_turbsin = new float[256]; + + /** + * R_Init + */ + protected boolean R_Init() { + return R_Init(0, 0); + } + /** + * R_Init + */ + public boolean R_Init(int vid_xpos, int vid_ypos) { + + assert(Warp.SIN.length == 256) : "warpsin table bug"; + + // fill r_turbsin + for (int j = 0; j < 256; j++) { + r_turbsin[j] = Warp.SIN[j] * 0.5f; + } + + VID.Printf(Defines.PRINT_ALL, "ref_gl version: " + REF_VERSION + '\n'); + + Draw_GetPalette(); + + R_Register(); + + // set our "safe" modes + gl_state.prev_mode = 3; + + // create the window and set up the context + if (!R_SetMode()) { + VID.Printf(Defines.PRINT_ALL, "ref_gl::R_Init() - could not R_SetMode()\n"); + return false; + } + return true; + } + + /** + * R_Init2 + */ + public boolean R_Init2() { + VID.MenuInit(); + + /* + ** get our various GL strings + */ + gl_config.vendor_string = gl.glGetString(GL_VENDOR); + VID.Printf(Defines.PRINT_ALL, "GL_VENDOR: " + gl_config.vendor_string + '\n'); + gl_config.renderer_string = gl.glGetString(GL_RENDERER); + VID.Printf(Defines.PRINT_ALL, "GL_RENDERER: " + gl_config.renderer_string + '\n'); + gl_config.version_string = gl.glGetString(GL_VERSION); + VID.Printf(Defines.PRINT_ALL, "GL_VERSION: " + gl_config.version_string + '\n'); + gl_config.extensions_string = gl.glGetString(GL_EXTENSIONS); + VID.Printf(Defines.PRINT_ALL, "GL_EXTENSIONS: " + gl_config.extensions_string + '\n'); + + gl_config.parseOpenGLVersion(); + + String renderer_buffer = gl_config.renderer_string.toLowerCase(); + String vendor_buffer = gl_config.vendor_string.toLowerCase(); + + if (renderer_buffer.indexOf("voodoo") >= 0) { + if (renderer_buffer.indexOf("rush") < 0) + gl_config.renderer = GL_RENDERER_VOODOO; + else + gl_config.renderer = GL_RENDERER_VOODOO_RUSH; + } + else if (vendor_buffer.indexOf("sgi") >= 0) + gl_config.renderer = GL_RENDERER_SGI; + else if (renderer_buffer.indexOf("permedia") >= 0) + gl_config.renderer = GL_RENDERER_PERMEDIA2; + else if (renderer_buffer.indexOf("glint") >= 0) + gl_config.renderer = GL_RENDERER_GLINT_MX; + else if (renderer_buffer.indexOf("glzicd") >= 0) + gl_config.renderer = GL_RENDERER_REALIZM; + else if (renderer_buffer.indexOf("gdi") >= 0) + gl_config.renderer = GL_RENDERER_MCD; + else if (renderer_buffer.indexOf("pcx2") >= 0) + gl_config.renderer = GL_RENDERER_PCX2; + else if (renderer_buffer.indexOf("verite") >= 0) + gl_config.renderer = GL_RENDERER_RENDITION; + else + gl_config.renderer = GL_RENDERER_OTHER; + + String monolightmap = gl_monolightmap.string.toUpperCase(); + if (monolightmap.length() < 2 || monolightmap.charAt(1) != 'F') { + if (gl_config.renderer == GL_RENDERER_PERMEDIA2) { + Cvar.Set("gl_monolightmap", "A"); + VID.Printf(Defines.PRINT_ALL, "...using gl_monolightmap 'a'\n"); + } + else if ((gl_config.renderer & GL_RENDERER_POWERVR) != 0) { + Cvar.Set("gl_monolightmap", "0"); + } + else { + Cvar.Set("gl_monolightmap", "0"); + } + } + + // power vr can't have anything stay in the framebuffer, so + // the screen needs to redraw the tiled background every frame + if ((gl_config.renderer & GL_RENDERER_POWERVR) != 0) { + Cvar.Set("scr_drawall", "1"); + } + else { + Cvar.Set("scr_drawall", "0"); + } + + // MCD has buffering issues + if (gl_config.renderer == GL_RENDERER_MCD) { + Cvar.SetValue("gl_finish", 1); + } + + if ((gl_config.renderer & GL_RENDERER_3DLABS) != 0) { + if (gl_3dlabs_broken.value != 0.0f) + gl_config.allow_cds = false; + else + gl_config.allow_cds = true; + } + else { + gl_config.allow_cds = true; + } + + if (gl_config.allow_cds) + VID.Printf(Defines.PRINT_ALL, "...allowing CDS\n"); + else + VID.Printf(Defines.PRINT_ALL, "...disabling CDS\n"); + + /* + ** grab extensions + */ + if (gl_config.extensions_string.indexOf("GL_EXT_compiled_vertex_array") >= 0 + || gl_config.extensions_string.indexOf("GL_SGI_compiled_vertex_array") >= 0) { + VID.Printf(Defines.PRINT_ALL, "...enabling GL_EXT_compiled_vertex_array\n"); + // qgl.glLockArraysEXT = ( void * ) qwglGetProcAddress( "glLockArraysEXT" ); + if (gl_ext_compiled_vertex_array.value != 0.0f) + qglLockArraysEXT = true; + else + qglLockArraysEXT = false; + // qgl.glUnlockArraysEXT = ( void * ) qwglGetProcAddress( "glUnlockArraysEXT" ); + //qglUnlockArraysEXT = true; + } + else { + VID.Printf(Defines.PRINT_ALL, "...GL_EXT_compiled_vertex_array not found\n"); + qglLockArraysEXT = false; + } + + if (gl_config.extensions_string.indexOf("WGL_EXT_swap_control") >= 0) { + qwglSwapIntervalEXT = true; + VID.Printf(Defines.PRINT_ALL, "...enabling WGL_EXT_swap_control\n"); + } else { + qwglSwapIntervalEXT = false; + VID.Printf(Defines.PRINT_ALL, "...WGL_EXT_swap_control not found\n"); + } + + if (gl_config.extensions_string.indexOf("GL_EXT_point_parameters") >= 0) { + if (gl_ext_pointparameters.value != 0.0f) { + // qgl.glPointParameterfEXT = ( void (APIENTRY *)( GLenum, GLfloat ) ) qwglGetProcAddress( "glPointParameterfEXT" ); + qglPointParameterfEXT = true; + // qgl.glPointParameterfvEXT = ( void (APIENTRY *)( GLenum, const GLfloat * ) ) qwglGetProcAddress( "glPointParameterfvEXT" ); + VID.Printf(Defines.PRINT_ALL, "...using GL_EXT_point_parameters\n"); + } + else { + VID.Printf(Defines.PRINT_ALL, "...ignoring GL_EXT_point_parameters\n"); + } + } + else { + VID.Printf(Defines.PRINT_ALL, "...GL_EXT_point_parameters not found\n"); + } + + // #ifdef __linux__ + // if ( strstr( gl_config.extensions_string, "3DFX_set_global_palette" )) + // { + // if ( gl_ext_palettedtexture->value ) + // { + // VID.Printf( Defines.PRINT_ALL, "...using 3DFX_set_global_palette\n" ); + // qgl3DfxSetPaletteEXT = ( void ( APIENTRY * ) (GLuint *) )qwgl.glGetProcAddress( "gl3DfxSetPaletteEXT" ); + //// qglColorTableEXT = Fake_glColorTableEXT; + // } + // else + // { + // VID.Printf( Defines.PRINT_ALL, "...ignoring 3DFX_set_global_palette\n" ); + // } + // } + // else + // { + // VID.Printf( Defines.PRINT_ALL, "...3DFX_set_global_palette not found\n" ); + // } + // #endif + + if (!qglColorTableEXT + && gl_config.extensions_string.indexOf("GL_EXT_paletted_texture") >= 0 + && gl_config.extensions_string.indexOf("GL_EXT_shared_texture_palette") >= 0) { + if (gl_ext_palettedtexture.value != 0.0f) { + VID.Printf(Defines.PRINT_ALL, "...using GL_EXT_shared_texture_palette\n"); + qglColorTableEXT = false; // true; TODO jogl bug + } + else { + VID.Printf(Defines.PRINT_ALL, "...ignoring GL_EXT_shared_texture_palette\n"); + qglColorTableEXT = false; + } + } + else { + VID.Printf(Defines.PRINT_ALL, "...GL_EXT_shared_texture_palette not found\n"); + } + + if (gl_config.extensions_string.indexOf("GL_ARB_multitexture") >= 0) { + VID.Printf(Defines.PRINT_ALL, "...using GL_ARB_multitexture\n"); + qglActiveTextureARB = true; + TEXTURE0 = GL_TEXTURE0_ARB; + TEXTURE1 = GL_TEXTURE1_ARB; + } + else { + VID.Printf(Defines.PRINT_ALL, "...GL_ARB_multitexture not found\n"); + } + + if (!(qglActiveTextureARB)) { + VID.Printf(Defines.PRINT_ALL, "Missing multi-texturing!\n"); + return false; + } + + GL_SetDefaultState(); + + GL_InitImages(); + Mod_Init(); + R_InitParticleTexture(); + Draw_InitLocal(); + + int err = gl.glGetError(); + if (err != GL_NO_ERROR) + VID.Printf( + Defines.PRINT_ALL, + "gl.glGetError() = 0x%x\n\t%s\n", + new Vargs(2).add(err).add("" + gl.glGetString(err))); + + glImpl.endFrame(); + return true; + } + + /** + * R_Shutdown + */ + public void R_Shutdown() { + Cmd.RemoveCommand("modellist"); + Cmd.RemoveCommand("screenshot"); + Cmd.RemoveCommand("imagelist"); + Cmd.RemoveCommand("gl_strings"); + + Mod_FreeAll(); + + GL_ShutdownImages(); + + /* + * shut down OS specific OpenGL stuff like contexts, etc. + */ + glImpl.shutdown(); + } + + /** + * R_BeginFrame + */ + public void R_BeginFrame(float camera_separation) { + + gl_state.camera_separation = camera_separation; + + /* + ** change modes if necessary + */ + if (gl_mode.modified || vid_fullscreen.modified) { + // FIXME: only restart if CDS is required + cvar_t ref; + + ref = Cvar.Get("vid_ref", "lwjgl", 0); + ref.modified = true; + } + + if (gl_log.modified) { + glImpl.enableLogging((gl_log.value != 0.0f)); + gl_log.modified = false; + } + + if (gl_log.value != 0.0f) { + glImpl.logNewFrame(); + } + + /* + ** update 3Dfx gamma -- it is expected that a user will do a vid_restart + ** after tweaking this value + */ + if (vid_gamma.modified) { + vid_gamma.modified = false; + + if ((gl_config.renderer & GL_RENDERER_VOODOO) != 0) { + // wird erstmal nicht gebraucht + + /* + char envbuffer[1024]; + float g; + + g = 2.00 * ( 0.8 - ( vid_gamma->value - 0.5 ) ) + 1.0F; + Com_sprintf( envbuffer, sizeof(envbuffer), "SSTV2_GAMMA=%f", g ); + putenv( envbuffer ); + Com_sprintf( envbuffer, sizeof(envbuffer), "SST_GAMMA=%f", g ); + putenv( envbuffer ); + */ + VID.Printf(Defines.PRINT_DEVELOPER, "gamma anpassung fuer VOODOO nicht gesetzt"); + } + } + + glImpl.beginFrame(camera_separation); + + /* + ** go into 2D mode + */ + gl.glViewport(0, 0, vid.width, vid.height); + gl.glMatrixMode(GL_PROJECTION); + gl.glLoadIdentity(); + gl.glOrtho(0, vid.width, vid.height, 0, -99999, 99999); + gl.glMatrixMode(GL_MODELVIEW); + gl.glLoadIdentity(); + gl.glDisable(GL_DEPTH_TEST); + gl.glDisable(GL_CULL_FACE); + gl.glDisable(GL_BLEND); + gl.glEnable(GL_ALPHA_TEST); + gl.glColor4f(1, 1, 1, 1); + + /* + ** draw buffer stuff + */ + if (gl_drawbuffer.modified) { + gl_drawbuffer.modified = false; + + if (gl_state.camera_separation == 0 || !gl_state.stereo_enabled) { + if (gl_drawbuffer.string.equalsIgnoreCase("GL_FRONT")) + gl.glDrawBuffer(GL_FRONT); + else + gl.glDrawBuffer(GL_BACK); + } + } + + /* + ** texturemode stuff + */ + if (gl_texturemode.modified) { + GL_TextureMode(gl_texturemode.string); + gl_texturemode.modified = false; + } + + if (gl_texturealphamode.modified) { + GL_TextureAlphaMode(gl_texturealphamode.string); + gl_texturealphamode.modified = false; + } + + if (gl_texturesolidmode.modified) { + GL_TextureSolidMode(gl_texturesolidmode.string); + gl_texturesolidmode.modified = false; + } + + /* + ** swapinterval stuff + */ + GL_UpdateSwapInterval(); + + // + // clear screen if desired + // + R_Clear(); + } + + int[] r_rawpalette = new int[256]; + + /** + * R_SetPalette + */ + public void R_SetPalette(byte[] palette) { + // 256 RGB values (768 bytes) + // or null + int i; + int color = 0; + + if (palette != null) { + int j =0; + for (i = 0; i < 256; i++) { + color = (palette[j++] & 0xFF) << 0; + color |= (palette[j++] & 0xFF) << 8; + color |= (palette[j++] & 0xFF) << 16; + color |= 0xFF000000; + r_rawpalette[i] = color; + } + } + else { + for (i = 0; i < 256; i++) { + r_rawpalette[i] = d_8to24table[i] | 0xff000000; + } + } + GL_SetTexturePalette(r_rawpalette); + + gl.glClearColor(0, 0, 0, 0); + gl.glClear(GL_COLOR_BUFFER_BIT); + gl.glClearColor(1f, 0f, 0.5f, 0.5f); + } + + static final int NUM_BEAM_SEGS = 6; + float[][] start_points = new float[NUM_BEAM_SEGS][3]; + // array of vec3_t + float[][] end_points = new float[NUM_BEAM_SEGS][3]; // array of vec3_t + + // stack variable + private final float[] perpvec = { 0, 0, 0 }; // vec3_t + private final float[] direction = { 0, 0, 0 }; // vec3_t + private final float[] normalized_direction = { 0, 0, 0 }; // vec3_t + private final float[] oldorigin = { 0, 0, 0 }; // vec3_t + private final float[] origin = { 0, 0, 0 }; // vec3_t + /** + * R_DrawBeam + */ + void R_DrawBeam(entity_t e) { + oldorigin[0] = e.oldorigin[0]; + oldorigin[1] = e.oldorigin[1]; + oldorigin[2] = e.oldorigin[2]; + + origin[0] = e.origin[0]; + origin[1] = e.origin[1]; + origin[2] = e.origin[2]; + + normalized_direction[0] = direction[0] = oldorigin[0] - origin[0]; + normalized_direction[1] = direction[1] = oldorigin[1] - origin[1]; + normalized_direction[2] = direction[2] = oldorigin[2] - origin[2]; + + if (Math3D.VectorNormalize(normalized_direction) == 0.0f) + return; + + Math3D.PerpendicularVector(perpvec, normalized_direction); + Math3D.VectorScale(perpvec, e.frame / 2, perpvec); + + for (int i = 0; i < 6; i++) { + Math3D.RotatePointAroundVector( + start_points[i], + normalized_direction, + perpvec, + (360.0f / NUM_BEAM_SEGS) * i); + + Math3D.VectorAdd(start_points[i], origin, start_points[i]); + Math3D.VectorAdd(start_points[i], direction, end_points[i]); + } + + gl.glDisable(GL_TEXTURE_2D); + gl.glEnable(GL_BLEND); + gl.glDepthMask(false); + + float r = (d_8to24table[e.skinnum & 0xFF]) & 0xFF; + float g = (d_8to24table[e.skinnum & 0xFF] >> 8) & 0xFF; + float b = (d_8to24table[e.skinnum & 0xFF] >> 16) & 0xFF; + + r *= 1 / 255.0f; + g *= 1 / 255.0f; + b *= 1 / 255.0f; + + gl.glColor4f(r, g, b, e.alpha); + + gl.glBegin(GL_TRIANGLE_STRIP); + + float[] v; + + for (int i = 0; i < NUM_BEAM_SEGS; i++) { + v = start_points[i]; + gl.glVertex3f(v[0], v[1], v[2]); + v = end_points[i]; + gl.glVertex3f(v[0], v[1], v[2]); + v = start_points[(i + 1) % NUM_BEAM_SEGS]; + gl.glVertex3f(v[0], v[1], v[2]); + v = end_points[(i + 1) % NUM_BEAM_SEGS]; + gl.glVertex3f(v[0], v[1], v[2]); + } + gl.glEnd(); + + gl.glEnable(GL_TEXTURE_2D); + gl.glDisable(GL_BLEND); + gl.glDepthMask(true); + } +}
\ No newline at end of file diff --git a/src/jake2/render/fast/Mesh.java b/src/jake2/render/fast/Mesh.java new file mode 100644 index 0000000..2cd7d81 --- /dev/null +++ b/src/jake2/render/fast/Mesh.java @@ -0,0 +1,688 @@ +/* + * Mesh.java + * Copyright (C) 2003 + * + * $Id: Mesh.java,v 1.2 2006-11-21 00:50:46 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 jake2.render.fast; + +import jake2.Defines; +import jake2.client.VID; +import jake2.client.entity_t; +import jake2.qcommon.qfiles; +import jake2.render.image_t; +import jake2.util.Lib; +import jake2.util.Math3D; + +import java.nio.FloatBuffer; +import java.nio.IntBuffer; + +/** + * Mesh + * + * @author cwei + */ +public abstract class Mesh extends Light { + + // g_mesh.c: triangle model functions + /* + ============================================================= + + ALIAS MODELS + + ============================================================= + */ + + static final int NUMVERTEXNORMALS = 162; + + float[][] r_avertexnormals = Anorms.VERTEXNORMALS; + float[] shadevector = {0, 0, 0}; + float[] shadelight = {0, 0, 0}; + + // precalculated dot products for quantized angles + static final int SHADEDOT_QUANT = 16; + + float[][] r_avertexnormal_dots = Anorms.VERTEXNORMAL_DOTS; + + float[] shadedots = r_avertexnormal_dots[0]; + + /** + * GL_LerpVerts + * @param nverts + * @param ov + * @param verts + * @param move + * @param frontv + * @param backv + */ + void GL_LerpVerts(int nverts, int[] ov, int[] v, float[] move, float[] frontv, float[] backv ) + { + FloatBuffer lerp = vertexArrayBuf; + lerp.limit((nverts << 2) - nverts); // nverts * 3 + + int ovv, vv; + //PMM -- added RF_SHELL_DOUBLE, RF_SHELL_HALF_DAM + if ( (currententity.flags & ( Defines.RF_SHELL_RED | Defines.RF_SHELL_GREEN | Defines.RF_SHELL_BLUE | Defines.RF_SHELL_DOUBLE | Defines.RF_SHELL_HALF_DAM)) != 0 ) + { + float[] normal; + int j = 0; + for (int i=0 ; i < nverts; i++/* , v++, ov++, lerp+=4 */) + { + vv = v[i]; + normal = r_avertexnormals[(vv >>> 24 ) & 0xFF]; + ovv = ov[i]; + lerp.put(j, move[0] + (ovv & 0xFF)* backv[0] + (vv & 0xFF) * frontv[0] + normal[0] * Defines.POWERSUIT_SCALE); + lerp.put(j + 1, move[1] + ((ovv >>> 8) & 0xFF) * backv[1] + ((vv >>> 8) & 0xFF) * frontv[1] + normal[1] * Defines.POWERSUIT_SCALE); + lerp.put(j + 2, move[2] + ((ovv >>> 16) & 0xFF) * backv[2] + ((vv >>> 16) & 0xFF) * frontv[2] + normal[2] * Defines.POWERSUIT_SCALE); + j += 3; + } + } + else + { + int j = 0; + for (int i=0 ; i < nverts; i++ /* , v++, ov++, lerp+=4 */) + { + ovv = ov[i]; + vv = v[i]; + + lerp.put(j, move[0] + (ovv & 0xFF)* backv[0] + (vv & 0xFF)*frontv[0]); + lerp.put(j + 1, move[1] + ((ovv >>> 8) & 0xFF)* backv[1] + ((vv >>> 8) & 0xFF)*frontv[1]); + lerp.put(j + 2, move[2] + ((ovv >>> 16) & 0xFF)* backv[2] + ((vv >>> 16) & 0xFF)*frontv[2]); + j += 3; + } + } + } + + FloatBuffer colorArrayBuf = Lib.newFloatBuffer(qfiles.MAX_VERTS * 4); + FloatBuffer vertexArrayBuf = Lib.newFloatBuffer(qfiles.MAX_VERTS * 3); + FloatBuffer textureArrayBuf = Lib.newFloatBuffer(qfiles.MAX_VERTS * 2); + boolean isFilled = false; + float[] tmpVec = {0, 0, 0}; + float[][] vectors = { + {0, 0, 0}, {0, 0, 0}, {0, 0, 0} // 3 mal vec3_t + }; + + // stack variable + private final float[] move = {0, 0, 0}; // vec3_t + private final float[] frontv = {0, 0, 0}; // vec3_t + private final float[] backv = {0, 0, 0}; // vec3_t + /** + * GL_DrawAliasFrameLerp + * + * interpolates between two frames and origins + * FIXME: batch lerp all vertexes + */ + void GL_DrawAliasFrameLerp(qfiles.dmdl_t paliashdr, float backlerp) + { + qfiles.daliasframe_t frame = paliashdr.aliasFrames[currententity.frame]; + + int[] verts = frame.verts; + + qfiles.daliasframe_t oldframe = paliashdr.aliasFrames[currententity.oldframe]; + + int[] ov = oldframe.verts; + + float alpha; + if ((currententity.flags & Defines.RF_TRANSLUCENT) != 0) + alpha = currententity.alpha; + else + alpha = 1.0f; + + // PMM - added double shell + if ( (currententity.flags & ( Defines.RF_SHELL_RED | Defines.RF_SHELL_GREEN | Defines.RF_SHELL_BLUE | Defines.RF_SHELL_DOUBLE | Defines.RF_SHELL_HALF_DAM)) != 0) + gl.glDisable( GL_TEXTURE_2D ); + + float frontlerp = 1.0f - backlerp; + + // move should be the delta back to the previous frame * backlerp + Math3D.VectorSubtract (currententity.oldorigin, currententity.origin, frontv); + Math3D.AngleVectors (currententity.angles, vectors[0], vectors[1], vectors[2]); + + move[0] = Math3D.DotProduct (frontv, vectors[0]); // forward + move[1] = -Math3D.DotProduct (frontv, vectors[1]); // left + move[2] = Math3D.DotProduct (frontv, vectors[2]); // up + + Math3D.VectorAdd (move, oldframe.translate, move); + + for (int i=0 ; i<3 ; i++) + { + move[i] = backlerp*move[i] + frontlerp*frame.translate[i]; + frontv[i] = frontlerp*frame.scale[i]; + backv[i] = backlerp*oldframe.scale[i]; + } + + // ab hier wird optimiert + + GL_LerpVerts( paliashdr.num_xyz, ov, verts, move, frontv, backv ); + + //gl.gl.glEnableClientState( GL_VERTEX_ARRAY ); + gl.glVertexPointer( 3, 0, vertexArrayBuf ); + + // PMM - added double damage shell + if ( (currententity.flags & ( Defines.RF_SHELL_RED | Defines.RF_SHELL_GREEN | Defines.RF_SHELL_BLUE | Defines.RF_SHELL_DOUBLE | Defines.RF_SHELL_HALF_DAM)) != 0) + { + gl.glColor4f( shadelight[0], shadelight[1], shadelight[2], alpha ); + } + else + { + gl.glEnableClientState( GL_COLOR_ARRAY ); + gl.glColorPointer( 4, 0, colorArrayBuf ); + + // + // pre light everything + // + FloatBuffer color = colorArrayBuf; + float l; + int size = paliashdr.num_xyz; + int j = 0; + for (int i = 0; i < size; i++ ) + { + l = shadedots[(verts[i] >>> 24) & 0xFF]; + color.put(j, l * shadelight[0]); + color.put(j + 1, l * shadelight[1]); + color.put(j + 2, l * shadelight[2]); + color.put(j + 3, alpha); + j += 4; + } + } + + gl.glClientActiveTextureARB(TEXTURE0); + gl.glTexCoordPointer( 2, 0, textureArrayBuf); + //gl.gl.glEnableClientState( GL_TEXTURE_COORD_ARRAY); + + int pos = 0; + int[] counts = paliashdr.counts; + + IntBuffer srcIndexBuf = null; + + FloatBuffer dstTextureCoords = textureArrayBuf; + FloatBuffer srcTextureCoords = paliashdr.textureCoordBuf; + + int dstIndex = 0; + int srcIndex = 0; + int count; + int mode; + int size = counts.length; + for (int j = 0; j < size; j++) { + + // get the vertex count and primitive type + count = counts[j]; + if (count == 0) + break; // done + + srcIndexBuf = paliashdr.indexElements[j]; + + mode = GL_TRIANGLE_STRIP; + if (count < 0) { + mode = GL_TRIANGLE_FAN; + count = -count; + } + srcIndex = pos << 1; + srcIndex--; + for (int k = 0; k < count; k++) { + dstIndex = srcIndexBuf.get(k) << 1; + dstTextureCoords.put(dstIndex, srcTextureCoords.get(++srcIndex)); + dstTextureCoords.put(++dstIndex, srcTextureCoords.get(++srcIndex)); + } + + gl.glDrawElements(mode, srcIndexBuf); + pos += count; + } + + // PMM - added double damage shell + if ( (currententity.flags & ( Defines.RF_SHELL_RED | Defines.RF_SHELL_GREEN | Defines.RF_SHELL_BLUE | Defines.RF_SHELL_DOUBLE | Defines.RF_SHELL_HALF_DAM)) != 0 ) + gl.glEnable( GL_TEXTURE_2D ); + + gl.glDisableClientState( GL_COLOR_ARRAY ); + } + + private final float[] point = {0, 0, 0}; + /** + * GL_DrawAliasShadow + */ + void GL_DrawAliasShadow(qfiles.dmdl_t paliashdr, int posenum) + { + float lheight = currententity.origin[2] - lightspot[2]; + qfiles.daliasframe_t frame = paliashdr.aliasFrames[currententity.frame]; + int[] order = paliashdr.glCmds; + float height = -lheight + 1.0f; + + int orderIndex = 0; + int index = 0; + + // TODO shadow drawing with vertex arrays + + int count; + while (true) + { + // get the vertex count and primitive type + count = order[orderIndex++]; + if (count == 0) + break; // done + if (count < 0) + { + count = -count; + gl.glBegin (GL_TRIANGLE_FAN); + } + else + gl.glBegin (GL_TRIANGLE_STRIP); + + do + { + index = order[orderIndex + 2] * 3; + point[0] = vertexArrayBuf.get(index); + point[1] = vertexArrayBuf.get(index + 1); + point[2] = vertexArrayBuf.get(index + 2); + + point[0] -= shadevector[0]*(point[2]+lheight); + point[1] -= shadevector[1]*(point[2]+lheight); + point[2] = height; + gl.glVertex3f(point[0], point[1], point[2]); + + orderIndex += 3; + + } while (--count != 0); + + gl.glEnd (); + } + } + +// TODO sync with jogl renderer. hoz + // stack variable + private final float[] mins = { 0, 0, 0 }; + private final float[] maxs = { 0, 0, 0 }; + /** + * R_CullAliasModel + */ + boolean R_CullAliasModel(entity_t e) { + qfiles.dmdl_t paliashdr = (qfiles.dmdl_t) currentmodel.extradata; + + if ((e.frame >= paliashdr.num_frames) || (e.frame < 0)) { + VID.Printf(Defines.PRINT_ALL, "R_CullAliasModel " + currentmodel.name + ": no such frame " + e.frame + '\n'); + e.frame = 0; + } + if ((e.oldframe >= paliashdr.num_frames) || (e.oldframe < 0)) { + VID.Printf(Defines.PRINT_ALL, "R_CullAliasModel " + currentmodel.name + ": no such oldframe " + e.oldframe + '\n'); + e.oldframe = 0; + } + + qfiles.daliasframe_t pframe = paliashdr.aliasFrames[e.frame]; + qfiles.daliasframe_t poldframe = paliashdr.aliasFrames[e.oldframe]; + + /* + ** compute axially aligned mins and maxs + */ + if (pframe == poldframe) { + for (int i = 0; i < 3; i++) { + mins[i] = pframe.translate[i]; + maxs[i] = mins[i] + pframe.scale[i] * 255; + } + } else { + float thismaxs, oldmaxs; + for (int i = 0; i < 3; i++) { + thismaxs = pframe.translate[i] + pframe.scale[i] * 255; + + oldmaxs = poldframe.translate[i] + poldframe.scale[i] * 255; + + if (pframe.translate[i] < poldframe.translate[i]) + mins[i] = pframe.translate[i]; + else + mins[i] = poldframe.translate[i]; + + if (thismaxs > oldmaxs) + maxs[i] = thismaxs; + else + maxs[i] = oldmaxs; + } + } + + /* + ** compute a full bounding box + */ + float[] tmp; + for (int i = 0; i < 8; i++) { + tmp = bbox[i]; + if ((i & 1) != 0) + tmp[0] = mins[0]; + else + tmp[0] = maxs[0]; + + if ((i & 2) != 0) + tmp[1] = mins[1]; + else + tmp[1] = maxs[1]; + + if ((i & 4) != 0) + tmp[2] = mins[2]; + else + tmp[2] = maxs[2]; + } + + /* + ** rotate the bounding box + */ + tmp = mins; + Math3D.VectorCopy(e.angles, tmp); + tmp[YAW] = -tmp[YAW]; + Math3D.AngleVectors(tmp, vectors[0], vectors[1], vectors[2]); + + for (int i = 0; i < 8; i++) { + Math3D.VectorCopy(bbox[i], tmp); + + bbox[i][0] = Math3D.DotProduct(vectors[0], tmp); + bbox[i][1] = -Math3D.DotProduct(vectors[1], tmp); + bbox[i][2] = Math3D.DotProduct(vectors[2], tmp); + + Math3D.VectorAdd(e.origin, bbox[i], bbox[i]); + } + + int f, mask; + int aggregatemask = ~0; // 0xFFFFFFFF + + for (int p = 0; p < 8; p++) { + mask = 0; + + for (f = 0; f < 4; f++) { + float dp = Math3D.DotProduct(frustum[f].normal, bbox[p]); + + if ((dp - frustum[f].dist) < 0) { + mask |= (1 << f); + } + } + + aggregatemask &= mask; + } + + if (aggregatemask != 0) { + return true; + } + + return false; + } + + + // bounding box + float[][] bbox = { + {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, + {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0} + }; + +// TODO sync with jogl renderer. hoz + /** + * R_DrawAliasModel + */ + void R_DrawAliasModel(entity_t e) + { + if ( ( e.flags & Defines.RF_WEAPONMODEL ) == 0) + { + if ( R_CullAliasModel(e) ) + return; + } + + if ( (e.flags & Defines.RF_WEAPONMODEL) != 0 ) + { + if ( r_lefthand.value == 2.0f ) + return; + } + + qfiles.dmdl_t paliashdr = (qfiles.dmdl_t)currentmodel.extradata; + + // + // get lighting information + // + // PMM - rewrote, reordered to handle new shells & mixing + // PMM - 3.20 code .. replaced with original way of doing it to keep mod authors happy + // + int i; + if ( (currententity.flags & ( Defines.RF_SHELL_HALF_DAM | Defines.RF_SHELL_GREEN | Defines.RF_SHELL_RED | Defines.RF_SHELL_BLUE | Defines.RF_SHELL_DOUBLE )) != 0 ) + { + Math3D.VectorClear(shadelight); + if ((currententity.flags & Defines.RF_SHELL_HALF_DAM) != 0) + { + shadelight[0] = 0.56f; + shadelight[1] = 0.59f; + shadelight[2] = 0.45f; + } + if ( (currententity.flags & Defines.RF_SHELL_DOUBLE) != 0 ) + { + shadelight[0] = 0.9f; + shadelight[1] = 0.7f; + } + if ( (currententity.flags & Defines.RF_SHELL_RED) != 0 ) + shadelight[0] = 1.0f; + if ( (currententity.flags & Defines.RF_SHELL_GREEN) != 0 ) + shadelight[1] = 1.0f; + if ( (currententity.flags & Defines.RF_SHELL_BLUE) != 0 ) + shadelight[2] = 1.0f; + } + + else if ( (currententity.flags & Defines.RF_FULLBRIGHT) != 0 ) + { + for (i=0 ; i<3 ; i++) + shadelight[i] = 1.0f; + } + else + { + R_LightPoint (currententity.origin, shadelight); + + // player lighting hack for communication back to server + // big hack! + if ( (currententity.flags & Defines.RF_WEAPONMODEL) != 0 ) + { + // pick the greatest component, which should be the same + // as the mono value returned by software + if (shadelight[0] > shadelight[1]) + { + if (shadelight[0] > shadelight[2]) + r_lightlevel.value = 150*shadelight[0]; + else + r_lightlevel.value = 150*shadelight[2]; + } + else + { + if (shadelight[1] > shadelight[2]) + r_lightlevel.value = 150*shadelight[1]; + else + r_lightlevel.value = 150*shadelight[2]; + } + } + + if ( gl_monolightmap.string.charAt(0) != '0' ) + { + float s = shadelight[0]; + + if ( s < shadelight[1] ) + s = shadelight[1]; + if ( s < shadelight[2] ) + s = shadelight[2]; + + shadelight[0] = s; + shadelight[1] = s; + shadelight[2] = s; + } + } + + if ( (currententity.flags & Defines.RF_MINLIGHT) != 0 ) + { + for (i=0 ; i<3 ; i++) + if (shadelight[i] > 0.1f) + break; + if (i == 3) + { + shadelight[0] = 0.1f; + shadelight[1] = 0.1f; + shadelight[2] = 0.1f; + } + } + + if ( (currententity.flags & Defines.RF_GLOW) != 0 ) + { // bonus items will pulse with time + float scale; + float min; + + scale = (float)(0.1f * Math.sin(r_newrefdef.time*7)); + for (i=0 ; i<3 ; i++) + { + min = shadelight[i] * 0.8f; + shadelight[i] += scale; + if (shadelight[i] < min) + shadelight[i] = min; + } + } + + // ================= + // PGM ir goggles color override + if ( (r_newrefdef.rdflags & Defines.RDF_IRGOGGLES) != 0 && (currententity.flags & Defines.RF_IR_VISIBLE) != 0) + { + shadelight[0] = 1.0f; + shadelight[1] = 0.0f; + shadelight[2] = 0.0f; + } + // PGM + // ================= + + shadedots = r_avertexnormal_dots[((int)(currententity.angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)]; + + float an = (float)(currententity.angles[1]/180*Math.PI); + shadevector[0] = (float)Math.cos(-an); + shadevector[1] = (float)Math.sin(-an); + shadevector[2] = 1; + Math3D.VectorNormalize(shadevector); + + // + // locate the proper data + // + + c_alias_polys += paliashdr.num_tris; + + // + // draw all the triangles + // + if ( (currententity.flags & Defines.RF_DEPTHHACK) != 0) // hack the depth range to prevent view model from poking into walls + gl.glDepthRange(gldepthmin, gldepthmin + 0.3*(gldepthmax-gldepthmin)); + + if ( (currententity.flags & Defines.RF_WEAPONMODEL) != 0 && (r_lefthand.value == 1.0f) ) + { + gl.glMatrixMode( GL_PROJECTION ); + gl.glPushMatrix(); + gl.glLoadIdentity(); + gl.glScalef( -1, 1, 1 ); + MYgluPerspective( r_newrefdef.fov_y, ( float ) r_newrefdef.width / r_newrefdef.height, 4, 4096); + gl.glMatrixMode( GL_MODELVIEW ); + + gl.glCullFace( GL_BACK ); + } + + gl.glPushMatrix (); + e.angles[PITCH] = -e.angles[PITCH]; // sigh. + R_RotateForEntity (e); + e.angles[PITCH] = -e.angles[PITCH]; // sigh. + + + + image_t skin; + // select skin + if (currententity.skin != null) + skin = currententity.skin; // custom player skin + else + { + if (currententity.skinnum >= qfiles.MAX_MD2SKINS) + skin = currentmodel.skins[0]; + else + { + skin = currentmodel.skins[currententity.skinnum]; + if (skin == null) + skin = currentmodel.skins[0]; + } + } + if (skin == null) + skin = r_notexture; // fallback... + GL_Bind(skin.texnum); + + // draw it + + gl.glShadeModel (GL_SMOOTH); + + GL_TexEnv( GL_MODULATE ); + if ( (currententity.flags & Defines.RF_TRANSLUCENT) != 0 ) + { + gl.glEnable (GL_BLEND); + } + + + if ( (currententity.frame >= paliashdr.num_frames) + || (currententity.frame < 0) ) + { + VID.Printf (Defines.PRINT_ALL, "R_DrawAliasModel " + currentmodel.name +": no such frame " + currententity.frame + '\n'); + currententity.frame = 0; + currententity.oldframe = 0; + } + + if ( (currententity.oldframe >= paliashdr.num_frames) + || (currententity.oldframe < 0)) + { + VID.Printf (Defines.PRINT_ALL, "R_DrawAliasModel " + currentmodel.name +": no such oldframe " + currententity.oldframe + '\n'); + currententity.frame = 0; + currententity.oldframe = 0; + } + + if ( r_lerpmodels.value == 0.0f) + currententity.backlerp = 0; + + GL_DrawAliasFrameLerp(paliashdr, currententity.backlerp); + + GL_TexEnv( GL_REPLACE ); + gl.glShadeModel (GL_FLAT); + + gl.glPopMatrix (); + + if ( ( currententity.flags & Defines.RF_WEAPONMODEL ) != 0 && ( r_lefthand.value == 1.0F ) ) + { + gl.glMatrixMode( GL_PROJECTION ); + gl.glPopMatrix(); + gl.glMatrixMode( GL_MODELVIEW ); + gl.glCullFace( GL_FRONT ); + } + + if ( (currententity.flags & Defines.RF_TRANSLUCENT) != 0 ) + { + gl.glDisable (GL_BLEND); + } + + if ( (currententity.flags & Defines.RF_DEPTHHACK) != 0) + gl.glDepthRange (gldepthmin, gldepthmax); + + if ( gl_shadows.value != 0.0f && (currententity.flags & (Defines.RF_TRANSLUCENT | Defines.RF_WEAPONMODEL)) == 0) + { + gl.glPushMatrix (); + R_RotateForEntity (e); + gl.glDisable (GL_TEXTURE_2D); + gl.glEnable (GL_BLEND); + gl.glColor4f (0,0,0,0.5f); + GL_DrawAliasShadow (paliashdr, currententity.frame ); + gl.glEnable (GL_TEXTURE_2D); + gl.glDisable (GL_BLEND); + gl.glPopMatrix (); + } + gl.glColor4f (1,1,1,1); + } +}
\ No newline at end of file diff --git a/src/jake2/render/fast/Misc.java b/src/jake2/render/fast/Misc.java new file mode 100644 index 0000000..d4df9af --- /dev/null +++ b/src/jake2/render/fast/Misc.java @@ -0,0 +1,282 @@ +/* + * Misc.java + * Copyright (C) 2003 + * + * $Id: Misc.java,v 1.2 2006-11-21 00:50:46 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 jake2.render.fast; + +import jake2.Defines; +import jake2.client.VID; +import jake2.qcommon.FS; +import jake2.util.Lib; + +import java.io.*; +import java.nio.FloatBuffer; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; + +/** + * Misc + * + * @author cwei + */ +public final class Misc extends Mesh { + + /* + ================== + R_InitParticleTexture + ================== + */ + byte[][] dottexture = + { + {0,0,0,0,0,0,0,0}, + {0,0,1,1,0,0,0,0}, + {0,1,1,1,1,0,0,0}, + {0,1,1,1,1,0,0,0}, + {0,0,1,1,0,0,0,0}, + {0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0}, + }; + + void R_InitParticleTexture() + { + int x,y; + byte[] data = new byte[8 * 8 * 4]; + + // + // particle texture + // + for (x=0 ; x<8 ; x++) + { + for (y=0 ; y<8 ; y++) + { + data[y * 32 + x * 4 + 0] = (byte)255; + data[y * 32 + x * 4 + 1] = (byte)255; + data[y * 32 + x * 4 + 2] = (byte)255; + data[y * 32 + x * 4 + 3] = (byte)(dottexture[x][y]*255); + + } + } + r_particletexture = GL_LoadPic("***particle***", data, 8, 8, it_sprite, 32); + + // + // also use this for bad textures, but without alpha + // + for (x=0 ; x<8 ; x++) + { + for (y=0 ; y<8 ; y++) + { + data[y * 32 + x * 4 + 0] = (byte)(dottexture[x&3][y&3]*255); + data[y * 32 + x * 4 + 1] = 0; // dottexture[x&3][y&3]*255; + data[y * 32 + x * 4 + 2] = 0; //dottexture[x&3][y&3]*255; + data[y * 32 + x * 4 + 3] = (byte)255; + } + } + r_notexture = GL_LoadPic("***r_notexture***", data, 8, 8, it_wall, 32); + } + +// /* +// ============================================================================== +// +// SCREEN SHOTS +// +// ============================================================================== +// */ +// +// typedef struct _TargaHeader { +// unsigned char id_length, colormap_type, image_type; +// unsigned short colormap_index, colormap_length; +// unsigned char colormap_size; +// unsigned short x_origin, y_origin, width, height; +// unsigned char pixel_size, attributes; +// } TargaHeader; + + private final static int TGA_HEADER_SIZE = 18; + + /* + ================== + GL_ScreenShot_f + ================== + */ + void GL_ScreenShot_f() { + StringBuffer sb = new StringBuffer(FS.Gamedir() + "/scrshot/jake00.tga"); + FS.CreatePath(sb.toString()); + File file = new File(sb.toString()); + // find a valid file name + int i = 0; int offset = sb.length() - 6; + while (file.exists() && i++ < 100) { + sb.setCharAt(offset, (char) ((i/10) + '0')); + sb.setCharAt(offset + 1, (char) ((i%10) + '0')); + file = new File(sb.toString()); + } + if (i == 100) { + VID.Printf(Defines.PRINT_ALL, "Clean up your screenshots\n"); + return; + } + + try { + RandomAccessFile out = new RandomAccessFile(file, "rw"); + FileChannel ch = out.getChannel(); + int fileLength = TGA_HEADER_SIZE + vid.width * vid.height * 3; + out.setLength(fileLength); + MappedByteBuffer image = ch.map(FileChannel.MapMode.READ_WRITE, 0, + fileLength); + + // write the TGA header + image.put(0, (byte) 0).put(1, (byte) 0); + image.put(2, (byte) 2); // uncompressed type + image.put(12, (byte) (vid.width & 0xFF)); // vid.width + image.put(13, (byte) (vid.width >> 8)); // vid.width + image.put(14, (byte) (vid.height & 0xFF)); // vid.height + image.put(15, (byte) (vid.height >> 8)); // vid.height + image.put(16, (byte) 24); // pixel size + + // go to image data position + image.position(TGA_HEADER_SIZE); + + + // change pixel alignment for reading + if (vid.width % 4 != 0) { + gl.glPixelStorei(GL_PACK_ALIGNMENT, 1); + } + + // OpenGL 1.2+ supports the GL_BGR color format + // check the GL_VERSION to use the TARGA BGR order if possible + // e.g.: 1.5.2 NVIDIA 66.29 + if (gl_config.getOpenGLVersion() >= 1.2f) { + // read the BGR values into the image buffer + gl.glReadPixels(0, 0, vid.width, vid.height, GL_BGR, GL_UNSIGNED_BYTE, image); + } else { + // read the RGB values into the image buffer + gl.glReadPixels(0, 0, vid.width, vid.height, GL_RGB, GL_UNSIGNED_BYTE, image); + // flip RGB to BGR + byte tmp; + for (i = TGA_HEADER_SIZE; i < fileLength; i += 3) { + tmp = image.get(i); + image.put(i, image.get(i + 2)); + image.put(i + 2, tmp); + } + } + // reset to default alignment + gl.glPixelStorei(GL_PACK_ALIGNMENT, 4); + // close the file channel + ch.close(); + } catch (IOException e) { + VID.Printf(Defines.PRINT_ALL, e.getMessage() + '\n'); + } + + VID.Printf(Defines.PRINT_ALL, "Wrote " + file + '\n'); + } + + /* + ** GL_Strings_f + */ + void GL_Strings_f() { + VID.Printf(Defines.PRINT_ALL, "GL_VENDOR: " + gl_config.vendor_string + '\n'); + VID.Printf(Defines.PRINT_ALL, "GL_RENDERER: " + gl_config.renderer_string + '\n'); + VID.Printf(Defines.PRINT_ALL, "GL_VERSION: " + gl_config.version_string + '\n'); + VID.Printf(Defines.PRINT_ALL, "GL_EXTENSIONS: " + gl_config.extensions_string + '\n'); + } + + /* + ** GL_SetDefaultState + */ + void GL_SetDefaultState() + { + gl.glClearColor(1f,0f, 0.5f , 0.5f); // original quake2 + //gl.gl.glClearColor(0, 0, 0, 0); // replaced with black + gl.glCullFace(GL_FRONT); + gl.glEnable(GL_TEXTURE_2D); + + gl.glEnable(GL_ALPHA_TEST); + gl.glAlphaFunc(GL_GREATER, 0.666f); + + gl.glDisable (GL_DEPTH_TEST); + gl.glDisable (GL_CULL_FACE); + gl.glDisable (GL_BLEND); + + gl.glColor4f (1,1,1,1); + + gl.glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + gl.glShadeModel (GL_FLAT); + + GL_TextureMode( gl_texturemode.string ); + GL_TextureAlphaMode( gl_texturealphamode.string ); + GL_TextureSolidMode( gl_texturesolidmode.string ); + + gl.glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); + gl.glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + + gl.glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + gl.glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + gl.glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + GL_TexEnv( GL_REPLACE ); + + if ( qglPointParameterfEXT ) + { + // float[] attenuations = { gl_particle_att_a.value, gl_particle_att_b.value, gl_particle_att_c.value }; + FloatBuffer att_buffer=Lib.newFloatBuffer(4); + att_buffer.put(0,gl_particle_att_a.value); + att_buffer.put(1,gl_particle_att_b.value); + att_buffer.put(2,gl_particle_att_c.value); + + gl.glEnable( GL_POINT_SMOOTH ); + gl.glPointParameterfEXT(GL_POINT_SIZE_MIN_EXT, gl_particle_min_size.value ); + gl.glPointParameterfEXT(GL_POINT_SIZE_MAX_EXT, gl_particle_max_size.value ); + gl.glPointParameterEXT(GL_DISTANCE_ATTENUATION_EXT, att_buffer ); + } + + if ( qglColorTableEXT && gl_ext_palettedtexture.value != 0.0f ) + { + gl.glEnable(GL_SHARED_TEXTURE_PALETTE_EXT ); + + GL_SetTexturePalette( d_8to24table ); + } + + GL_UpdateSwapInterval(); + + /* + * vertex array extension + */ + gl.glEnableClientState(GL_VERTEX_ARRAY); + gl.glClientActiveTextureARB(TEXTURE0); + gl.glEnableClientState(GL_TEXTURE_COORD_ARRAY); + } + + void GL_UpdateSwapInterval() + { + if ( gl_swapinterval.modified ) + { + gl_swapinterval.modified = false; + if ( !gl_state.stereo_enabled ) + { + if (qwglSwapIntervalEXT) { + // ((WGL)gl).wgl.glSwapIntervalEXT((int)gl_swapinterval.value); + } + } + } + } +} diff --git a/src/jake2/render/fast/Model.java b/src/jake2/render/fast/Model.java new file mode 100644 index 0000000..47541d9 --- /dev/null +++ b/src/jake2/render/fast/Model.java @@ -0,0 +1,1358 @@ +/* + * Model.java + * Copyright (C) 2003 + * + * $Id: Model.java,v 1.2 2006-11-21 00:50:46 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 jake2.render.fast; + +import jake2.Defines; +import jake2.client.VID; +import jake2.game.cplane_t; +import jake2.game.cvar_t; +import jake2.qcommon.*; +import jake2.render.*; +import jake2.util.*; + +import java.nio.*; +import java.util.Arrays; +import java.util.Vector; + +/** + * Model + * + * @author cwei + */ +public abstract class Model extends Surf { + + // models.c -- model loading and caching + + model_t loadmodel; + int modfilelen; + + byte[] mod_novis = new byte[Defines.MAX_MAP_LEAFS/8]; + + static final int MAX_MOD_KNOWN = 512; + model_t[] mod_known = new model_t[MAX_MOD_KNOWN]; + int mod_numknown; + + // the inline * models from the current map are kept seperate + model_t[] mod_inline = new model_t[MAX_MOD_KNOWN]; + + abstract void GL_SubdivideSurface(msurface_t surface); // Warp.java + + /* + =============== + Mod_PointInLeaf + =============== + */ + mleaf_t Mod_PointInLeaf(float[] p, model_t model) + { + mnode_t node; + float d; + cplane_t plane; + + if (model == null || model.nodes == null) + Com.Error (Defines.ERR_DROP, "Mod_PointInLeaf: bad model"); + + node = model.nodes[0]; // root node + while (true) + { + if (node.contents != -1) + return (mleaf_t)node; + + plane = node.plane; + d = Math3D.DotProduct(p, plane.normal) - plane.dist; + if (d > 0) + node = node.children[0]; + else + node = node.children[1]; + } + // never reached + } + + + byte[] decompressed = new byte[Defines.MAX_MAP_LEAFS / 8]; + byte[] model_visibility = new byte[Defines.MAX_MAP_VISIBILITY]; + + /* + =================== + Mod_DecompressVis + =================== + */ + byte[] Mod_DecompressVis(byte[] in, int offset, model_t model) + { + int c; + byte[] out; + int outp, inp; + int row; + + row = (model.vis.numclusters+7)>>3; + out = decompressed; + outp = 0; + inp = offset; + + if (in == null) + { // no vis info, so make all visible + while (row != 0) + { + out[outp++] = (byte)0xFF; + row--; + } + return decompressed; + } + + do + { + if (in[inp] != 0) + { + out[outp++] = in[inp++]; + continue; + } + + c = in[inp + 1] & 0xFF; + inp += 2; + while (c != 0) + { + out[outp++] = 0; + c--; + } + } while (outp < row); + + return decompressed; + } + + /* + ============== + Mod_ClusterPVS + ============== + */ + byte[] Mod_ClusterPVS(int cluster, model_t model) + { + if (cluster == -1 || model.vis == null) + return mod_novis; + //return Mod_DecompressVis( (byte *)model.vis + model.vis.bitofs[cluster][Defines.DVIS_PVS], model); + return Mod_DecompressVis(model_visibility, model.vis.bitofs[cluster][Defines.DVIS_PVS], model); + } + + +// =============================================================================== + + /* + ================ + Mod_Modellist_f + ================ + */ + void Mod_Modellist_f() + { + int i; + model_t mod; + int total; + + total = 0; + VID.Printf(Defines.PRINT_ALL,"Loaded models:\n"); + for (i=0; i < mod_numknown ; i++) + { + mod = mod_known[i]; + if (mod.name.length() == 0) + continue; + + VID.Printf (Defines.PRINT_ALL, "%8i : %s\n", new Vargs(2).add(mod.extradatasize).add(mod.name)); + total += mod.extradatasize; + } + VID.Printf (Defines.PRINT_ALL, "Total resident: " + total +'\n'); + } + + /* + =============== + Mod_Init + =============== + */ + void Mod_Init() + { + // init mod_known + for (int i=0; i < MAX_MOD_KNOWN; i++) { + mod_known[i] = new model_t(); + } + Arrays.fill(mod_novis, (byte)0xff); + } + + byte[] fileBuffer; + + /* + ================== + Mod_ForName + + Loads in a model for the given name + ================== + */ + model_t Mod_ForName(String name, boolean crash) + { + model_t mod = null; + int i; + + if (name == null || name.length() == 0) + Com.Error(Defines.ERR_DROP, "Mod_ForName: NULL name"); + + // + // inline models are grabbed only from worldmodel + // + if (name.charAt(0) == '*') + { + i = Integer.parseInt(name.substring(1)); + if (i < 1 || r_worldmodel == null || i >= r_worldmodel.numsubmodels) + Com.Error (Defines.ERR_DROP, "bad inline model number"); + return mod_inline[i]; + } + + // + // search the currently loaded models + // + for (i=0; i<mod_numknown ; i++) + { + mod = mod_known[i]; + + if (mod.name.length() == 0) + continue; + if (mod.name.equals(name) ) + return mod; + } + + // + // find a free model slot spot + // + for (i=0; i<mod_numknown ; i++) + { + mod = mod_known[i]; + + if (mod.name.length() == 0) + break; // free spot + } + if (i == mod_numknown) + { + if (mod_numknown == MAX_MOD_KNOWN) + Com.Error (Defines.ERR_DROP, "mod_numknown == MAX_MOD_KNOWN"); + mod_numknown++; + mod = mod_known[i]; + } + + mod.name = name; + + // + // load the file + // + fileBuffer = FS.LoadFile(name); + + if (fileBuffer == null) + { + if (crash) + Com.Error(Defines.ERR_DROP, "Mod_NumForName: " + mod.name + " not found"); + + mod.name = ""; + return null; + } + + modfilelen = fileBuffer.length; + + loadmodel = mod; + + // + // fill it in + // + ByteBuffer bb = ByteBuffer.wrap(fileBuffer); + bb.order(ByteOrder.LITTLE_ENDIAN); + + // call the apropriate loader + + bb.mark(); + int ident = bb.getInt(); + + bb.reset(); + + switch (ident) + { + case qfiles.IDALIASHEADER: + Mod_LoadAliasModel(mod, bb); + break; + case qfiles.IDSPRITEHEADER: + Mod_LoadSpriteModel(mod, bb); + break; + case qfiles.IDBSPHEADER: + Mod_LoadBrushModel(mod, bb); + break; + default: + Com.Error(Defines.ERR_DROP,"Mod_NumForName: unknown fileid for " + mod.name); + break; + } + + this.fileBuffer = null; // free it for garbage collection + return mod; + } + + /* + =============================================================================== + + BRUSHMODEL LOADING + + =============================================================================== + */ + + byte[] mod_base; + + + /* + ================= + Mod_LoadLighting + ================= + */ + void Mod_LoadLighting(lump_t l) + { + if (l.filelen == 0) + { + loadmodel.lightdata = null; + return; + } + // memcpy (loadmodel.lightdata, mod_base + l.fileofs, l.filelen); + loadmodel.lightdata = new byte[l.filelen]; + System.arraycopy(mod_base, l.fileofs, loadmodel.lightdata, 0, l.filelen); + } + + + /* + ================= + Mod_LoadVisibility + ================= + */ + void Mod_LoadVisibility(lump_t l) + { + int i; + + if (l.filelen == 0) + { + loadmodel.vis = null; + return; + } + + System.arraycopy(mod_base, l.fileofs, model_visibility, 0, l.filelen); + + ByteBuffer bb = ByteBuffer.wrap(model_visibility, 0, l.filelen); + + loadmodel.vis = new qfiles.dvis_t(bb.order(ByteOrder.LITTLE_ENDIAN)); + + /* done: + memcpy (loadmodel.vis, mod_base + l.fileofs, l.filelen); + + loadmodel.vis.numclusters = LittleLong (loadmodel.vis.numclusters); + for (i=0 ; i<loadmodel.vis.numclusters ; i++) + { + loadmodel.vis.bitofs[i][0] = LittleLong (loadmodel.vis.bitofs[i][0]); + loadmodel.vis.bitofs[i][1] = LittleLong (loadmodel.vis.bitofs[i][1]); + } + */ + } + + + /* + ================= + Mod_LoadVertexes + ================= + */ + void Mod_LoadVertexes(lump_t l) + { + mvertex_t[] vertexes; + int i, count; + + if ( (l.filelen % mvertex_t.DISK_SIZE) != 0) + Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name); + + count = l.filelen / mvertex_t.DISK_SIZE; + + vertexes = new mvertex_t[count]; + + loadmodel.vertexes = vertexes; + loadmodel.numvertexes = count; + + ByteBuffer bb = ByteBuffer.wrap(mod_base, l.fileofs, l.filelen); + bb.order(ByteOrder.LITTLE_ENDIAN); + + for ( i=0 ; i<count ; i++) + { + vertexes[i] = new mvertex_t(bb); + } + } + + /* + ================= + RadiusFromBounds + ================= + */ + float RadiusFromBounds(float[] mins, float[] maxs) + { + float[] corner = {0, 0, 0}; + + for (int i=0 ; i<3 ; i++) + { + corner[i] = Math.abs(mins[i]) > Math.abs(maxs[i]) ? Math.abs(mins[i]) : Math.abs(maxs[i]); + } + return Math3D.VectorLength(corner); + } + + + /* + ================= + Mod_LoadSubmodels + ================= + */ + void Mod_LoadSubmodels(lump_t l) { + + if ((l.filelen % qfiles.dmodel_t.SIZE) != 0) + Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + + loadmodel.name); + + int i, j; + + int count = l.filelen / qfiles.dmodel_t.SIZE; + // out = Hunk_Alloc ( count*sizeof(*out)); + mmodel_t out; + mmodel_t[] outs = new mmodel_t[count]; + for (i = 0; i < count; i++) { + outs[i] = new mmodel_t(); + } + + loadmodel.submodels = outs; + loadmodel.numsubmodels = count; + + ByteBuffer bb = ByteBuffer.wrap(mod_base, l.fileofs, l.filelen); + bb.order(ByteOrder.LITTLE_ENDIAN); + + qfiles.dmodel_t in; + + for (i = 0; i < count; i++) { + in = new qfiles.dmodel_t(bb); + out = outs[i]; + for (j = 0; j < 3; j++) { // spread the mins / maxs by a + // pixel + out.mins[j] = in.mins[j] - 1; + out.maxs[j] = in.maxs[j] + 1; + out.origin[j] = in.origin[j]; + } + out.radius = RadiusFromBounds(out.mins, out.maxs); + out.headnode = in.headnode; + out.firstface = in.firstface; + out.numfaces = in.numfaces; + } + } + + /* + ================= + Mod_LoadEdges + ================= + */ + void Mod_LoadEdges (lump_t l) + { + medge_t[] edges; + int i, count; + + if ( (l.filelen % medge_t.DISK_SIZE) != 0) + Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name); + + count = l.filelen / medge_t.DISK_SIZE; + // out = Hunk_Alloc ( (count + 1) * sizeof(*out)); + edges = new medge_t[count + 1]; + + loadmodel.edges = edges; + loadmodel.numedges = count; + + ByteBuffer bb = ByteBuffer.wrap(mod_base, l.fileofs, l.filelen); + bb.order(ByteOrder.LITTLE_ENDIAN); + + for ( i=0 ; i<count ; i++) + { + edges[i] = new medge_t(bb); + } + } + + /* + ================= + Mod_LoadTexinfo + ================= + */ + void Mod_LoadTexinfo(lump_t l) + { + texinfo_t in; + mtexinfo_t[] out; + mtexinfo_t step; + int i, j, count; + int next; + String name; + + if ((l.filelen % texinfo_t.SIZE) != 0) + Com.Error (Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name); + + count = l.filelen / texinfo_t.SIZE; + // out = Hunk_Alloc ( count*sizeof(*out)); + out = new mtexinfo_t[count]; + for ( i=0 ; i<count ; i++) { + out[i] = new mtexinfo_t(); + } + + loadmodel.texinfo = out; + loadmodel.numtexinfo = count; + + ByteBuffer bb = ByteBuffer.wrap(mod_base, l.fileofs, l.filelen); + bb.order(ByteOrder.LITTLE_ENDIAN); + + for ( i=0 ; i<count ; i++) { + + in = new texinfo_t(bb); + out[i].vecs = in.vecs; + out[i].flags = in.flags; + next = in.nexttexinfo; + if (next > 0) + out[i].next = loadmodel.texinfo[next]; + else + out[i].next = null; + + name = "textures/" + in.texture + ".wal"; + + out[i].image = GL_FindImage(name, it_wall); + if (out[i].image == null) { + VID.Printf(Defines.PRINT_ALL, "Couldn't load " + name + '\n'); + out[i].image = r_notexture; + } + } + + // count animation frames + for (i=0 ; i<count ; i++) { + out[i].numframes = 1; + for (step = out[i].next ; (step != null) && (step != out[i]) ; step=step.next) + out[i].numframes++; + } + } + + /* + ================ + CalcSurfaceExtents + + Fills in s.texturemins[] and s.extents[] + ================ + */ + void CalcSurfaceExtents(msurface_t s) + { + float[] mins = {0, 0}; + float[] maxs = {0, 0}; + float val; + + int j, e; + mvertex_t v; + int[] bmins = {0, 0}; + int[] bmaxs = {0, 0}; + + mins[0] = mins[1] = 999999; + maxs[0] = maxs[1] = -99999; + + mtexinfo_t tex = s.texinfo; + + for (int i=0 ; i<s.numedges ; i++) + { + e = loadmodel.surfedges[s.firstedge+i]; + if (e >= 0) + v = loadmodel.vertexes[loadmodel.edges[e].v[0]]; + else + v = loadmodel.vertexes[loadmodel.edges[-e].v[1]]; + + for (j=0 ; j<2 ; j++) + { + val = v.position[0] * tex.vecs[j][0] + + v.position[1] * tex.vecs[j][1] + + v.position[2] * tex.vecs[j][2] + + tex.vecs[j][3]; + if (val < mins[j]) + mins[j] = val; + if (val > maxs[j]) + maxs[j] = val; + } + } + + for (int i=0 ; i<2 ; i++) + { + bmins[i] = (int)Math.floor(mins[i]/16); + bmaxs[i] = (int)Math.ceil(maxs[i]/16); + + s.texturemins[i] = (short)(bmins[i] * 16); + s.extents[i] = (short)((bmaxs[i] - bmins[i]) * 16); + + } + } + + /* + ================= + Mod_LoadFaces + ================= + */ + void Mod_LoadFaces(lump_t l) { + + int i, surfnum; + int planenum, side; + int ti; + + if ((l.filelen % qfiles.dface_t.SIZE) != 0) + Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + + loadmodel.name); + + int count = l.filelen / qfiles.dface_t.SIZE; + // out = Hunk_Alloc ( count*sizeof(*out)); + msurface_t[] outs = new msurface_t[count]; + for (i = 0; i < count; i++) { + outs[i] = new msurface_t(); + } + + loadmodel.surfaces = outs; + loadmodel.numsurfaces = count; + + ByteBuffer bb = ByteBuffer.wrap(mod_base, l.fileofs, l.filelen); + bb.order(ByteOrder.LITTLE_ENDIAN); + + currentmodel = loadmodel; + + GL_BeginBuildingLightmaps(loadmodel); + + qfiles.dface_t in; + msurface_t out; + + for (surfnum = 0; surfnum < count; surfnum++) { + in = new qfiles.dface_t(bb); + out = outs[surfnum]; + out.firstedge = in.firstedge; + out.numedges = in.numedges; + out.flags = 0; + out.polys = null; + + planenum = in.planenum; + side = in.side; + if (side != 0) + out.flags |= Defines.SURF_PLANEBACK; + + out.plane = loadmodel.planes[planenum]; + + ti = in.texinfo; + if (ti < 0 || ti >= loadmodel.numtexinfo) + Com.Error(Defines.ERR_DROP, + "MOD_LoadBmodel: bad texinfo number"); + + out.texinfo = loadmodel.texinfo[ti]; + + CalcSurfaceExtents(out); + + // lighting info + + for (i = 0; i < Defines.MAXLIGHTMAPS; i++) + out.styles[i] = in.styles[i]; + + i = in.lightofs; + if (i == -1) + out.samples = null; + else { + ByteBuffer pointer = ByteBuffer.wrap(loadmodel.lightdata); + pointer.position(i); + pointer = pointer.slice(); + pointer.mark(); + out.samples = pointer; // subarray + } + + // set the drawing flags + + if ((out.texinfo.flags & Defines.SURF_WARP) != 0) { + out.flags |= Defines.SURF_DRAWTURB; + for (i = 0; i < 2; i++) { + out.extents[i] = 16384; + out.texturemins[i] = -8192; + } + GL_SubdivideSurface(out); // cut up polygon for warps + } + + // create lightmaps and polygons + if ((out.texinfo.flags & (Defines.SURF_SKY | Defines.SURF_TRANS33 + | Defines.SURF_TRANS66 | Defines.SURF_WARP)) == 0) + GL_CreateSurfaceLightmap(out); + + if ((out.texinfo.flags & Defines.SURF_WARP) == 0) + GL_BuildPolygonFromSurface(out); + + } + GL_EndBuildingLightmaps(); + } + + + /* + ================= + Mod_SetParent + ================= + */ + void Mod_SetParent(mnode_t node, mnode_t parent) + { + node.parent = parent; + if (node.contents != -1) return; + Mod_SetParent(node.children[0], node); + Mod_SetParent(node.children[1], node); + } + + /* + ================= + Mod_LoadNodes + ================= + */ + void Mod_LoadNodes(lump_t l) + { + int i, j, count, p; + qfiles.dnode_t in; + mnode_t[] out; + + if ((l.filelen % qfiles.dnode_t.SIZE) != 0) + Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name); + + count = l.filelen / qfiles.dnode_t.SIZE; + // out = Hunk_Alloc ( count*sizeof(*out)); + out = new mnode_t[count]; + + loadmodel.nodes = out; + loadmodel.numnodes = count; + + ByteBuffer bb = ByteBuffer.wrap(mod_base, l.fileofs, l.filelen); + bb.order(ByteOrder.LITTLE_ENDIAN); + + // initialize the tree array + for ( i=0 ; i<count ; i++) out[i] = new mnode_t(); // do first before linking + + // fill and link the nodes + for ( i=0 ; i<count ; i++) + { + in = new qfiles.dnode_t(bb); + for (j=0 ; j<3 ; j++) + { + out[i].mins[j] = in.mins[j]; + out[i].maxs[j] = in.maxs[j]; + } + + p = in.planenum; + out[i].plane = loadmodel.planes[p]; + + out[i].firstsurface = in.firstface; + out[i].numsurfaces = in.numfaces; + out[i].contents = -1; // differentiate from leafs + + for (j=0 ; j<2 ; j++) + { + p = in.children[j]; + if (p >= 0) + out[i].children[j] = loadmodel.nodes[p]; + else + out[i].children[j] = loadmodel.leafs[-1 - p]; // mleaf_t extends mnode_t + } + } + + Mod_SetParent(loadmodel.nodes[0], null); // sets nodes and leafs + } + + /* + ================= + Mod_LoadLeafs + ================= + */ + void Mod_LoadLeafs(lump_t l) + { + qfiles.dleaf_t in; + mleaf_t[] out; + int i, j, count, p; + + if ((l.filelen % qfiles.dleaf_t.SIZE) != 0) + Com.Error (Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name); + + count = l.filelen / qfiles.dleaf_t.SIZE; + // out = Hunk_Alloc ( count*sizeof(*out)); + out = new mleaf_t[count]; + + loadmodel.leafs = out; + loadmodel.numleafs = count; + + ByteBuffer bb = ByteBuffer.wrap(mod_base, l.fileofs, l.filelen); + bb.order(ByteOrder.LITTLE_ENDIAN); + + for ( i=0 ; i<count ; i++) + { + in = new qfiles.dleaf_t(bb); + out[i] = new mleaf_t(); + for (j=0 ; j<3 ; j++) + { + out[i].mins[j] = in.mins[j]; + out[i].maxs[j] = in.maxs[j]; + + } + + out[i].contents = in.contents; + out[i].cluster = in.cluster; + out[i].area = in.area; + + out[i].setMarkSurface(in.firstleafface, loadmodel.marksurfaces); + out[i].nummarksurfaces = in.numleaffaces; + } + } + + + /* + ================= + Mod_LoadMarksurfaces + ================= + */ + void Mod_LoadMarksurfaces(lump_t l) + { + int i, j, count; + + msurface_t[] out; + + if ((l.filelen % Defines.SIZE_OF_SHORT) != 0) + Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name); + count = l.filelen / Defines.SIZE_OF_SHORT; + // out = Hunk_Alloc ( count*sizeof(*out)); + out = new msurface_t[count]; + + loadmodel.marksurfaces = out; + loadmodel.nummarksurfaces = count; + + ByteBuffer bb = ByteBuffer.wrap(mod_base, l.fileofs, l.filelen); + bb.order(ByteOrder.LITTLE_ENDIAN); + + for ( i=0 ; i<count ; i++) + { + j = bb.getShort(); + if (j < 0 || j >= loadmodel.numsurfaces) + Com.Error(Defines.ERR_DROP, "Mod_ParseMarksurfaces: bad surface number"); + + out[i] = loadmodel.surfaces[j]; + } + } + + + /* + ================= + Mod_LoadSurfedges + ================= + */ + void Mod_LoadSurfedges(lump_t l) + { + int i, count; + int[] offsets; + + if ( (l.filelen % Defines.SIZE_OF_INT) != 0) + Com.Error (Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name); + + count = l.filelen / Defines.SIZE_OF_INT; + if (count < 1 || count >= Defines.MAX_MAP_SURFEDGES) + Com.Error (Defines.ERR_DROP, "MOD_LoadBmodel: bad surfedges count in " + loadmodel.name + ": " + count); + + offsets = new int[count]; + + loadmodel.surfedges = offsets; + loadmodel.numsurfedges = count; + + ByteBuffer bb = ByteBuffer.wrap(mod_base, l.fileofs, l.filelen); + bb.order(ByteOrder.LITTLE_ENDIAN); + + for ( i=0 ; i<count ; i++) offsets[i] = bb.getInt(); + } + + + /* + ================= + Mod_LoadPlanes + ================= + */ + void Mod_LoadPlanes(lump_t l) + { + int i, j; + cplane_t[] out; + qfiles.dplane_t in; + int count; + int bits; + + if ((l.filelen % qfiles.dplane_t.SIZE) != 0) + Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name); + + count = l.filelen / qfiles.dplane_t.SIZE; + // out = Hunk_Alloc ( count*2*sizeof(*out)); + out = new cplane_t[count * 2]; + for (i = 0; i < count; i++) { + out[i] = new cplane_t(); + } + + loadmodel.planes = out; + loadmodel.numplanes = count; + + ByteBuffer bb = ByteBuffer.wrap(mod_base, l.fileofs, l.filelen); + bb.order(ByteOrder.LITTLE_ENDIAN); + + for ( i=0 ; i<count ; i++) + { + bits = 0; + in = new qfiles.dplane_t(bb); + for (j=0 ; j<3 ; j++) + { + out[i].normal[j] = in.normal[j]; + if (out[i].normal[j] < 0) + bits |= (1<<j); + } + + out[i].dist = in.dist; + out[i].type = (byte)in.type; + out[i].signbits = (byte)bits; + } + } + + /* + ================= + Mod_LoadBrushModel + ================= + */ + void Mod_LoadBrushModel(model_t mod, ByteBuffer buffer) + { + int i; + qfiles.dheader_t header; + mmodel_t bm; + + loadmodel.type = mod_brush; + if (loadmodel != mod_known[0]) + Com.Error(Defines.ERR_DROP, "Loaded a brush model after the world"); + + header = new qfiles.dheader_t(buffer); + + i = header.version; + if (i != Defines.BSPVERSION) + Com.Error (Defines.ERR_DROP, "Mod_LoadBrushModel: " + mod.name + " has wrong version number (" + i + " should be " + Defines.BSPVERSION + ")"); + + mod_base = fileBuffer; //(byte *)header; + + // load into heap + Mod_LoadVertexes(header.lumps[Defines.LUMP_VERTEXES]); // ok + Mod_LoadEdges(header.lumps[Defines.LUMP_EDGES]); // ok + Mod_LoadSurfedges(header.lumps[Defines.LUMP_SURFEDGES]); // ok + Mod_LoadLighting(header.lumps[Defines.LUMP_LIGHTING]); // ok + Mod_LoadPlanes(header.lumps[Defines.LUMP_PLANES]); // ok + Mod_LoadTexinfo(header.lumps[Defines.LUMP_TEXINFO]); // ok + Mod_LoadFaces(header.lumps[Defines.LUMP_FACES]); // ok + Mod_LoadMarksurfaces(header.lumps[Defines.LUMP_LEAFFACES]); + Mod_LoadVisibility(header.lumps[Defines.LUMP_VISIBILITY]); // ok + Mod_LoadLeafs(header.lumps[Defines.LUMP_LEAFS]); // ok + Mod_LoadNodes(header.lumps[Defines.LUMP_NODES]); // ok + Mod_LoadSubmodels(header.lumps[Defines.LUMP_MODELS]); + mod.numframes = 2; // regular and alternate animation + + // + // set up the submodels + // + model_t starmod; + + for (i=0 ; i<mod.numsubmodels ; i++) + { + + bm = mod.submodels[i]; + starmod = mod_inline[i] = loadmodel.copy(); + + starmod.firstmodelsurface = bm.firstface; + starmod.nummodelsurfaces = bm.numfaces; + starmod.firstnode = bm.headnode; + if (starmod.firstnode >= loadmodel.numnodes) + Com.Error(Defines.ERR_DROP, "Inline model " + i + " has bad firstnode"); + + Math3D.VectorCopy(bm.maxs, starmod.maxs); + Math3D.VectorCopy(bm.mins, starmod.mins); + starmod.radius = bm.radius; + + if (i == 0) + loadmodel = starmod.copy(); + + starmod.numleafs = bm.visleafs; + } + } + + /* + ============================================================================== + + ALIAS MODELS + + ============================================================================== + */ + + /* + ================= + Mod_LoadAliasModel + ================= + */ + void Mod_LoadAliasModel (model_t mod, ByteBuffer buffer) + { + int i, j; + qfiles.dmdl_t pheader; + qfiles.dstvert_t[] poutst; + qfiles.dtriangle_t[] pouttri; + qfiles.daliasframe_t[] poutframe; + int[] poutcmd; + + pheader = new qfiles.dmdl_t(buffer); + + if (pheader.version != qfiles.ALIAS_VERSION) + Com.Error(Defines.ERR_DROP, "%s has wrong version number (%i should be %i)", + new Vargs(3).add(mod.name).add(pheader.version).add(qfiles.ALIAS_VERSION)); + + if (pheader.skinheight > MAX_LBM_HEIGHT) + Com.Error(Defines.ERR_DROP, "model "+ mod.name +" has a skin taller than " + MAX_LBM_HEIGHT); + + if (pheader.num_xyz <= 0) + Com.Error(Defines.ERR_DROP, "model " + mod.name + " has no vertices"); + + if (pheader.num_xyz > qfiles.MAX_VERTS) + Com.Error(Defines.ERR_DROP, "model " + mod.name +" has too many vertices"); + + if (pheader.num_st <= 0) + Com.Error(Defines.ERR_DROP, "model " + mod.name + " has no st vertices"); + + if (pheader.num_tris <= 0) + Com.Error(Defines.ERR_DROP, "model " + mod.name + " has no triangles"); + + if (pheader.num_frames <= 0) + Com.Error(Defines.ERR_DROP, "model " + mod.name + " has no frames"); + + // + // load base s and t vertices (not used in gl version) + // + poutst = new qfiles.dstvert_t[pheader.num_st]; + buffer.position(pheader.ofs_st); + for (i=0 ; i<pheader.num_st ; i++) + { + poutst[i] = new qfiles.dstvert_t(buffer); + } + + // + // load triangle lists + // + pouttri = new qfiles.dtriangle_t[pheader.num_tris]; + buffer.position(pheader.ofs_tris); + for (i=0 ; i<pheader.num_tris ; i++) + { + pouttri[i] = new qfiles.dtriangle_t(buffer); + } + + // + // load the frames + // + poutframe = new qfiles.daliasframe_t[pheader.num_frames]; + buffer.position(pheader.ofs_frames); + for (i=0 ; i<pheader.num_frames ; i++) + { + poutframe[i] = new qfiles.daliasframe_t(buffer); + // verts are all 8 bit, so no swapping needed + poutframe[i].verts = new int[pheader.num_xyz]; + for (int k=0; k < pheader.num_xyz; k++) { + poutframe[i].verts[k] = buffer.getInt(); + } + } + + mod.type = mod_alias; + + // + // load the glcmds + // + poutcmd = new int[pheader.num_glcmds]; + buffer.position(pheader.ofs_glcmds); + for (i=0 ; i<pheader.num_glcmds ; i++) + poutcmd[i] = buffer.getInt(); // LittleLong (pincmd[i]); + + // register all skins + String[] skinNames = new String[pheader.num_skins]; + byte[] nameBuf = new byte[qfiles.MAX_SKINNAME]; + buffer.position(pheader.ofs_skins); + for (i=0 ; i<pheader.num_skins ; i++) + { + buffer.get(nameBuf); + skinNames[i] = new String(nameBuf); + int n = skinNames[i].indexOf('\0'); + if (n > -1) { + skinNames[i] = skinNames[i].substring(0, n); + } + mod.skins[i] = GL_FindImage(skinNames[i], it_skin); + } + + // set the model arrays + pheader.skinNames = skinNames; // skin names + pheader.stVerts = poutst; // textur koordinaten + pheader.triAngles = pouttri; // dreiecke + pheader.glCmds = poutcmd; // STRIP or FAN + pheader.aliasFrames = poutframe; // frames mit vertex array + + mod.extradata = pheader; + + mod.mins[0] = -32; + mod.mins[1] = -32; + mod.mins[2] = -32; + mod.maxs[0] = 32; + mod.maxs[1] = 32; + mod.maxs[2] = 32; + + precompileGLCmds(pheader); + } + + /* + ============================================================================== + + SPRITE MODELS + + ============================================================================== + */ + + /* + ================= + Mod_LoadSpriteModel + ================= + */ + void Mod_LoadSpriteModel(model_t mod, ByteBuffer buffer) + { + qfiles.dsprite_t sprout = new qfiles.dsprite_t(buffer); + + if (sprout.version != qfiles.SPRITE_VERSION) + Com.Error(Defines.ERR_DROP, "%s has wrong version number (%i should be %i)", + new Vargs(3).add(mod.name).add(sprout.version).add(qfiles.SPRITE_VERSION)); + + if (sprout.numframes > qfiles.MAX_MD2SKINS) + Com.Error(Defines.ERR_DROP, "%s has too many frames (%i > %i)", + new Vargs(3).add(mod.name).add(sprout.numframes).add(qfiles.MAX_MD2SKINS)); + + for (int i=0 ; i<sprout.numframes ; i++) + { + mod.skins[i] = GL_FindImage(sprout.frames[i].name, it_sprite); + } + + mod.type = mod_sprite; + mod.extradata = sprout; + } + +// ============================================================================= + + /* + @@@@@@@@@@@@@@@@@@@@@ + R_BeginRegistration + + Specifies the model that will be used as the world + @@@@@@@@@@@@@@@@@@@@@ + */ + public void R_BeginRegistration(String model) + { + resetModelArrays(); + Polygon.reset(); + + cvar_t flushmap; + + registration_sequence++; + r_oldviewcluster = -1; // force markleafs + + String fullname = "maps/" + model + ".bsp"; + + // explicitly free the old map if different + // this guarantees that mod_known[0] is the world map + flushmap = Cvar.Get("flushmap", "0", 0); + if ( !mod_known[0].name.equals(fullname) || flushmap.value != 0.0f) + Mod_Free(mod_known[0]); + r_worldmodel = Mod_ForName(fullname, true); + + r_viewcluster = -1; + } + + + /* + @@@@@@@@@@@@@@@@@@@@@ + R_RegisterModel + + @@@@@@@@@@@@@@@@@@@@@ + */ + public model_t R_RegisterModel(String name) + { + model_t mod = null; + int i; + qfiles.dsprite_t sprout; + qfiles.dmdl_t pheader; + + mod = Mod_ForName(name, false); + if (mod != null) + { + mod.registration_sequence = registration_sequence; + + // register any images used by the models + if (mod.type == mod_sprite) + { + sprout = (qfiles.dsprite_t)mod.extradata; + for (i=0 ; i<sprout.numframes ; i++) + mod.skins[i] = GL_FindImage(sprout.frames[i].name, it_sprite); + } + else if (mod.type == mod_alias) + { + pheader = (qfiles.dmdl_t)mod.extradata; + for (i=0 ; i<pheader.num_skins ; i++) + mod.skins[i] = GL_FindImage(pheader.skinNames[i], it_skin); + // PGM + mod.numframes = pheader.num_frames; + // PGM + } + else if (mod.type == mod_brush) + { + for (i=0 ; i<mod.numtexinfo ; i++) + mod.texinfo[i].image.registration_sequence = registration_sequence; + } + } + return mod; + } + + + /* + @@@@@@@@@@@@@@@@@@@@@ + R_EndRegistration + + @@@@@@@@@@@@@@@@@@@@@ + */ + public void R_EndRegistration() + { + model_t mod; + + for (int i=0; i<mod_numknown ; i++) + { + mod = mod_known[i]; + if (mod.name.length() == 0) + continue; + if (mod.registration_sequence != registration_sequence) + { // don't need this model + Mod_Free(mod); + } else { + // precompile AliasModels + if (mod.type == mod_alias) + precompileGLCmds((qfiles.dmdl_t)mod.extradata); + } + } + GL_FreeUnusedImages(); + //modelMemoryUsage(); + } + + +// ============================================================================= + + + /* + ================ + Mod_Free + ================ + */ + void Mod_Free (model_t mod) + { + mod.clear(); + } + + /* + ================ + Mod_FreeAll + ================ + */ + void Mod_FreeAll() + { + for (int i=0 ; i<mod_numknown ; i++) + { + if (mod_known[i].extradata != null) + Mod_Free(mod_known[i]); + } + } + + /* + * new functions for vertex array handling + */ + static final int MODEL_BUFFER_SIZE = 50000; + static FloatBuffer globalModelTextureCoordBuf = Lib.newFloatBuffer(MODEL_BUFFER_SIZE * 2); + static IntBuffer globalModelVertexIndexBuf = Lib.newIntBuffer(MODEL_BUFFER_SIZE); + + void precompileGLCmds(qfiles.dmdl_t model) { + model.textureCoordBuf = globalModelTextureCoordBuf.slice(); + model.vertexIndexBuf = globalModelVertexIndexBuf.slice(); + Vector tmp = new Vector(); + + int count = 0; + int[] order = model.glCmds; + int orderIndex = 0; + while (true) + { + // get the vertex count and primitive type + count = order[orderIndex++]; + if (count == 0) + break; // done + + tmp.addElement(new Integer(count)); + + if (count < 0) + { + count = -count; + //gl.glBegin (GL.GL_TRIANGLE_FAN); + } + else + { + //gl.glBegin (GL.GL_TRIANGLE_STRIP); + } + + do { + // texture coordinates come from the draw list + globalModelTextureCoordBuf.put(Float.intBitsToFloat(order[orderIndex + 0])); + globalModelTextureCoordBuf.put(Float.intBitsToFloat(order[orderIndex + 1])); + globalModelVertexIndexBuf.put(order[orderIndex + 2]); + + orderIndex += 3; + } while (--count != 0); + } + + int size = tmp.size(); + + model.counts = new int[size]; + model.indexElements = new IntBuffer[size]; + + count = 0; + int pos = 0; + for (int i = 0; i < model.counts.length; i++) { + count = ((Integer)tmp.get(i)).intValue(); + model.counts[i] = count; + + count = (count < 0) ? -count : count; + model.vertexIndexBuf.position(pos); + model.indexElements[i] = model.vertexIndexBuf.slice(); + model.indexElements[i].limit(count); + pos += count; + } + } + + static void resetModelArrays() { + globalModelTextureCoordBuf.rewind(); + globalModelVertexIndexBuf.rewind(); + } + + static void modelMemoryUsage() { + System.out.println("AliasModels: globalVertexBuffer size " + globalModelVertexIndexBuf.position()); + } +} diff --git a/src/jake2/render/fast/Polygon.java b/src/jake2/render/fast/Polygon.java new file mode 100644 index 0000000..13019fc --- /dev/null +++ b/src/jake2/render/fast/Polygon.java @@ -0,0 +1,164 @@ +/* + * Polygon.java + * Copyright (C) 2003 + * + * $Id: Polygon.java,v 1.2 2006-11-21 00:50:46 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 jake2.render.fast; + +import jake2.render.glpoly_t; +import jake2.util.Lib; + +import java.nio.FloatBuffer; + +/** + * Polygon + * + * @author cwei + */ +public final class Polygon extends glpoly_t { + + private final static int MAX_POLYS = 20000; + + private final static int MAX_BUFFER_VERTICES = 120000; + + // backup for s1 scrolling + private static float[] s1_old = new float[MAX_VERTICES]; + + private static FloatBuffer buffer = Lib.newFloatBuffer(MAX_BUFFER_VERTICES + * STRIDE); + + private static int bufferIndex = 0; + + private static int polyCount = 0; + + private static Polygon[] polyCache = new Polygon[MAX_POLYS]; + static { + for (int i = 0; i < polyCache.length; i++) { + polyCache[i] = new Polygon(); + } + } + + static glpoly_t create(int numverts) { + Polygon poly = polyCache[polyCount++]; + poly.clear(); + poly.numverts = numverts; + poly.pos = bufferIndex; + bufferIndex += numverts; + return poly; + } + + static void reset() { + polyCount = 0; + bufferIndex = 0; + } + + static FloatBuffer getInterleavedBuffer() { + return (FloatBuffer) buffer.rewind(); + } + + private Polygon() { + } + + private final void clear() { + next = null; + chain = null; + numverts = 0; + flags = 0; + } + + // the interleaved buffer has the format: + // textureCoord0 (index 0, 1) + // vertex (index 2, 3, 4) + // textureCoord1 (index 5, 6) + + public final float s1(int index) { + return buffer.get((index + pos) * STRIDE); + } + + public final void s1(int index, float value) { + buffer.put((index + pos) * STRIDE, value); + } + + public final float t1(int index) { + return buffer.get((index + pos) * STRIDE + 1); + } + + public final void t1(int index, float value) { + buffer.put((index + pos) * STRIDE + 1, value); + } + + public final float x(int index) { + return buffer.get((index + pos) * STRIDE + 2); + } + + public final void x(int index, float value) { + buffer.put((index + pos) * STRIDE + 2, value); + } + + public final float y(int index) { + return buffer.get((index + pos) * STRIDE + 3); + } + + public final void y(int index, float value) { + buffer.put((index + pos) * STRIDE + 3, value); + } + + public final float z(int index) { + return buffer.get((index + pos) * STRIDE + 4); + } + + public final void z(int index, float value) { + buffer.put((index + pos) * STRIDE + 4, value); + } + + public final float s2(int index) { + return buffer.get((index + pos) * STRIDE + 5); + } + + public final void s2(int index, float value) { + buffer.put((index + pos) * STRIDE + 5, value); + } + + public final float t2(int index) { + return buffer.get((index + pos) * STRIDE + 6); + } + + public final void t2(int index, float value) { + buffer.put((index + pos) * STRIDE + 6, value); + } + + public final void beginScrolling(float scroll) { + int index = pos * STRIDE; + for (int i = 0; i < numverts; i++, index += STRIDE) { + scroll += s1_old[i] = buffer.get(index); + buffer.put(index, scroll); + } + } + + public final void endScrolling() { + int index = pos * STRIDE; + for (int i = 0; i < numverts; i++, index += STRIDE) { + buffer.put(index, s1_old[i]); + } + } +}
\ No newline at end of file diff --git a/src/jake2/render/fast/Surf.java b/src/jake2/render/fast/Surf.java new file mode 100644 index 0000000..c9312ff --- /dev/null +++ b/src/jake2/render/fast/Surf.java @@ -0,0 +1,1333 @@ +/* + * Surf.java + * Copyright (C) 2003 + * + * $Id: Surf.java,v 1.2 2006-11-21 00:50:46 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 jake2.render.fast; + +import jake2.Defines; +import jake2.client.*; +import jake2.game.cplane_t; +import jake2.qcommon.Com; +import jake2.render.*; +import jake2.util.Lib; +import jake2.util.Math3D; + +import java.nio.*; +import java.util.Arrays; + +/** + * 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_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]; + IntBuffer lightmap_buffer = Lib.newIntBuffer(BLOCK_WIDTH * BLOCK_HEIGHT, ByteOrder.LITTLE_ENDIAN); + + public gllightmapstate_t() { + for (int i = 0; i < MAX_LIGHTMAPS; i++) + lightmap_surfaces[i] = new msurface_t(); + } + + public void clearLightmapSurfaces() { + for (int i = 0; i < MAX_LIGHTMAPS; i++) + // TODO lightmap_surfaces[i].clear(); + lightmap_surfaces[i] = new msurface_t(); + } + + } + + gllightmapstate_t gl_lms = new gllightmapstate_t(); + + // 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, IntBuffer 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) + { + if (tex.next == null) + return tex.image; + + int c = currententity.frame % tex.numframes; + while (c != 0) + { + tex = tex.next; + c--; + } + + return tex.image; + } + + /** + * DrawGLPoly + */ + void DrawGLPoly(glpoly_t p) + { + gl.glDrawArrays(GL_POLYGON, p.pos, p.numverts); + } + + /** + * DrawGLFlowingPoly + * version that handles scrolling texture + */ + void DrawGLFlowingPoly(glpoly_t p) + { + float scroll = -64 * ( (r_newrefdef.time / 40.0f) - (int)(r_newrefdef.time / 40.0f) ); + if(scroll == 0.0f) + scroll = -64.0f; + p.beginScrolling(scroll); + gl.glDrawArrays(GL_POLYGON, p.pos, p.numverts); + p.endScrolling(); + } + + /** + * R_DrawTriangleOutlines + */ + void R_DrawTriangleOutlines() + { + if (gl_showtris.value == 0) + return; + + gl.glDisable(GL_TEXTURE_2D); + gl.glDisable(GL_DEPTH_TEST); + gl.glColor4f(1, 1, 1, 1); + + msurface_t surf; + glpoly_t 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++) { + gl.glBegin(GL_LINE_STRIP); + gl.glVertex3f(p.x(0), p.y(0), p.z(0)); + gl.glVertex3f(p.x(j-1), p.y(j-1), p.z(j-1)); + gl.glVertex3f(p.x(j), p.y(j), p.z(j)); + gl.glVertex3f(p.x(0), p.y(0), p.z(0)); + gl.glEnd(); + } + } + } + } + + gl.glEnable(GL_DEPTH_TEST); + gl.glEnable(GL_TEXTURE_2D); + } + + private final IntBuffer temp2 = Lib.newIntBuffer(34 * 34, ByteOrder.LITTLE_ENDIAN); + + /** + * R_RenderBrushPoly + */ + void R_RenderBrushPoly(msurface_t fa) + { + c_brush_polys++; + + image_t image = R_TextureAnimation(fa.texinfo); + + if ((fa.flags & Defines.SURF_DRAWTURB) != 0) + { + GL_Bind( image.texnum ); + + // warp texture, no lightmaps + GL_TexEnv( GL_MODULATE ); + gl.glColor4f( gl_state.inverse_intensity, + gl_state.inverse_intensity, + gl_state.inverse_intensity, + 1.0F ); + EmitWaterPolys (fa); + GL_TexEnv( GL_REPLACE ); + + return; + } + else + { + GL_Bind( image.texnum ); + GL_TexEnv( GL_REPLACE ); + } + + // ====== + // PGM + if((fa.texinfo.flags & Defines.SURF_FLOWING) != 0) + DrawGLFlowingPoly(fa.polys); + else + DrawGLPoly (fa.polys); + // PGM + // ====== + + // ersetzt goto + boolean gotoDynamic = false; + /* + ** check for lightmap modification + */ + int maps; + for ( maps = 0; maps < Defines.MAXLIGHTMAPS && fa.styles[maps] != (byte)255; maps++ ) + { + if ( r_newrefdef.lightstyles[fa.styles[maps] & 0xFF].white != fa.cached_light[maps] ) { + gotoDynamic = true; + break; + } + } + + // this is a hack from cwei + if (maps == 4) maps--; + + // dynamic this frame or dynamic previously + boolean is_dynamic = false; + if ( gotoDynamic || ( fa.dlightframe == r_framecount ) ) + { + // label dynamic: + if ( gl_dynamic.value != 0 ) + { + if (( fa.texinfo.flags & (Defines.SURF_SKY | Defines.SURF_TRANS33 | Defines.SURF_TRANS66 | Defines.SURF_WARP ) ) == 0) + { + is_dynamic = true; + } + } + } + + if ( is_dynamic ) + { + if ( ( (fa.styles[maps] & 0xFF) >= 32 || fa.styles[maps] == 0 ) && ( fa.dlightframe != r_framecount ) ) + { + // ist ersetzt durch temp2: unsigned temp[34*34]; + int smax, tmax; + + smax = (fa.extents[0]>>4)+1; + tmax = (fa.extents[1]>>4)+1; + + R_BuildLightMap( fa, temp2, smax); + R_SetCacheState( fa ); + + GL_Bind( gl_state.lightmap_textures + fa.lightmaptexturenum ); + + gl.glTexSubImage2D( GL_TEXTURE_2D, 0, + fa.light_s, fa.light_t, + smax, tmax, + GL_LIGHTMAP_FORMAT, + GL_UNSIGNED_BYTE, temp2 ); + + 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() + { + r_world_matrix.clear(); + // + // go back to the world matrix + // + gl.glLoadMatrix(r_world_matrix); + + gl.glEnable (GL_BLEND); + GL_TexEnv(GL_MODULATE ); + + + // the textures are prescaled up for a better lighting range, + // so scale it back down + float intens = gl_state.inverse_intensity; + + gl.glInterleavedArrays(GL_T2F_V3F, glpoly_t.BYTE_STRIDE, globalPolygonInterleavedBuf); + + for (msurface_t 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.polys); // PGM + else + DrawGLPoly(s.polys); + } + + GL_TexEnv( GL_REPLACE ); + gl.glColor4f (1,1,1,1); + gl.glDisable (GL_BLEND); + + r_alpha_surfaces = null; + } + + /** + * DrawTextureChains + */ + void DrawTextureChains() + { + c_visible_textures = 0; + + msurface_t s; + image_t image; + int i; + 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_REPLACE ); + } + + // direct buffer + private final IntBuffer temp = Lib.newIntBuffer(128 * 128, ByteOrder.LITTLE_ENDIAN); + + /** + * GL_RenderLightmappedPoly + * @param surf + */ + void GL_RenderLightmappedPoly( msurface_t surf ) + { + + // ersetzt goto + boolean gotoDynamic = false; + int map; + 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] ) { + gotoDynamic = true; + break; + } + } + + // this is a hack from cwei + if (map == 4) map--; + + // dynamic this frame or dynamic previously + boolean is_dynamic = false; + if ( gotoDynamic || ( surf.dlightframe == r_framecount ) ) + { + // label dynamic: + if ( gl_dynamic.value != 0 ) + { + if ( (surf.texinfo.flags & (Defines.SURF_SKY | Defines.SURF_TRANS33 | Defines.SURF_TRANS66 | Defines.SURF_WARP )) == 0 ) + { + is_dynamic = true; + } + } + } + + glpoly_t p; + FloatBuffer texCoord = globalPolygonInterleavedBuf; + image_t image = R_TextureAnimation( surf.texinfo ); + int lmtex = surf.lightmaptexturenum; + + if ( is_dynamic ) + { + // ist raus gezogen worden int[] temp = new int[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, temp, smax); + R_SetCacheState( surf ); + + GL_MBind(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, temp, smax); + + GL_MBind(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(TEXTURE0, image.texnum ); + GL_MBind(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 ) + { + p.beginScrolling(scroll); + gl.glDrawArrays(GL_POLYGON, p.pos, p.numverts); + p.endScrolling(); + } + } + else + { + for ( p = surf.polys; p != null; p = p.chain ) + { + gl.glDrawArrays(GL_POLYGON, p.pos, p.numverts); + } + } + // PGM + // ========== + } + else + { + c_brush_polys++; + + GL_MBind(TEXTURE0, image.texnum ); + GL_MBind(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 ) + { + p.beginScrolling(scroll); + gl.glDrawArrays(GL_POLYGON, p.pos, p.numverts); + p.endScrolling(); + } + } + else + { + // PGM + // ========== + for ( p = surf.polys; p != null; p = p.chain ) + { + gl.glDrawArrays(GL_POLYGON, p.pos, p.numverts); + } + + // ========== + // PGM + } + // PGM + // ========== + } + } + + /** + * R_DrawInlineBModel + */ + void R_DrawInlineBModel() + { + // calculate dynamic lighting for bmodel + if ( gl_flashblend.value == 0 ) + { + dlight_t lt; + for (int 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 = currentmodel.surfaces; + //psurf = surfaces[psurfp]; + + if ( (currententity.flags & Defines.RF_TRANSLUCENT) != 0 ) + { + gl.glEnable (GL_BLEND); + gl.glColor4f (1,1,1,0.25f); + GL_TexEnv( GL_MODULATE ); + } + + // + // draw texture + // + msurface_t psurf; + cplane_t pplane; + float dot; + for (int 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 ( (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 ) { + gl.glDisable (GL_BLEND); + gl.glColor4f (1,1,1,1); + GL_TexEnv( GL_REPLACE ); + } + } + + // stack variable + private final float[] mins = {0, 0, 0}; + private final float[] maxs = {0, 0, 0}; + private final float[] org = {0, 0, 0}; + private final float[] forward = {0, 0, 0}; + private final float[] right = {0, 0, 0}; + private final float[] up = {0, 0, 0}; + /** + * R_DrawBrushModel + */ + void R_DrawBrushModel(entity_t e) + { + if (currentmodel.nummodelsurfaces == 0) + return; + + currententity = e; + gl_state.currenttextures[0] = 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] - currentmodel.radius; + maxs[i] = e.origin[i] + currentmodel.radius; + } + } + else + { + rotated = false; + Math3D.VectorAdd(e.origin, currentmodel.mins, mins); + Math3D.VectorAdd(e.origin, currentmodel.maxs, maxs); + } + + if (R_CullBox(mins, maxs)) return; + + 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 (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); + } + + 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(TEXTURE0); + GL_TexEnv( GL_REPLACE ); + gl.glInterleavedArrays(GL_T2F_V3F, glpoly_t.BYTE_STRIDE, globalPolygonInterleavedBuf); + GL_SelectTexture(TEXTURE1); + GL_TexEnv( GL_MODULATE ); + gl.glTexCoordPointer(2, glpoly_t.BYTE_STRIDE, globalPolygonTexCoord1Buf); + gl.glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + R_DrawInlineBModel(); + + gl.glClientActiveTextureARB(TEXTURE1); + gl.glDisableClientState(GL_TEXTURE_COORD_ARRAY); + + GL_EnableMultitexture( false ); + + gl.glPopMatrix(); + } + + /* + ============================================================= + + WORLD MODEL + + ============================================================= + */ + + /** + * R_RecursiveWorldNode + */ + void R_RecursiveWorldNode (mnode_t node) + { + if (node.contents == Defines.CONTENTS_SOLID) + return; // solid + + if (node.visframe != r_visframecount) + return; + + if (R_CullBox(node.mins, node.maxs)) + return; + + int c; + msurface_t mark; + // if a leaf node, draw stuff + if (node.contents != -1) + { + mleaf_t 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 + cplane_t plane = node.plane; + float dot; + 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; + } + + int side, sidebit; + 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 + msurface_t surf; + image_t image; + //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 ( ( 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]); + } + + private final entity_t worldEntity = new entity_t(); + + /** + * R_DrawWorld + */ + void R_DrawWorld() + { + if (r_drawworld.value == 0) + return; + + if ( (r_newrefdef.rdflags & Defines.RDF_NOWORLDMODEL) != 0 ) + return; + + currentmodel = r_worldmodel; + + Math3D.VectorCopy(r_newrefdef.vieworg, modelorg); + + entity_t ent = worldEntity; + // auto cycle the world frame for texture animation + ent.clear(); + 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)); + // TODO wird bei multitexture nicht gebraucht + //gl_lms.clearLightmapSurfaces(); + + R_ClearSkyBox(); + + GL_EnableMultitexture( true ); + + GL_SelectTexture(TEXTURE0); + GL_TexEnv( GL_REPLACE ); + gl.glInterleavedArrays(GL_T2F_V3F, glpoly_t.BYTE_STRIDE, globalPolygonInterleavedBuf); + GL_SelectTexture(TEXTURE1); + gl.glTexCoordPointer(2, glpoly_t.BYTE_STRIDE, globalPolygonTexCoord1Buf); + gl.glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + if ( gl_lightmap.value != 0) + GL_TexEnv( GL_REPLACE ); + else + GL_TexEnv( GL_MODULATE ); + + R_RecursiveWorldNode(r_worldmodel.nodes[0]); // root node + + gl.glClientActiveTextureARB(TEXTURE1); + gl.glDisableClientState(GL_TEXTURE_COORD_ARRAY); + + GL_EnableMultitexture( false ); + + DrawTextureChains(); + R_DrawSkyBox(); + R_DrawTriangleOutlines(); + } + + final 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() + { + 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; + + int i; + 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; + } + + byte[] vis = Mod_ClusterPVS(r_viewcluster, r_worldmodel); + int c; + // 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) >> 3); + vis = Mod_ClusterPVS(r_viewcluster2, r_worldmodel); + c = (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; + } + + mnode_t node; + mleaf_t leaf; + int cluster; + 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 + + ============================================================================= + */ + + /** + * LM_InitBlock + */ + void LM_InitBlock() + { + Arrays.fill(gl_lms.allocated, 0); + } + + /** + * LM_UploadBlock + * @param dynamic + */ + void LM_UploadBlock( boolean dynamic ) + { + int texture = ( dynamic ) ? 0 : gl_lms.current_lightmap_texture; + + GL_Bind( gl_state.lightmap_textures + texture ); + gl.glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl.glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, 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]; + } + + gl.glTexSubImage2D( GL_TEXTURE_2D, + 0, + 0, 0, + BLOCK_WIDTH, height, + GL_LIGHTMAP_FORMAT, + GL_UNSIGNED_BYTE, + gl_lms.lightmap_buffer ); + } + else + { + gl.glTexImage2D( GL_TEXTURE_2D, + 0, + gl_lms.internal_format, + BLOCK_WIDTH, BLOCK_HEIGHT, + 0, + GL_LIGHTMAP_FORMAT, + GL_UNSIGNED_BYTE, + gl_lms.lightmap_buffer ); + if ( ++gl_lms.current_lightmap_texture == MAX_LIGHTMAPS ) + Com.Error( Defines.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 + */ + boolean LM_AllocBlock (int w, int h, pos_t pos) + { + int best = BLOCK_HEIGHT; + int x = pos.x; + int y = pos.y; + + 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 = 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) + { + // reconstruct the polygon + medge_t[] pedges = currentmodel.edges; + int lnumverts = fa.numedges; + int vertpage = 0; + // + // draw texture + // + // poly = Hunk_Alloc (sizeof(glpoly_t) + (lnumverts-4) * VERTEXSIZE*sizeof(float)); + glpoly_t poly = Polygon.create(lnumverts); + + poly.next = fa.polys; + poly.flags = fa.flags; + fa.polys = poly; + + int lindex; + float[] vec; + medge_t r_pedge; + float s, t; + for (int 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; + + poly.x(i, vec[0]); + poly.y(i, vec[1]); + poly.z(i, vec[2]); + + poly.s1(i, s); + poly.t1(i, 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.s2(i, s); + poly.t2(i, t); + } + } + + /** + * GL_CreateSurfaceLightmap + */ + void GL_CreateSurfaceLightmap(msurface_t surf) + { + if ( (surf.flags & (Defines.SURF_DRAWSKY | Defines.SURF_DRAWTURB)) != 0) + return; + + int smax = (surf.extents[0]>>4)+1; + int 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 ) ) + { + Com.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; + + IntBuffer base = gl_lms.lightmap_buffer; + base.position(surf.light_t * BLOCK_WIDTH + surf.light_s); + + R_SetCacheState( surf ); + R_BuildLightMap(surf, base.slice(), BLOCK_WIDTH); + } + + lightstyle_t[] lightstyles; + private final IntBuffer dummy = Lib.newIntBuffer(128*128); + + /** + * GL_BeginBuildingLightmaps + */ + void GL_BeginBuildingLightmaps(model_t m) + { + // static lightstyle_t lightstyles[MAX_LIGHTSTYLES]; + int i; + + // 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(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_INTENSITY8; + } + else if ( format == 'L' ) + { + gl_lms.internal_format = 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_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl.glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gl.glTexImage2D( GL_TEXTURE_2D, + 0, + gl_lms.internal_format, + BLOCK_WIDTH, BLOCK_HEIGHT, + 0, + GL_LIGHTMAP_FORMAT, + GL_UNSIGNED_BYTE, + dummy ); + } + + /** + * GL_EndBuildingLightmaps + */ + void GL_EndBuildingLightmaps() + { + LM_UploadBlock( false ); + GL_EnableMultitexture( false ); + } + + /* + * new buffers for vertex array handling + */ + static FloatBuffer globalPolygonInterleavedBuf = Polygon.getInterleavedBuffer(); + static FloatBuffer globalPolygonTexCoord1Buf = null; + + static { + globalPolygonInterleavedBuf.position(glpoly_t.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); +// +// } + +} diff --git a/src/jake2/render/fast/Warp.java b/src/jake2/render/fast/Warp.java new file mode 100644 index 0000000..bb0cc81 --- /dev/null +++ b/src/jake2/render/fast/Warp.java @@ -0,0 +1,716 @@ +/* + * Warp.java + * Copyright (C) 2003 + * + * $Id: Warp.java,v 1.2 2006-11-21 00:50:46 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 jake2.render.fast; + +import jake2.Defines; +import jake2.Globals; +import jake2.qcommon.Com; +import jake2.render.*; +import jake2.util.Math3D; +import jake2.util.Vec3Cache; + +/** + * Warp + * + * @author cwei + */ +public abstract class Warp extends Model { + // warpsin.h + public static final float[] SIN = { + 0f, 0.19633f, 0.392541f, 0.588517f, 0.784137f, 0.979285f, 1.17384f, 1.3677f, + 1.56072f, 1.75281f, 1.94384f, 2.1337f, 2.32228f, 2.50945f, 2.69512f, 2.87916f, + 3.06147f, 3.24193f, 3.42044f, 3.59689f, 3.77117f, 3.94319f, 4.11282f, 4.27998f, + 4.44456f, 4.60647f, 4.76559f, 4.92185f, 5.07515f, 5.22538f, 5.37247f, 5.51632f, + 5.65685f, 5.79398f, 5.92761f, 6.05767f, 6.18408f, 6.30677f, 6.42566f, 6.54068f, + 6.65176f, 6.75883f, 6.86183f, 6.9607f, 7.05537f, 7.14579f, 7.23191f, 7.31368f, + 7.39104f, 7.46394f, 7.53235f, 7.59623f, 7.65552f, 7.71021f, 7.76025f, 7.80562f, + 7.84628f, 7.88222f, 7.91341f, 7.93984f, 7.96148f, 7.97832f, 7.99036f, 7.99759f, + 8f, 7.99759f, 7.99036f, 7.97832f, 7.96148f, 7.93984f, 7.91341f, 7.88222f, + 7.84628f, 7.80562f, 7.76025f, 7.71021f, 7.65552f, 7.59623f, 7.53235f, 7.46394f, + 7.39104f, 7.31368f, 7.23191f, 7.14579f, 7.05537f, 6.9607f, 6.86183f, 6.75883f, + 6.65176f, 6.54068f, 6.42566f, 6.30677f, 6.18408f, 6.05767f, 5.92761f, 5.79398f, + 5.65685f, 5.51632f, 5.37247f, 5.22538f, 5.07515f, 4.92185f, 4.76559f, 4.60647f, + 4.44456f, 4.27998f, 4.11282f, 3.94319f, 3.77117f, 3.59689f, 3.42044f, 3.24193f, + 3.06147f, 2.87916f, 2.69512f, 2.50945f, 2.32228f, 2.1337f, 1.94384f, 1.75281f, + 1.56072f, 1.3677f, 1.17384f, 0.979285f, 0.784137f, 0.588517f, 0.392541f, 0.19633f, + 9.79717e-16f, -0.19633f, -0.392541f, -0.588517f, -0.784137f, -0.979285f, -1.17384f, -1.3677f, + -1.56072f, -1.75281f, -1.94384f, -2.1337f, -2.32228f, -2.50945f, -2.69512f, -2.87916f, + -3.06147f, -3.24193f, -3.42044f, -3.59689f, -3.77117f, -3.94319f, -4.11282f, -4.27998f, + -4.44456f, -4.60647f, -4.76559f, -4.92185f, -5.07515f, -5.22538f, -5.37247f, -5.51632f, + -5.65685f, -5.79398f, -5.92761f, -6.05767f, -6.18408f, -6.30677f, -6.42566f, -6.54068f, + -6.65176f, -6.75883f, -6.86183f, -6.9607f, -7.05537f, -7.14579f, -7.23191f, -7.31368f, + -7.39104f, -7.46394f, -7.53235f, -7.59623f, -7.65552f, -7.71021f, -7.76025f, -7.80562f, + -7.84628f, -7.88222f, -7.91341f, -7.93984f, -7.96148f, -7.97832f, -7.99036f, -7.99759f, + -8f, -7.99759f, -7.99036f, -7.97832f, -7.96148f, -7.93984f, -7.91341f, -7.88222f, + -7.84628f, -7.80562f, -7.76025f, -7.71021f, -7.65552f, -7.59623f, -7.53235f, -7.46394f, + -7.39104f, -7.31368f, -7.23191f, -7.14579f, -7.05537f, -6.9607f, -6.86183f, -6.75883f, + -6.65176f, -6.54068f, -6.42566f, -6.30677f, -6.18408f, -6.05767f, -5.92761f, -5.79398f, + -5.65685f, -5.51632f, -5.37247f, -5.22538f, -5.07515f, -4.92185f, -4.76559f, -4.60647f, + -4.44456f, -4.27998f, -4.11282f, -3.94319f, -3.77117f, -3.59689f, -3.42044f, -3.24193f, + -3.06147f, -2.87916f, -2.69512f, -2.50945f, -2.32228f, -2.1337f, -1.94384f, -1.75281f, + -1.56072f, -1.3677f, -1.17384f, -0.979285f, -0.784137f, -0.588517f, -0.392541f, -0.19633f + }; + + String skyname; + float skyrotate; + float[] skyaxis = {0, 0, 0}; + image_t[] sky_images = new image_t[6]; + + msurface_t warpface; + + static final int SUBDIVIDE_SIZE = 64; + + /** + * BoundPoly + * @param numverts + * @param verts + * @param mins + * @param maxs + */ + 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]; + } + } + } + + /** + * SubdividePolygon + * @param numverts + * @param verts + */ + void SubdividePolygon(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(Defines.ERR_DROP, "numverts = " + numverts); + + float[] mins = Vec3Cache.get(); + float[] maxs = Vec3Cache.get(); + + 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 = SUBDIVIDE_SIZE * (float)Math.floor(m / 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(f, front); + SubdividePolygon(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 + glpoly_t poly = Polygon.create(numverts + 2); + + poly.next = warpface.polys; + warpface.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.x(i + 1, verts[i][0]); + poly.y(i + 1, verts[i][1]); + poly.z(i + 1, verts[i][2]); + s = Math3D.DotProduct(verts[i], warpface.texinfo.vecs[0]); + t = Math3D.DotProduct(verts[i], warpface.texinfo.vecs[1]); + + total_s += s; + total_t += t; + Math3D.VectorAdd(total, verts[i], total); + + poly.s1(i + 1, s); + poly.t1(i + 1, t); + } + + float scale = 1.0f / numverts; + poly.x(0, total[0] * scale); + poly.y(0, total[1] * scale); + poly.z(0, total[2] * scale); + poly.s1(0, total_s * scale); + poly.t1(0, total_t * scale); + + poly.x(i + 1, poly.x(1)); + poly.y(i + 1, poly.y(1)); + poly.z(i + 1, poly.z(1)); + poly.s1(i + 1, poly.s1(1)); + poly.t1(i + 1, poly.t1(1)); + poly.s2(i + 1, poly.s2(1)); + poly.t2(i + 1, poly.t2(1)); + + Vec3Cache.release(); // total + } + + private final float[][] tmpVerts = new float[64][3]; + /** + * GL_SubdivideSurface + * Breaks a polygon up along axial 64 unit + * boundaries so that turbulent and sky warps + * can be done reasonably. + */ + void GL_SubdivideSurface(msurface_t fa) { + float[][] verts = tmpVerts; + float[] vec; + warpface = fa; + // + // convert edges back to a normal polygon + // + int numverts = 0; + for (int i = 0; i < fa.numedges; i++) { + int lindex = loadmodel.surfedges[fa.firstedge + i]; + + if (lindex > 0) + vec = loadmodel.vertexes[loadmodel.edges[lindex].v[0]].position; + else + vec = loadmodel.vertexes[loadmodel.edges[-lindex].v[1]].position; + Math3D.VectorCopy(vec, verts[numverts]); + numverts++; + } + SubdividePolygon(numverts, verts); + } + +// ========================================================= + static final float TURBSCALE = (float)(256.0f / (2 * Math.PI)); + + /** + * EmitWaterPolys + * Does a water warp on the pre-fragmented glpoly_t chain + */ + void EmitWaterPolys(msurface_t fa) + { + float rdt = r_newrefdef.time; + + float scroll; + if ((fa.texinfo.flags & Defines.SURF_FLOWING) != 0) + scroll = -64 * ( (r_newrefdef.time*0.5f) - (int)(r_newrefdef.time*0.5f) ); + else + scroll = 0; + + int i; + float s, t, os, ot; + glpoly_t p, bp; + for (bp = fa.polys; bp != null; bp = bp.next) { + p = bp; + + gl.glBegin(GL_TRIANGLE_FAN); + for (i = 0; i < p.numverts; i++) { + os = p.s1(i); + ot = p.t1(i); + + s = os + + Warp.SIN[(int) ((ot * 0.125f + r_newrefdef.time) * TURBSCALE) & 255]; + s += scroll; + s *= (1.0f / 64); + + t = ot + + Warp.SIN[(int) ((os * 0.125f + rdt) * TURBSCALE) & 255]; + t *= (1.0f / 64); + + gl.glTexCoord2f(s, t); + gl.glVertex3f(p.x(i), p.y(i), p.z(i)); + } + gl.glEnd(); + } + } + +// =================================================================== + + float[][] skyclip = { + { 1, 1, 0}, + { 1, -1, 0}, + { 0, -1, 1}, + { 0, 1, 1}, + { 1, 0, 1}, + {-1, 0, 1} + }; + + int c_sky; + + // 1 = s, 2 = t, 3 = 2048 + 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 + + }; + + int[][] vec_to_st = + { + {-2,3,1}, + {2,3,-1}, + + {1,3,2}, + {-1,3,-2}, + + {-2,-1,3}, + {-2,1,-3} + + }; + + float[][] skymins = new float[2][6]; + float[][] skymaxs = new float[2][6]; + float sky_min, sky_max; + + // stack variable + private final float[] v = {0, 0, 0}; + private final float[] av = {0, 0, 0}; + /** + * DrawSkyPolygon + * @param nump + * @param vecs + */ + 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 float ON_EPSILON = 0.1f; // point on plane side epsilon + static final int MAX_CLIP_VERTS = 64; + + static final int SIDE_BACK = 1; + static final int SIDE_FRONT = 0; + static final int SIDE_ON = 2; + + float[] dists = new float[MAX_CLIP_VERTS]; + int[] sides = new int[MAX_CLIP_VERTS]; + float[][][][] newv = new float[6][2][MAX_CLIP_VERTS][3]; + + /** + * ClipSkyPolygon + * @param nump + * @param vecs + * @param stage + */ + void ClipSkyPolygon(int nump, float[][] vecs, int stage) + { + if (nump > MAX_CLIP_VERTS-2) + Com.Error(Defines.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 > ON_EPSILON) + { + front = true; + sides[i] = SIDE_FRONT; + } + else if (d < -ON_EPSILON) + { + back = true; + sides[i] = SIDE_BACK; + } + else + sides[i] = SIDE_ON; + dists[i] = d; + } + + if (!front || !back) + { // not clipped + ClipSkyPolygon (nump, vecs, stage+1); + return; + } + + // clip it + sides[i] = sides[0]; + dists[i] = 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 = dists[i] / (dists[i] - 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); + } + + float[][] verts = new float[MAX_CLIP_VERTS][3]; + + /** + * R_AddSkySurface + */ + void R_AddSkySurface(msurface_t fa) + { + // calculate vertex values for sky box + for (glpoly_t p = fa.polys; p != null; p = p.next) { + for (int i = 0; i < p.numverts; i++) { + verts[i][0] = p.x(i) - r_origin[0]; + verts[i][1] = p.y(i) - r_origin[1]; + verts[i][2] = p.z(i) - r_origin[2]; + } + ClipSkyPolygon(p.numverts, verts, 0); + } + } + + /** + * R_ClearSkyBox + */ + 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 + private final float[] v1 = {0, 0, 0}; + private final float[] b = {0, 0, 0}; + /** + * MakeSkyVec + * @param s + * @param t + * @param axis + */ + 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; + gl.glTexCoord2f (s, t); + gl.glVertex3f(v1[0], v1[1], v1[2]); + } + + int[] skytexorder = {0,2,1,3,4,5}; + + /** + * R_DrawSkyBox + */ + void R_DrawSkyBox() + { + int i; + + if (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 + } + + gl.glPushMatrix (); + gl.glTranslatef (r_origin[0], r_origin[1], r_origin[2]); + gl.glRotatef (r_newrefdef.time * skyrotate, skyaxis[0], skyaxis[1], skyaxis[2]); + + for (i=0 ; i<6 ; i++) + { + if (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; + + GL_Bind(sky_images[skytexorder[i]].texnum); + + gl.glBegin(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); + gl.glEnd (); + } + gl.glPopMatrix (); + } + + // 3dstudio environment map names + String[] suf = {"rt", "bk", "lf", "ft", "up", "dn"}; + + /** + * R_SetSky + * @param name + * @param rotate + * @param axis + */ + public void R_SetSky(String name, float rotate, float[] axis) + { + assert (axis.length == 3) : "vec3_t bug"; + String pathname; + skyname = name; + + skyrotate = rotate; + Math3D.VectorCopy(axis, skyaxis); + + for (int i=0 ; i<6 ; i++) + { + // chop down rotating skies for less memory + if (gl_skymip.value != 0 || skyrotate != 0) + gl_picmip.value++; + + if ( qglColorTableEXT && gl_ext_palettedtexture.value != 0) { + // Com_sprintf (pathname, sizeof(pathname), "env/%s%s.pcx", skyname, suf[i]); + pathname = "env/" + skyname + suf[i] + ".pcx"; + } else { + // Com_sprintf (pathname, sizeof(pathname), "env/%s%s.tga", skyname, suf[i]); + pathname = "env/" + skyname + suf[i] + ".tga"; + } + + sky_images[i] = GL_FindImage(pathname, it_sky); + + if (sky_images[i] == null) + sky_images[i] = r_notexture; + + if (gl_skymip.value != 0 || skyrotate != 0) + { // take less memory + gl_picmip.value--; + sky_min = 1.0f / 256; + sky_max = 255.0f / 256; + } + else + { + sky_min = 1.0f / 512; + sky_max = 511.0f / 512; + } + } + } +}
\ No newline at end of file |