/*
Copyright (C) 1997-2001 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/* Modifications
Copyright 2003-2004 Bytonic Software
Copyright 2010 Google Inc.
*/
package com.googlecode.gwtquake.shared.render;
import static com.googlecode.gwtquake.shared.common.Constants.CVAR_ARCHIVE;
import static com.googlecode.gwtquake.shared.common.Constants.CVAR_USERINFO;
import com.googlecode.gwtquake.shared.client.Dimension;
import com.googlecode.gwtquake.shared.client.EntityType;
import com.googlecode.gwtquake.shared.client.RendererState;
import com.googlecode.gwtquake.shared.client.Window;
import com.googlecode.gwtquake.shared.common.Com;
import com.googlecode.gwtquake.shared.common.ConsoleVariables;
import com.googlecode.gwtquake.shared.common.Constants;
import com.googlecode.gwtquake.shared.common.ExecutableCommand;
import com.googlecode.gwtquake.shared.common.QuakeFiles;
import com.googlecode.gwtquake.shared.common.QuakeImage;
import com.googlecode.gwtquake.shared.game.Commands;
import com.googlecode.gwtquake.shared.game.ConsoleVariable;
import com.googlecode.gwtquake.shared.game.Plane;
import com.googlecode.gwtquake.shared.util.Math3D;
import com.googlecode.gwtquake.shared.util.Vargs;
/**
* Main
*
* @author cwei
*/
public class Entities {
/*
====================================================================
from gl_rmain.c
====================================================================
*/
// ============================================================================
// to port from gl_rmain.c, ...
// ============================================================================
/**
* R_CullBox
* Returns true if the box is completely outside the frustum
*/
static final boolean R_CullBox(float[] mins, float[] maxs) {
assert(mins.length == 3 && maxs.length == 3) : "vec3_t bug";
if (GlConfig.r_nocull.value != 0)
return false;
for (int i = 0; i < 4; i++) {
if (Math3D.BoxOnPlaneSide(mins, maxs, GlState.frustum[i]) == 2)
return true;
}
return false;
}
/**
* R_RotateForEntity
*/
static final void rotateForEntity(EntityType e) {
GlState.gl.glTranslatef(e.origin[0], e.origin[1], e.origin[2]);
GlState.gl.glRotatef(e.angles[1], 0, 0, 1);
GlState.gl.glRotatef(-e.angles[0], 0, 1, 0);
GlState.gl.glRotatef(-e.angles[2], 1, 0, 0);
}
/*
=============================================================
SPRITE MODELS
=============================================================
*/
/**
* R_DrawSpriteModel
*/
static void drawSpriteModel(EntityType e) {
float alpha = 1.0F;
QuakeFiles.dsprframe_t frame;
QuakeFiles.dsprite_t psprite;
// don't even bother culling, because it's just a single
// polygon without a surface cache
psprite = (QuakeFiles.dsprite_t) GlState.currentmodel.extradata;
e.frame %= psprite.numframes;
frame = psprite.frames[e.frame];
if ((e.flags & Constants.RF_TRANSLUCENT) != 0)
alpha = e.alpha;
if (alpha != 1.0F)
GlState.gl.glEnable(Gl1Context.GL_BLEND);
GlState.gl.glColor4f(1, 1, 1, alpha);
Images.GL_Bind(GlState.currentmodel.skins[e.frame].texnum);
Images.GL_TexEnv(Gl1Context.GL_MODULATE);
// if (alpha == 1.0)
// gl.glEnable(GLAdapter.GL_ALPHA_TEST);
// else
// gl.glDisable(GLAdapter.GL_ALPHA_TEST);
GlState.gl.glBegin(Gl1Context._GL_QUADS);
GlState.gl.glTexCoord2f(0, 1);
Math3D.VectorMA(e.origin, -frame.origin_y, GlState.vup, GlState.point);
Math3D.VectorMA(GlState.point, -frame.origin_x, GlState.vright, GlState.point);
GlState.gl.glVertex3f(GlState.point[0], GlState.point[1], GlState.point[2]);
GlState.gl.glTexCoord2f(0, 0);
Math3D.VectorMA(e.origin, frame.height - frame.origin_y, GlState.vup, GlState.point);
Math3D.VectorMA(GlState.point, -frame.origin_x, GlState.vright, GlState.point);
GlState.gl.glVertex3f(GlState.point[0], GlState.point[1], GlState.point[2]);
GlState.gl.glTexCoord2f(1, 0);
Math3D.VectorMA(e.origin, frame.height - frame.origin_y, GlState.vup, GlState.point);
Math3D.VectorMA(GlState.point, frame.width - frame.origin_x, GlState.vright, GlState.point);
GlState.gl.glVertex3f(GlState.point[0], GlState.point[1], GlState.point[2]);
GlState.gl.glTexCoord2f(1, 1);
Math3D.VectorMA(e.origin, -frame.origin_y, GlState.vup, GlState.point);
Math3D.VectorMA(GlState.point, frame.width - frame.origin_x, GlState.vright, GlState.point);
GlState.gl.glVertex3f(GlState.point[0], GlState.point[1], GlState.point[2]);
GlState.gl.glEnd();
// gl.glDisable(GLAdapter.GL_ALPHA_TEST);
Images.GL_TexEnv(Gl1Context.GL_REPLACE);
if (alpha != 1.0F)
GlState.gl.glDisable(Gl1Context.GL_BLEND);
GlState.gl.glColor4f(1, 1, 1, 1);
}
// ==================================================================================
/**
* R_DrawNullModel
*/
static void R_DrawNullModel() {
if ((GlState.currententity.flags & Constants.RF_FULLBRIGHT) != 0) {
// cwei wollte blau: shadelight[0] = shadelight[1] = shadelight[2] = 1.0F;
GlState.shadelight[0] = GlState.shadelight[1] = GlState.shadelight[2] = 0.0F;
GlState.shadelight[2] = 0.8F;
}
else {
DynamicLights.R_LightPoint(GlState.currententity.origin, GlState.shadelight);
}
GlState.gl.glPushMatrix();
rotateForEntity(GlState.currententity);
GlState.gl.glDisable(Gl1Context.GL_TEXTURE_2D);
GlState.gl.glColor3f(GlState.shadelight[0], GlState.shadelight[1], GlState.shadelight[2]);
// this replaces the TRIANGLE_FAN
//glut.glutWireCube(gl, 20);
GlState.gl.glBegin(Gl1Context.GL_TRIANGLE_FAN);
GlState.gl.glVertex3f(0, 0, -16);
int i;
for (i=0 ; i<=4 ; i++) {
GlState.gl.glVertex3f((float)(16.0f * Math.cos(i * Math.PI / 2)), (float)(16.0f * Math.sin(i * Math.PI / 2)), 0.0f);
}
GlState.gl.glEnd();
GlState.gl.glBegin(Gl1Context.GL_TRIANGLE_FAN);
GlState.gl.glVertex3f (0, 0, 16);
for (i=4 ; i>=0 ; i--) {
GlState.gl.glVertex3f((float)(16.0f * Math.cos(i * Math.PI / 2)), (float)(16.0f * Math.sin(i * Math.PI / 2)), 0.0f);
}
GlState.gl.glEnd();
GlState.gl.glColor3f(1, 1, 1);
GlState.gl.glPopMatrix();
GlState.gl.glEnable(Gl1Context.GL_TEXTURE_2D);
}
/**
* R_DrawEntitiesOnList
*/
static void R_DrawEntitiesOnList() {
if (GlConfig.r_drawentities.value == 0.0f)
return;
// draw non-transparent first
int i;
for (i = 0; i < GlState.r_newrefdef.num_entities; i++) {
GlState.currententity = GlState.r_newrefdef.entities[i];
if ((GlState.currententity.flags & Constants.RF_TRANSLUCENT) != 0)
continue; // solid
if ((GlState.currententity.flags & Constants.RF_BEAM) != 0) {
R_DrawBeam(GlState.currententity);
}
else {
GlState.currentmodel = GlState.currententity.model;
if (GlState.currentmodel == null) {
R_DrawNullModel();
continue;
}
switch (GlState.currentmodel.type) {
case GlConstants.mod_alias :
Mesh.R_DrawAliasModel(GlState.currententity);
break;
case GlConstants.mod_brush :
Surfaces.R_DrawBrushModel(GlState.currententity);
break;
case GlConstants.mod_sprite :
drawSpriteModel(GlState.currententity);
break;
default :
Com.Error(Constants.ERR_DROP, "Bad modeltype");
break;
}
}
}
// draw transparent entities
// we could sort these if it ever becomes a problem...
GlState.gl.glDepthMask(false); // no z writes
for (i = 0; i < GlState.r_newrefdef.num_entities; i++) {
GlState.currententity = GlState.r_newrefdef.entities[i];
if ((GlState.currententity.flags & Constants.RF_TRANSLUCENT) == 0)
continue; // solid
if ((GlState.currententity.flags & Constants.RF_BEAM) != 0) {
R_DrawBeam(GlState.currententity);
}
else {
GlState.currentmodel = GlState.currententity.model;
if (GlState.currentmodel == null) {
R_DrawNullModel();
continue;
}
switch (GlState.currentmodel.type) {
case GlConstants.mod_alias :
Mesh.R_DrawAliasModel(GlState.currententity);
break;
case GlConstants.mod_brush :
Surfaces.R_DrawBrushModel(GlState.currententity);
break;
case GlConstants.mod_sprite :
drawSpriteModel(GlState.currententity);
break;
default :
Com.Error(Constants.ERR_DROP, "Bad modeltype");
break;
}
}
}
GlState.gl.glDepthMask(true); // back to writing
}
/**
* R_PolyBlend
*/
static void R_PolyBlend() {
if (GlConfig.gl_polyblend.value == 0.0f)
return;
if (GlState.v_blend[3] == 0.0f)
return;
// gl.glDisable(GLAdapter.GL_ALPHA_TEST);
GlState.gl.glEnable(Gl1Context.GL_BLEND);
GlState.gl.glDisable(Gl1Context.GL_DEPTH_TEST);
GlState.gl.glDisable(Gl1Context.GL_TEXTURE_2D);
GlState.gl.glLoadIdentity();
// FIXME: get rid of these
GlState.gl.glRotatef(-90, 1, 0, 0); // put Z going up
GlState.gl.glRotatef(90, 0, 0, 1); // put Z going up
GlState.gl.glColor4f(GlState.v_blend[0], GlState.v_blend[1], GlState.v_blend[2], GlState.v_blend[3]);
GlState.gl.glBegin(Gl1Context._GL_QUADS);
GlState.gl.glVertex3f(10, 100, 100);
GlState.gl.glVertex3f(10, -100, 100);
GlState.gl.glVertex3f(10, -100, -100);
GlState.gl.glVertex3f(10, 100, -100);
GlState.gl.glEnd();
GlState.gl.glDisable(Gl1Context.GL_BLEND);
GlState.gl.glEnable(Gl1Context.GL_TEXTURE_2D);
// gl.glEnable(GLAdapter.GL_ALPHA_TEST);
GlState.gl.glColor4f(1, 1, 1, 1);
}
// =======================================================================
/**
* R_SetFrustum
*/
static void R_SetFrustum() {
// rotate VPN right by FOV_X/2 degrees
Math3D.RotatePointAroundVector(GlState.frustum[0].normal, GlState.vup, GlState.vpn, - (90f - GlState.r_newrefdef.fov_x / 2f));
// rotate VPN left by FOV_X/2 degrees
Math3D.RotatePointAroundVector(GlState.frustum[1].normal, GlState.vup, GlState.vpn, 90f - GlState.r_newrefdef.fov_x / 2f);
// rotate VPN up by FOV_X/2 degrees
Math3D.RotatePointAroundVector(GlState.frustum[2].normal, GlState.vright, GlState.vpn, 90f - GlState.r_newrefdef.fov_y / 2f);
// rotate VPN down by FOV_X/2 degrees
Math3D.RotatePointAroundVector(GlState.frustum[3].normal, GlState.vright, GlState.vpn, - (90f - GlState.r_newrefdef.fov_y / 2f));
for (int i = 0; i < 4; i++) {
GlState.frustum[i].type = Constants.PLANE_ANYZ;
GlState.frustum[i].dist = Math3D.DotProduct(GlState.r_origin, GlState.frustum[i].normal);
GlState.frustum[i].signbits = (byte) Plane.SignbitsForPlane(GlState.frustum[i]);
}
}
// =======================================================================
/**
* R_SetupFrame
*/
static void R_SetupFrame() {
GlState.r_framecount++;
// build the transformation matrix for the given view angles
Math3D.VectorCopy(GlState.r_newrefdef.vieworg, GlState.r_origin);
Math3D.AngleVectors(GlState.r_newrefdef.viewangles, GlState.vpn, GlState.vright, GlState.vup);
// current viewcluster
Leaf leaf;
if ((GlState.r_newrefdef.rdflags & Constants.RDF_NOWORLDMODEL) == 0) {
GlState.r_oldviewcluster = GlState.r_viewcluster;
GlState.r_oldviewcluster2 = GlState.r_viewcluster2;
leaf = Models.Mod_PointInLeaf(GlState.r_origin, GlState.r_worldmodel);
GlState.r_viewcluster = GlState.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(GlState.r_origin, GlState.temp);
GlState.temp[2] -= 16;
leaf = Models.Mod_PointInLeaf(GlState.temp, GlState.r_worldmodel);
if ((leaf.contents & Constants.CONTENTS_SOLID) == 0 && (leaf.cluster != GlState.r_viewcluster2))
GlState.r_viewcluster2 = leaf.cluster;
}
else { // look up a bit
Math3D.VectorCopy(GlState.r_origin, GlState.temp);
GlState.temp[2] += 16;
leaf = Models.Mod_PointInLeaf(GlState.temp, GlState.r_worldmodel);
if ((leaf.contents & Constants.CONTENTS_SOLID) == 0 && (leaf.cluster != GlState.r_viewcluster2))
GlState.r_viewcluster2 = leaf.cluster;
}
}
for (int i = 0; i < 4; i++)
GlState.v_blend[i] = GlState.r_newrefdef.blend[i];
GlState.c_brush_polys = 0;
GlState.c_alias_polys = 0;
// clear out the portion of the screen that the NOWORLDMODEL defines
if ((GlState.r_newrefdef.rdflags & Constants.RDF_NOWORLDMODEL) != 0) {
GlState.gl.glEnable(Gl1Context.GL_SCISSOR_TEST);
GlState.gl.glClearColor(0.3f, 0.3f, 0.3f, 1.0f);
GlState.gl.glScissor(
GlState.r_newrefdef.x,
GlState.vid.height - GlState.r_newrefdef.height - GlState.r_newrefdef.y,
GlState.r_newrefdef.width,
GlState.r_newrefdef.height);
GlState.gl.glClear(Gl1Context.GL_COLOR_BUFFER_BIT | Gl1Context.GL_DEPTH_BUFFER_BIT);
GlState.gl.glClearColor(1.0f, 0.0f, 0.5f, 0.5f);
GlState.gl.glDisable(Gl1Context.GL_SCISSOR_TEST);
}
}
/**
* MYgluPerspective
*
* @param fovy
* @param aspect
* @param zNear
* @param zFar
*/
static 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 * GlConfig.gl_state.camera_separation) / zNear;
xmax += - (2 * GlConfig.gl_state.camera_separation) / zNear;
GlState.gl.glFrustum(xmin, xmax, ymin, ymax, zNear, zFar);
}
/**
* R_SetupGL
*/
static void R_SetupGL() {
//
// set up viewport
//
//int x = (int) Math.floor(r_newrefdef.x * vid.width / vid.width);
int x = GlState.r_newrefdef.x;
//int x2 = (int) Math.ceil((r_newrefdef.x + r_newrefdef.width) * vid.width / vid.width);
int x2 = GlState.r_newrefdef.x + GlState.r_newrefdef.width;
//int y = (int) Math.floor(vid.height - r_newrefdef.y * vid.height / vid.height);
int y = GlState.vid.height - GlState.r_newrefdef.y;
//int y2 = (int) Math.ceil(vid.height - (r_newrefdef.y + r_newrefdef.height) * vid.height / vid.height);
int y2 = GlState.vid.height - (GlState.r_newrefdef.y + GlState.r_newrefdef.height);
int w = x2 - x;
int h = y - y2;
GlState.gl.glViewport(x, y2, w, h);
//
// set up projection matrix
//
float screenaspect = (float) GlState.r_newrefdef.width / GlState.r_newrefdef.height;
GlState.gl.glMatrixMode(Gl1Context.GL_PROJECTION);
GlState.gl.glLoadIdentity();
MYgluPerspective(GlState.r_newrefdef.fov_y, screenaspect, 4, 4096);
GlState.gl.glCullFace(Gl1Context.GL_FRONT);
GlState.gl.glMatrixMode(Gl1Context.GL_MODELVIEW);
GlState.gl.glLoadIdentity();
GlState.gl.glRotatef(-90, 1, 0, 0); // put Z going up
GlState.gl.glRotatef(90, 0, 0, 1); // put Z going up
GlState.gl.glRotatef(-GlState.r_newrefdef.viewangles[2], 1, 0, 0);
GlState.gl.glRotatef(-GlState.r_newrefdef.viewangles[0], 0, 1, 0);
GlState.gl.glRotatef(-GlState.r_newrefdef.viewangles[1], 0, 0, 1);
GlState.gl.glTranslatef(-GlState.r_newrefdef.vieworg[0], -GlState.r_newrefdef.vieworg[1], -GlState.r_newrefdef.vieworg[2]);
GlState.gl.glGetFloat(Gl1Context._GL_MODELVIEW_MATRIX, GlState.r_world_matrix);
GlState.r_world_matrix.clear();
//
// set drawing parms
//
if (GlConfig.gl_cull.value != 0.0f)
GlState.gl.glEnable(Gl1Context.GL_CULL_FACE);
else
GlState.gl.glDisable(Gl1Context.GL_CULL_FACE);
GlState.gl.glDisable(Gl1Context.GL_BLEND);
// gl.glDisable(GLAdapter.GL_ALPHA_TEST);
GlState.gl.glEnable(Gl1Context.GL_DEPTH_TEST);
}
/**
* R_Clear
*/
static void R_Clear() {
if (GlConfig.gl_ztrick.value != 0.0f) {
if (GlConfig.gl_clear.value != 0.0f) {
GlState.gl.glClear(Gl1Context.GL_COLOR_BUFFER_BIT);
}
GlState.trickframe++;
if ((GlState.trickframe & 1) != 0) {
GlState.gldepthmin = 0;
GlState.gldepthmax = 0.49999f;
GlState.gl.glDepthFunc(Gl1Context.GL_LEQUAL);
}
else {
GlState.gldepthmin = 1;
GlState.gldepthmax = 0.5f;
GlState.gl.glDepthFunc(Gl1Context.GL_GEQUAL);
}
}
else {
if (GlConfig.gl_clear.value != 0.0f)
GlState.gl.glClear(Gl1Context.GL_COLOR_BUFFER_BIT | Gl1Context.GL_DEPTH_BUFFER_BIT);
else
GlState.gl.glClear(Gl1Context.GL_DEPTH_BUFFER_BIT);
GlState.gldepthmin = 0;
GlState.gldepthmax = 1;
GlState.gl.glDepthFunc(Gl1Context.GL_LEQUAL);
}
GlState.gl.glDepthRange(GlState.gldepthmin, GlState.gldepthmax);
}
/**
* R_Flash
*/
static void R_Flash() {
R_PolyBlend();
}
/**
* R_RenderView
* r_newrefdef must be set before the first call
*/
static void R_RenderView(RendererState fd) {
if (GlConfig.r_norefresh.value != 0.0f)
return;
GlState.r_newrefdef = fd;
// included by cwei
if (GlState.r_newrefdef == null) {
Com.Error(Constants.ERR_DROP, "R_RenderView: refdef_t fd is null");
}
if (GlState.r_worldmodel == null && (GlState.r_newrefdef.rdflags & Constants.RDF_NOWORLDMODEL) == 0)
Com.Error(Constants.ERR_DROP, "R_RenderView: NULL worldmodel");
if (GlConfig.r_speeds.value != 0.0f) {
GlState.c_brush_polys = 0;
GlState.c_alias_polys = 0;
}
DynamicLights.push();
if (GlConfig.gl_finish.value != 0.0f)
GlState.gl.glFinish();
R_SetupFrame();
R_SetFrustum();
R_SetupGL();
Surfaces.R_MarkLeaves(); // done here so we know if we're in water
Surfaces.R_DrawWorld();
R_DrawEntitiesOnList();
DynamicLights.render();
com.googlecode.gwtquake.shared.render.Particles.draw();
Surfaces.R_DrawAlphaSurfaces();
R_Flash();
if (GlConfig.r_speeds.value != 0.0f) {
Window.Printf(
Constants.PRINT_ALL,
"%4i wpoly %4i epoly %i tex %i lmaps\n",
new Vargs(4).add(GlState.c_brush_polys).add(GlState.c_alias_polys).add(GlState.c_visible_textures).add(GlState.c_visible_lightmaps));
}
}
/**
* R_SetGL2D
*/
static void R_SetGL2D() {
// set 2D virtual screen size
GlState.gl.glViewport(0, 0, GlState.vid.width, GlState.vid.height);
GlState.gl.glMatrixMode(Gl1Context.GL_PROJECTION);
GlState.gl.glLoadIdentity();
GlState.gl.glOrtho(0, GlState.vid.width, GlState.vid.height, 0, -99999, 99999);
GlState.gl.glMatrixMode(Gl1Context.GL_MODELVIEW);
GlState.gl.glLoadIdentity();
GlState.gl.glDisable(Gl1Context.GL_DEPTH_TEST);
GlState.gl.glDisable(Gl1Context.GL_CULL_FACE);
GlState.gl.glDisable(Gl1Context.GL_BLEND);
// gl.glEnable(GLAdapter.GL_ALPHA_TEST);
GlState.gl.glColor4f(1, 1, 1, 1);
}
/**
* R_SetLightLevel
*/
static void R_SetLightLevel() {
if ((GlState.r_newrefdef.rdflags & Constants.RDF_NOWORLDMODEL) != 0)
return;
// save off light value for server to look at (BIG HACK!)
DynamicLights.R_LightPoint(GlState.r_newrefdef.vieworg, GlState.light);
// pick the greatest component, which should be the same
// as the mono value returned by software
if (GlState.light[0] > GlState.light[1]) {
if (GlState.light[0] > GlState.light[2])
GlConfig.r_lightlevel.value = 150 * GlState.light[0];
else
GlConfig.r_lightlevel.value = 150 * GlState.light[2];
}
else {
if (GlState.light[1] > GlState.light[2])
GlConfig.r_lightlevel.value = 150 * GlState.light[1];
else
GlConfig.r_lightlevel.value = 150 * GlState.light[2];
}
}
/**
* R_RenderFrame
*/
protected static void R_RenderFrame(RendererState fd) {
R_RenderView(fd);
R_SetLightLevel();
R_SetGL2D();
}
/**
* R_DrawBeam
*/
static void R_DrawBeam(EntityType e) {
GlState.oldorigin[0] = e.oldorigin[0];
GlState.oldorigin[1] = e.oldorigin[1];
GlState.oldorigin[2] = e.oldorigin[2];
GlState.origin[0] = e.origin[0];
GlState.origin[1] = e.origin[1];
GlState.origin[2] = e.origin[2];
GlState.normalized_direction[0] = GlState.direction[0] = GlState.oldorigin[0] - GlState.origin[0];
GlState.normalized_direction[1] = GlState.direction[1] = GlState.oldorigin[1] - GlState.origin[1];
GlState.normalized_direction[2] = GlState.direction[2] = GlState.oldorigin[2] - GlState.origin[2];
if (Math3D.VectorNormalize(GlState.normalized_direction) == 0.0f)
return;
Math3D.PerpendicularVector(GlState.perpvec, GlState.normalized_direction);
Math3D.VectorScale(GlState.perpvec, e.frame / 2, GlState.perpvec);
for (int i = 0; i < 6; i++) {
Math3D.RotatePointAroundVector(
GlState.start_points[i],
GlState.normalized_direction,
GlState.perpvec,
(360.0f / GlConstants.NUM_BEAM_SEGS) * i);
Math3D.VectorAdd(GlState.start_points[i], GlState.origin, GlState.start_points[i]);
Math3D.VectorAdd(GlState.start_points[i], GlState.direction, GlState.end_points[i]);
}
GlState.gl.glDisable(Gl1Context.GL_TEXTURE_2D);
GlState.gl.glEnable(Gl1Context.GL_BLEND);
GlState.gl.glDepthMask(false);
float r = (QuakeImage.PALETTE_ABGR[e.skinnum & 0xFF]) & 0xFF;
float g = (QuakeImage.PALETTE_ABGR[e.skinnum & 0xFF] >> 8) & 0xFF;
float b = (QuakeImage.PALETTE_ABGR[e.skinnum & 0xFF] >> 16) & 0xFF;
r *= 1 / 255.0f;
g *= 1 / 255.0f;
b *= 1 / 255.0f;
GlState.gl.glColor4f(r, g, b, e.alpha);
GlState.gl.glBegin(Gl1Context.GL_TRIANGLE_STRIP);
float[] v;
for (int i = 0; i < GlConstants.NUM_BEAM_SEGS; i++) {
v = GlState.start_points[i];
GlState.gl.glVertex3f(v[0], v[1], v[2]);
v = GlState.end_points[i];
GlState.gl.glVertex3f(v[0], v[1], v[2]);
v = GlState.start_points[(i + 1) % GlConstants.NUM_BEAM_SEGS];
GlState.gl.glVertex3f(v[0], v[1], v[2]);
v = GlState.end_points[(i + 1) % GlConstants.NUM_BEAM_SEGS];
GlState.gl.glVertex3f(v[0], v[1], v[2]);
}
GlState.gl.glEnd();
GlState.gl.glEnable(Gl1Context.GL_TEXTURE_2D);
GlState.gl.glDisable(Gl1Context.GL_BLEND);
GlState.gl.glDepthMask(true);
}
}