/*
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.client;
import static com.googlecode.gwtquake.shared.common.Constants.CVAR_ARCHIVE;
import static com.googlecode.gwtquake.shared.common.Constants.ERR_DROP;
import static com.googlecode.gwtquake.shared.common.Constants.MAX_DLIGHTS;
import static com.googlecode.gwtquake.shared.common.Constants.MAX_ENTITIES;
import static com.googlecode.gwtquake.shared.common.Constants.MAX_LIGHTSTYLES;
import static com.googlecode.gwtquake.shared.common.Constants.MAX_PARTICLES;
import static com.googlecode.gwtquake.shared.common.Constants.YAW;
import static com.googlecode.gwtquake.shared.common.Constants.ca_active;
import static com.googlecode.gwtquake.shared.common.Globals.cl;
import static com.googlecode.gwtquake.shared.common.Globals.cl_add_blend;
import static com.googlecode.gwtquake.shared.common.Globals.cl_add_entities;
import static com.googlecode.gwtquake.shared.common.Globals.cl_add_lights;
import static com.googlecode.gwtquake.shared.common.Globals.cl_add_particles;
import static com.googlecode.gwtquake.shared.common.Globals.cl_paused;
import static com.googlecode.gwtquake.shared.common.Globals.cl_timedemo;
import static com.googlecode.gwtquake.shared.common.Globals.cls;
import static com.googlecode.gwtquake.shared.common.Globals.crosshair;
import static com.googlecode.gwtquake.shared.common.Globals.gun_frame;
import static com.googlecode.gwtquake.shared.common.Globals.gun_model;
import static com.googlecode.gwtquake.shared.common.Globals.re;
import static com.googlecode.gwtquake.shared.common.Globals.scr_vrect;
import java.nio.FloatBuffer;
import com.googlecode.gwtquake.shared.common.AsyncCallback;
import com.googlecode.gwtquake.shared.common.Com;
import com.googlecode.gwtquake.shared.common.ConsoleVariables;
import com.googlecode.gwtquake.shared.common.ExecutableCommand;
import com.googlecode.gwtquake.shared.game.Commands;
import com.googlecode.gwtquake.shared.game.ConsoleVariable;
import com.googlecode.gwtquake.shared.render.DynamicLight;
import com.googlecode.gwtquake.shared.render.Model;
import com.googlecode.gwtquake.shared.render.Particles;
import com.googlecode.gwtquake.shared.sys.Timer;
import com.googlecode.gwtquake.shared.util.Math3D;
import com.googlecode.gwtquake.shared.util.Vargs;
/**
* V
*/
public final class Video {
static ConsoleVariable cl_testblend;
static ConsoleVariable cl_testparticles;
static ConsoleVariable cl_testentities;
static ConsoleVariable cl_testlights;
static ConsoleVariable cl_stats;
static int r_numdlights;
static DynamicLight[] r_dlights = new DynamicLight[MAX_DLIGHTS];
static int r_numentities;
static EntityType[] r_entities = new EntityType[MAX_ENTITIES];
static int r_numparticles;
//static particle_t[] r_particles = new particle_t[MAX_PARTICLES];
static Lightstyle[] r_lightstyles = new Lightstyle[MAX_LIGHTSTYLES];
static {
for (int i = 0; i < r_dlights.length; i++)
r_dlights[i] = new DynamicLight();
for (int i = 0; i < r_entities.length; i++)
r_entities[i] = new EntityType();
for (int i = 0; i < r_lightstyles.length; i++)
r_lightstyles[i] = new Lightstyle();
}
/*
* ==================== V_ClearScene
*
* Specifies the model that will be used as the world ====================
*/
static void ClearScene() {
r_numdlights = 0;
r_numentities = 0;
r_numparticles = 0;
}
/*
* ===================== V_AddEntity
*
* =====================
*/
static void AddEntity(EntityType ent) {
if (r_numentities >= MAX_ENTITIES)
return;
r_entities[r_numentities++].set(ent);
}
/*
* ===================== V_AddParticle
*
* =====================
*/
static void AddParticle(float[] org, int color, float alpha) {
if (r_numparticles >= MAX_PARTICLES)
return;
int i = r_numparticles++;
int c = Particles.colorTable[color];
c |= (int) (alpha * 255) << 24;
Particles.colorArray.put(i, c);
i *= 3;
FloatBuffer vertexBuf = Particles.vertexArray;
vertexBuf.put(i++, org[0]);
vertexBuf.put(i++, org[1]);
vertexBuf.put(i++, org[2]);
}
/*
* ===================== V_AddLight
*
* =====================
*/
static void AddLight(float[] org, float intensity, float r, float g, float b) {
DynamicLight dl;
if (r_numdlights >= MAX_DLIGHTS)
return;
dl = r_dlights[r_numdlights++];
Math3D.VectorCopy(org, dl.origin);
dl.intensity = intensity;
dl.color[0] = r;
dl.color[1] = g;
dl.color[2] = b;
}
/*
* ===================== V_AddLightStyle
*
* =====================
*/
static void AddLightStyle(int style, float r, float g, float b) {
Lightstyle ls;
if (style < 0 || style > MAX_LIGHTSTYLES)
Com.Error(ERR_DROP, "Bad light style " + style);
ls = r_lightstyles[style];
ls.white = r + g + b;
ls.rgb[0] = r;
ls.rgb[1] = g;
ls.rgb[2] = b;
}
// stack variable
private static final float[] origin = { 0, 0, 0 };
/*
* ================ V_TestParticles
*
* If cl_testparticles is set, create 4096 particles in the view
* ================
*/
static void TestParticles() {
int i, j;
float d, r, u;
r_numparticles = 0;
for (i = 0; i < MAX_PARTICLES; i++) {
d = i * 0.25f;
r = 4 * ((i & 7) - 3.5f);
u = 4 * (((i >> 3) & 7) - 3.5f);
for (j = 0; j < 3; j++)
origin[j] = cl.refdef.vieworg[j] + cl.v_forward[j] * d
+ cl.v_right[j] * r + cl.v_up[j] * u;
AddParticle(origin, 8, cl_testparticles.value);
}
}
/*
* ================ V_TestEntities
*
* If cl_testentities is set, create 32 player models ================
*/
static void TestEntities() {
int i, j;
float f, r;
EntityType ent;
r_numentities = 32;
//memset (r_entities, 0, sizeof(r_entities));
for (i = 0; i < r_entities.length; i++)
r_entities[i].clear();
for (i = 0; i < r_numentities; i++) {
ent = r_entities[i];
r = 64 * ((i % 4) - 1.5f);
f = 64 * (i / 4) + 128;
for (j = 0; j < 3; j++)
ent.origin[j] = cl.refdef.vieworg[j] + cl.v_forward[j] * f
+ cl.v_right[j] * r;
ent.model = cl.baseclientinfo.model;
ent.skin = cl.baseclientinfo.skin;
}
}
/*
* ================ V_TestLights
*
* If cl_testlights is set, create 32 lights models ================
*/
static void TestLights() {
int i, j;
float f, r;
DynamicLight dl;
r_numdlights = 32;
//memset (r_dlights, 0, sizeof(r_dlights));
for (i = 0; i < r_dlights.length; i++)
r_dlights[i] = new DynamicLight();
for (i = 0; i < r_numdlights; i++) {
dl = r_dlights[i];
r = 64 * ((i % 4) - 1.5f);
f = 64 * (i / 4) + 128;
for (j = 0; j < 3; j++)
dl.origin[j] = cl.refdef.vieworg[j] + cl.v_forward[j] * f
+ cl.v_right[j] * r;
dl.color[0] = ((i % 6) + 1) & 1;
dl.color[1] = (((i % 6) + 1) & 2) >> 1;
dl.color[2] = (((i % 6) + 1) & 4) >> 2;
dl.intensity = 200;
}
}
static ExecutableCommand Gun_Next_f = new ExecutableCommand() {
public void execute() {
gun_frame++;
Com.Printf("frame " + gun_frame + "\n");
}
};
static ExecutableCommand Gun_Prev_f = new ExecutableCommand() {
public void execute() {
gun_frame--;
if (gun_frame < 0)
gun_frame = 0;
Com.Printf("frame " + gun_frame + "\n");
}
};
static ExecutableCommand Gun_Model_f = new ExecutableCommand() {
public void execute() {
if (Commands.Argc() != 2) {
gun_model = null;
return;
}
String name = "models/" + Commands.Argv(1) + "/tris.md2";
re.RegisterModel(name, new AsyncCallback<Model>() {
public void onSuccess(Model response) {
gun_model = response;
}
public void onFailure(Throwable e) {
// TODO(jgw): doesn't look like anyone cares.
}
});
}
};
/*
* ================== V_RenderView
*
* ==================
*/
static void RenderView(float stereo_separation) {
// extern int entitycmpfnc( const entity_t *, const entity_t * );
//
if (cls.state != ca_active)
return;
if (!cl.refresh_prepped)
return; // still loading
if (cl_timedemo.value != 0.0f) {
if (cl.timedemo_start == 0)
cl.timedemo_start = Timer.Milliseconds();
cl.timedemo_frames++;
}
// an invalid frame will just use the exact previous refdef
// we can't use the old frame if the video mode has changed, though...
if (cl.frame.valid && (cl.force_refdef || cl_paused.value == 0.0f)) {
cl.force_refdef = false;
Video.ClearScene();
// build a refresh entity list and calc cl.sim*
// this also calls CL_CalcViewValues which loads
// v_forward, etc.
ClientEntities.AddEntities();
if (cl_testparticles.value != 0.0f)
TestParticles();
if (cl_testentities.value != 0.0f)
TestEntities();
if (cl_testlights.value != 0.0f)
TestLights();
if (cl_testblend.value != 0.0f) {
cl.refdef.blend[0] = 1.0f;
cl.refdef.blend[1] = 0.5f;
cl.refdef.blend[2] = 0.25f;
cl.refdef.blend[3] = 0.5f;
}
// offset vieworg appropriately if we're doing stereo separation
if (stereo_separation != 0) {
float[] tmp = new float[3];
Math3D.VectorScale(cl.v_right, stereo_separation, tmp);
Math3D.VectorAdd(cl.refdef.vieworg, tmp, cl.refdef.vieworg);
}
// never let it sit exactly on a node line, because a water plane
// can
// dissapear when viewed with the eye exactly on it.
// the server protocol only specifies to 1/8 pixel, so add 1/16 in
// each axis
cl.refdef.vieworg[0] += 1.0 / 16;
cl.refdef.vieworg[1] += 1.0 / 16;
cl.refdef.vieworg[2] += 1.0 / 16;
cl.refdef.x = scr_vrect.x;
cl.refdef.y = scr_vrect.y;
cl.refdef.width = scr_vrect.width;
cl.refdef.height = scr_vrect.height;
cl.refdef.fov_y = Math3D.CalcFov(cl.refdef.fov_x, cl.refdef.width,
cl.refdef.height);
cl.refdef.time = cl.time * 0.001f;
cl.refdef.areabits = cl.frame.areabits;
if (cl_add_entities.value == 0.0f)
r_numentities = 0;
if (cl_add_particles.value == 0.0f)
r_numparticles = 0;
if (cl_add_lights.value == 0.0f)
r_numdlights = 0;
if (cl_add_blend.value == 0) {
Math3D.VectorClear(cl.refdef.blend);
}
cl.refdef.num_entities = r_numentities;
cl.refdef.entities = r_entities;
cl.refdef.num_particles = r_numparticles;
cl.refdef.num_dlights = r_numdlights;
cl.refdef.dlights = r_dlights;
cl.refdef.lightstyles = r_lightstyles;
cl.refdef.rdflags = cl.frame.playerstate.rdflags;
// sort entities for better cache locality
// !!! useless in Java !!!
//Arrays.sort(cl.refdef.entities, entitycmpfnc);
}
re.RenderFrame(cl.refdef);
if (cl_stats.value != 0.0f)
Com.Printf("ent:%i lt:%i part:%i\n", new Vargs(3).add(
r_numentities).add(r_numdlights).add(r_numparticles));
Screen.AddDirtyPoint(scr_vrect.x, scr_vrect.y);
Screen.AddDirtyPoint(scr_vrect.x + scr_vrect.width - 1, scr_vrect.y
+ scr_vrect.height - 1);
Screen.DrawCrosshair();
}
/*
* ============= V_Viewpos_f =============
*/
static ExecutableCommand Viewpos_f = new ExecutableCommand() {
public void execute() {
Com.Printf("(%i %i %i) : %i\n", new Vargs(4).add(
(int) cl.refdef.vieworg[0]).add((int) cl.refdef.vieworg[1])
.add((int) cl.refdef.vieworg[2]).add(
(int) cl.refdef.viewangles[YAW]));
}
};
public static void Init() {
Commands.addCommand("gun_next", Gun_Next_f);
Commands.addCommand("gun_prev", Gun_Prev_f);
Commands.addCommand("gun_model", Gun_Model_f);
Commands.addCommand("viewpos", Viewpos_f);
crosshair = ConsoleVariables.Get("crosshair", "0", CVAR_ARCHIVE);
cl_testblend = ConsoleVariables.Get("cl_testblend", "0", 0);
cl_testparticles = ConsoleVariables.Get("cl_testparticles", "0", 0);
cl_testentities = ConsoleVariables.Get("cl_testentities", "0", 0);
cl_testlights = ConsoleVariables.Get("cl_testlights", "0", 0);
cl_stats = ConsoleVariables.Get("cl_stats", "0", 0);
}
}