openmohaa/code/cgame/cg_beam.cpp

1460 lines
40 KiB
C++
Raw Permalink Normal View History

2023-04-30 00:02:16 +02:00
/*
===========================================================================
Copyright (C) 2023 the OpenMoHAA team
This file is part of OpenMoHAA source code.
OpenMoHAA source code 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.
OpenMoHAA source code 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 OpenMoHAA source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// DESCRIPTION:
// Beam effects
#include "cg_local.h"
#include "vector.h"
#include "container.h"
#include "cg_commands.h"
class beam_t : public Class
2023-05-06 23:07:04 +02:00
{
public:
beam_t();
2023-07-05 21:24:23 +02:00
int entity;
qhandle_t hModel;
int endtime;
Vector start, end;
float scale;
float alpha;
int flags;
int parent;
float max_offset;
float min_offset;
int numSubdivisions;
int overlap;
int beamshader;
byte shaderRGBA[4];
int update_time;
int delay;
float life;
int numspherebeams;
float sphereradius;
int toggletime;
int toggledelay;
qboolean active;
float alphastep;
int renderfx;
str name;
beam_t *next;
beam_t *prev;
2023-05-06 23:07:04 +02:00
};
2023-04-30 00:02:16 +02:00
beam_t::beam_t()
2023-05-06 23:07:04 +02:00
{
2023-07-05 21:24:23 +02:00
entity = 0;
hModel = 0;
endtime = 0;
scale = 0;
alpha = 0;
flags = 0;
parent = ENTITYNUM_NONE;
max_offset = 0;
min_offset = 0;
2023-05-06 23:07:04 +02:00
numSubdivisions = 0;
2023-07-05 21:24:23 +02:00
overlap = 0;
beamshader = 0;
update_time = 0;
delay = 0;
life = 0;
numspherebeams = 0;
sphereradius = 0;
toggletime = 0;
toggledelay = 0;
active = 0;
alphastep = 0;
renderfx = 0;
2023-05-06 23:07:04 +02:00
memset(shaderRGBA, 0, 4);
next = NULL;
prev = NULL;
}
2023-04-30 00:02:16 +02:00
2023-07-05 21:24:23 +02:00
beam_t cl_beams[MAX_BEAMS];
beam_t *cl_free_beams;
beam_t *cl_active_beams;
2023-04-30 00:02:16 +02:00
static int seed = 100;
// Recursive beam builder - I don't use it anymore
/*
void CG_BuildRenderBeam_r
(
Vector start,
Vector end,
float angleVar,
int numSubdivisions,
int maxSubdivisions
)
2023-05-06 23:07:04 +02:00
{
2023-04-30 00:02:16 +02:00
if ( numSubdivisions == maxSubdivisions )
2023-05-06 23:07:04 +02:00
{
return;
}
else
{
// subdivide line and call on both halves
numSubdivisions += 1;
2023-04-30 00:02:16 +02:00
mid = ( p1 * 0.5 ) + ( p2 * 0.5 );
2023-05-06 23:07:04 +02:00
2023-04-30 00:02:16 +02:00
int seed = 100;
mid[0] += Q_crandom( &seed ) * angleVar;
mid[1] += Q_crandom( &seed ) * angleVar;
mid[2] += Q_crandom( &seed ) * angleVar;
2023-05-06 23:07:04 +02:00
CG_BuildRendererBeam( p1, mid, angleVar, numSubdivisions, maxSubdivisions, color, beamshader, scale );
CG_BuildRendererBeam( mid, p2, angleVar, numSubdivisions, maxSubdivisions, color, beamshader, scale );
}
2023-04-30 00:02:16 +02:00
}
*/
#define MAX_BEAM_BACKUP 6
#define MAX_BEAM_SEGMENTS 32
2023-07-05 21:24:23 +02:00
typedef struct beamSegment_t {
2023-05-06 23:07:04 +02:00
polyVert_t points[4];
} beamSegment_t;
2023-04-30 00:02:16 +02:00
2023-07-05 21:24:23 +02:00
typedef struct beamList_t {
int time;
int updatetime;
int numsegments;
2023-05-06 23:07:04 +02:00
beamSegment_t segments[MAX_BEAM_SEGMENTS];
} beamList_t;
2023-04-30 00:02:16 +02:00
2023-07-05 21:24:23 +02:00
typedef struct beamEnt_t {
int owner;
int numbeams;
int life;
int renderfx;
2023-05-06 23:07:04 +02:00
beamList_t beamlist[MAX_BEAM_BACKUP];
} beamEnt_t;
2023-07-05 21:24:23 +02:00
Container<beamEnt_t *> beamManager;
2023-05-06 23:07:04 +02:00
void ClientGameCommandManager::InitializeBeams()
{
2023-07-05 21:24:23 +02:00
int i;
beam_t *b;
2023-05-06 23:07:04 +02:00
2023-07-05 21:24:23 +02:00
cl_free_beams = NULL;
2023-05-06 23:07:04 +02:00
cl_active_beams = NULL;
2023-07-05 21:24:23 +02:00
for (i = 0; i < MAX_BEAMS; i++) {
2023-05-06 23:07:04 +02:00
b = &cl_beams[i];
if (cl_free_beams) {
cl_free_beams->prev = b;
}
2023-04-30 00:02:16 +02:00
2023-07-05 21:24:23 +02:00
b->next = cl_free_beams;
b->prev = 0;
2023-05-06 23:07:04 +02:00
cl_free_beams = b;
}
}
2023-04-30 00:02:16 +02:00
2023-07-05 21:24:23 +02:00
void RemoveBeamList(int owner)
2023-05-06 23:07:04 +02:00
{
int i, num;
2023-04-30 00:02:16 +02:00
2023-05-06 23:07:04 +02:00
num = beamManager.NumObjects();
2023-04-30 00:02:16 +02:00
2023-07-05 21:24:23 +02:00
for (i = 1; i <= num; i++) {
beamEnt_t *be = beamManager.ObjectAt(i);
2023-04-30 00:02:16 +02:00
2023-07-05 21:24:23 +02:00
if (be->owner == owner) {
2023-05-06 23:07:04 +02:00
beamManager.RemoveObjectAt(i);
delete be;
return;
}
}
}
2023-04-30 00:02:16 +02:00
2023-07-05 21:24:23 +02:00
beamEnt_t *FindBeamList(int owner)
2023-05-06 23:07:04 +02:00
{
int i, num;
2023-04-30 00:02:16 +02:00
2023-05-06 23:07:04 +02:00
num = beamManager.NumObjects();
2023-04-30 00:02:16 +02:00
2023-07-05 21:24:23 +02:00
for (i = 1; i <= num; i++) {
beamEnt_t *be = beamManager.ObjectAt(i);
2023-04-30 00:02:16 +02:00
2023-07-05 21:24:23 +02:00
if (be->owner == owner) {
2023-05-06 23:07:04 +02:00
return be;
2023-07-05 21:24:23 +02:00
}
2023-05-06 23:07:04 +02:00
}
return NULL;
}
2023-04-30 00:02:16 +02:00
2023-07-05 21:24:23 +02:00
int CreateNewBeamEntity(int owner, float life)
2023-05-06 23:07:04 +02:00
{
2023-07-05 21:24:23 +02:00
beamEnt_t *be;
int i, oldest, oldest_time;
2023-05-06 23:07:04 +02:00
be = FindBeamList(owner);
2023-07-05 21:24:23 +02:00
if (!be) {
2023-05-06 23:07:04 +02:00
be = new beamEnt_t;
2023-07-05 21:24:23 +02:00
if (!be) {
2023-05-06 23:07:04 +02:00
cgi.Error(ERR_DROP, "Could not allocate memory for beamEnt.\n");
2023-07-05 21:24:23 +02:00
}
2023-05-06 23:07:04 +02:00
memset(be, 0, sizeof(beamEnt_t));
memset(be->beamlist, 0, sizeof(beamList_t) * MAX_BEAM_BACKUP);
be->owner = owner;
2023-07-05 21:24:23 +02:00
be->life = life;
2023-05-06 23:07:04 +02:00
beamManager.AddObject(be);
}
// find the oldest beam and overwrite it.
2023-07-05 21:24:23 +02:00
oldest = -1;
2023-05-06 23:07:04 +02:00
oldest_time = 999999999;
2023-07-05 21:24:23 +02:00
for (i = 0; i < MAX_BEAM_BACKUP; i++) {
2023-05-06 23:07:04 +02:00
// Check for update time
float t = be->beamlist[i].time;
2023-07-05 21:24:23 +02:00
if (!t) {
2023-05-06 23:07:04 +02:00
oldest = i;
break;
}
2023-07-05 21:24:23 +02:00
if (t < oldest_time) {
oldest = i;
2023-05-06 23:07:04 +02:00
oldest_time = t;
}
}
// Use the oldest beam for the next beam.
be->beamlist[oldest].numsegments = 0;
2023-07-05 21:24:23 +02:00
be->beamlist[oldest].time = cg.time;
be->beamlist[oldest].updatetime = cg.time + be->life;
2023-05-06 23:07:04 +02:00
return oldest;
}
2023-04-30 00:02:16 +02:00
2023-07-05 21:24:23 +02:00
void RemoveBeamEntity(int owner)
2023-05-06 23:07:04 +02:00
{
RemoveBeamList(owner);
}
2023-04-30 00:02:16 +02:00
2023-07-05 21:24:23 +02:00
void AddBeamSegmentToList(int owner, polyVert_t points[4], int beamnum, int segnum, int renderfx)
2023-05-06 23:07:04 +02:00
{
2023-07-05 21:24:23 +02:00
beamEnt_t *be;
2023-05-06 23:07:04 +02:00
be = FindBeamList(owner);
2023-07-05 21:24:23 +02:00
if (!be) {
2023-05-06 23:07:04 +02:00
cgi.DPrintf("Could not find beam entity for owner:%d\n", owner);
return;
}
2023-07-05 21:24:23 +02:00
if (segnum >= MAX_BEAM_SEGMENTS) {
2023-05-06 23:07:04 +02:00
return;
2023-07-05 21:24:23 +02:00
}
2023-05-06 23:07:04 +02:00
be->renderfx = renderfx;
// Copy the 4 points
memcpy(&be->beamlist[beamnum].segments[segnum].points, points, 4 * sizeof(polyVert_t));
// Increase the segment counter
be->beamlist[beamnum].numsegments++;
}
2023-04-30 00:02:16 +02:00
2023-07-05 21:24:23 +02:00
void CG_AddBeamsFromList(int owner, int beamshader)
2023-05-06 23:07:04 +02:00
{
2023-07-05 21:24:23 +02:00
int i, j, k, l;
float frac, fade;
beamEnt_t *be = FindBeamList(owner);
polyVert_t newpoints[4];
2023-05-06 23:07:04 +02:00
2023-07-05 21:24:23 +02:00
if (!be) {
2023-05-06 23:07:04 +02:00
return;
}
2023-07-05 21:24:23 +02:00
for (i = 0; i < MAX_BEAM_BACKUP; i++) {
beamList_t *bl = &be->beamlist[i];
2023-05-06 23:07:04 +02:00
2023-07-05 21:24:23 +02:00
if (!bl->time) {
2023-05-06 23:07:04 +02:00
continue;
2023-07-05 21:24:23 +02:00
}
2023-05-06 23:07:04 +02:00
2023-07-05 21:24:23 +02:00
// Calculate the blend factor for fading
2023-05-06 23:07:04 +02:00
frac = (float)(cg.time - bl->time) / (float)be->life;
fade = 1.0f - frac;
2023-07-05 21:24:23 +02:00
if (fade <= 0) {
2023-05-06 23:07:04 +02:00
bl->time = 0; // RemoveBeamList( owner );
continue;
}
// Go through each segment and draw it with the new modulate
assert(bl->numsegments < MAX_BEAM_SEGMENTS);
2023-07-05 21:24:23 +02:00
for (j = 0; j < bl->numsegments; j++) {
2023-05-06 23:07:04 +02:00
memcpy(newpoints, bl->segments[j].points, 4 * sizeof(polyVert_t));
2023-07-05 21:24:23 +02:00
for (k = 0; k < 4; k++) {
for (l = 0; l < 4; l++) {
2023-05-06 23:07:04 +02:00
newpoints[k].modulate[l] = bl->segments[j].points[k].modulate[l] * fade;
}
2023-04-30 00:02:16 +02:00
}
2023-05-06 23:07:04 +02:00
cgi.R_AddPolyToScene(beamshader, 4, newpoints, be->renderfx);
}
}
}
2023-04-30 00:02:16 +02:00
2023-07-05 21:24:23 +02:00
void RenderSegment(Vector pt1a, Vector pt1b, Vector pt2a, Vector pt2b, byte modulate[4], int beamshader, int renderfx)
2023-05-06 23:07:04 +02:00
{
2023-07-05 21:24:23 +02:00
int i, j;
polyVert_t points[4];
2023-05-06 23:07:04 +02:00
VectorCopy(pt1a, points[0].xyz);
VectorCopy(pt2a, points[1].xyz);
VectorCopy(pt2b, points[2].xyz);
VectorCopy(pt1b, points[3].xyz);
2023-07-05 21:24:23 +02:00
points[0].st[0] = 1;
points[0].st[1] = 1;
points[1].st[0] = 0;
points[1].st[1] = 1;
points[2].st[0] = 0;
points[2].st[1] = 0;
points[3].st[0] = 1;
points[3].st[1] = 0;
2023-05-06 23:07:04 +02:00
// Set the color of the verts
2023-07-05 21:24:23 +02:00
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
2023-05-06 23:07:04 +02:00
points[i].modulate[j] = modulate[j];
}
}
// Add a segment to the list
cgi.R_AddPolyToScene(beamshader, 4, points, renderfx);
}
2023-04-30 00:02:16 +02:00
2023-07-05 21:24:23 +02:00
const int MAX_SUBPOINTS = 256;
2023-04-30 00:02:16 +02:00
static Vector subpoints[MAX_SUBPOINTS];
2023-07-05 21:24:23 +02:00
static int ptctr = 0;
2023-04-30 00:02:16 +02:00
/*
===============
CG_Subdivide
a, b, and c are control points.
the subdivided sequence will be: a, out1, out2, out3, c
===============
*/
2023-05-06 23:07:04 +02:00
static void CG_Subdivide(Vector a, Vector b, Vector c, Vector& out1, Vector& out2, Vector& out3)
{
out1 = 0.5 * (a + b);
out3 = 0.5 * (b + c);
out2 = 0.5 * (out1 + out3);
}
2023-04-30 00:02:16 +02:00
2023-07-05 21:24:23 +02:00
void CG_MultiBeamBegin(void)
2023-05-06 23:07:04 +02:00
{
ptctr = 0;
}
2023-04-30 00:02:16 +02:00
2023-07-05 21:24:23 +02:00
void CG_MultiBeamAddPoints(
vec3_t start, vec3_t end, int numsegments, int flags, float minoffset, float maxoffset, qboolean addstartpoint
2023-05-06 23:07:04 +02:00
)
{
2023-07-05 21:24:23 +02:00
Vector delta, dir, randdir;
float length;
int i;
2023-05-06 23:07:04 +02:00
2023-07-05 21:24:23 +02:00
if (ptctr > MAX_SUBPOINTS) {
2023-05-06 23:07:04 +02:00
return;
2023-07-05 21:24:23 +02:00
}
2023-05-06 23:07:04 +02:00
2023-07-05 21:24:23 +02:00
if (addstartpoint) {
2023-05-06 23:07:04 +02:00
subpoints[ptctr++] = start;
}
2023-07-05 21:24:23 +02:00
delta = Vector(end) - Vector(start);
2023-05-06 23:07:04 +02:00
length = delta.length();
length /= numsegments;
// get the dir of beam
dir = delta;
dir.normalize();
2023-07-05 21:24:23 +02:00
for (i = 1; i < numsegments; i++) {
2023-05-06 23:07:04 +02:00
Vector newpt;
2023-07-05 21:24:23 +02:00
if (ptctr > MAX_SUBPOINTS) {
2023-05-06 23:07:04 +02:00
return;
2023-07-05 21:24:23 +02:00
}
2023-05-06 23:07:04 +02:00
randdir = Vector(crandom(), crandom(), crandom());
newpt = Vector(start) + dir * i * length;
newpt += minoffset * randdir + maxoffset * randdir;
subpoints[ptctr++] = newpt;
}
subpoints[ptctr++] = end;
}
2023-04-30 00:02:16 +02:00
2023-07-05 21:24:23 +02:00
void CG_MultiBeamEnd(
float scale, int renderfx, const char *beamshadername, byte modulate[4], int flags, int owner, float life
2023-05-06 23:07:04 +02:00
)
{
2023-07-05 21:24:23 +02:00
Vector prevpt, currpt;
Vector p1, p2, p3, p4, v1, v2, up, currpt1, currpt2, prevpt1, prevpt2;
qboolean prevptvalid = false;
int i, beamshader;
2023-05-06 23:07:04 +02:00
beamshader = cgi.R_RegisterShader(beamshadername);
2023-07-05 21:24:23 +02:00
prevpt = subpoints[0];
2023-05-06 23:07:04 +02:00
prevptvalid = false;
2023-07-05 21:24:23 +02:00
for (i = 1; i < ptctr; i++) {
2023-05-06 23:07:04 +02:00
currpt = subpoints[i];
// Generate the up vector
v1 = prevpt - cg.refdef.vieworg;
v2 = currpt - cg.refdef.vieworg;
2023-04-30 00:02:16 +02:00
#if 0
2023-05-06 23:07:04 +02:00
cgi.R_DebugLine(prevpt, currpt, 1, 1, 1, 1);
Vector pt = prevpt + up * 5;
cgi.R_DebugLine(prevpt, pt, 0, 0, 1, 1);
2023-04-30 00:02:16 +02:00
#endif
2023-05-06 23:07:04 +02:00
up.CrossProduct(v1, v2);
up.normalize();
// Calculate the first points
currpt1 = currpt + (up * scale);
currpt2 = currpt + (up * -scale);
2023-07-05 21:24:23 +02:00
if (!prevptvalid) {
prevpt1 = prevpt + up * scale;
prevpt2 = prevpt + up * -scale;
2023-05-06 23:07:04 +02:00
prevptvalid = true;
}
2023-04-30 00:02:16 +02:00
#if 1
2023-05-06 23:07:04 +02:00
RenderSegment(currpt1, currpt2, prevpt1, prevpt2, modulate, beamshader, renderfx);
2023-04-30 00:02:16 +02:00
#endif
2023-05-06 23:07:04 +02:00
2023-07-05 21:24:23 +02:00
prevpt = currpt;
2023-05-06 23:07:04 +02:00
prevpt1 = currpt1;
prevpt2 = currpt2;
}
2023-07-05 21:24:23 +02:00
if (flags & BEAM_PERSIST_EFFECT) {
2023-05-06 23:07:04 +02:00
CG_AddBeamsFromList(owner, beamshader);
2023-07-05 21:24:23 +02:00
}
2023-05-06 23:07:04 +02:00
}
2023-04-30 00:02:16 +02:00
2023-07-05 21:24:23 +02:00
static void CG_MultiBeamSubdivide(centity_t *cent)
2023-05-06 23:07:04 +02:00
{
2023-07-05 21:24:23 +02:00
Vector pt1, pt2, pt3;
Vector out1, out2, out3, out4, out5, out6, out7, out8, out9;
centity_t *current;
2023-05-06 23:07:04 +02:00
2023-07-05 21:24:23 +02:00
ptctr = 0;
2023-05-06 23:07:04 +02:00
current = cent;
// Multibeam requires at least 3 points to start with
// Get pt1
2023-07-05 21:24:23 +02:00
if (current->currentState.tag_num == ENTITYNUM_NONE) {
2023-05-06 23:07:04 +02:00
cgi.DPrintf("CG_MultiBeamSubdivide : Multi beam entity does not have a child\n");
return;
}
pt1 = current->lerpOrigin;
// Get pt2
current = &cg_entities[current->currentState.tag_num];
// Make sure that child is a multibeam
2023-07-05 21:24:23 +02:00
if (current->currentState.eType != ET_MULTIBEAM) {
2023-05-06 23:07:04 +02:00
return;
2023-07-05 21:24:23 +02:00
}
2023-05-06 23:07:04 +02:00
2023-07-05 21:24:23 +02:00
if (current->currentState.tag_num == ENTITYNUM_NONE) {
2023-05-06 23:07:04 +02:00
cgi.DPrintf("CG_MultiBeamSubdivide : Multi beam entity does not have a child\n");
return;
}
pt2 = current->lerpOrigin;
// Get pt3
current = &cg_entities[current->currentState.tag_num];
// Make sure that child is a multibeam
2023-07-05 21:24:23 +02:00
if (current->currentState.eType != ET_MULTIBEAM) {
2023-05-06 23:07:04 +02:00
return;
}
2023-07-05 21:24:23 +02:00
if (current->currentState.tag_num == ENTITYNUM_NONE) {
2023-05-06 23:07:04 +02:00
cgi.DPrintf("CG_MultiBeamSubdivide : Multi beam entity does not have a child\n");
return;
}
pt3 = current->lerpOrigin;
// First point into the subdivided points
subpoints[ptctr++] = pt1;
2023-07-05 21:24:23 +02:00
while (1) {
2023-05-06 23:07:04 +02:00
// Do the subdivide
CG_Subdivide(pt1, pt2, pt3, out1, out2, out3);
CG_Subdivide(pt1, out1, out2, out4, out5, out6);
2023-07-05 21:24:23 +02:00
if ((ptctr + 4) > MAX_SUBPOINTS) {
2023-05-06 23:07:04 +02:00
break;
2023-07-05 21:24:23 +02:00
}
2023-05-06 23:07:04 +02:00
// Save the points
subpoints[ptctr++] = out4;
subpoints[ptctr++] = out5;
subpoints[ptctr++] = out6;
subpoints[ptctr++] = out2;
// end condition
2023-07-05 21:24:23 +02:00
if ((current->currentState.tag_num == ENTITYNUM_NONE) || (!current->currentValid)) {
2023-05-06 23:07:04 +02:00
CG_Subdivide(out2, out3, pt3, out7, out8, out9);
subpoints[ptctr++] = out7;
subpoints[ptctr++] = out8;
subpoints[ptctr++] = out9;
subpoints[ptctr++] = pt3;
break;
}
// Advance to next ent
current = &cg_entities[current->currentState.tag_num];
2023-07-05 21:24:23 +02:00
if (!current->currentValid) {
2023-05-06 23:07:04 +02:00
break;
}
// Advance the points down the line
pt1 = out2;
pt2 = pt3;
pt3 = current->lerpOrigin;
}
}
2023-04-30 00:02:16 +02:00
2023-07-05 21:24:23 +02:00
void CG_MultiBeam(centity_t *cent)
2023-05-06 23:07:04 +02:00
{
Vector prevpt, currpt;
2023-07-05 21:24:23 +02:00
entityState_t *s1;
2023-05-06 23:07:04 +02:00
Vector p1, p2, p3, p4, v1, v2, up, currpt1, currpt2, prevpt1, prevpt2;
2023-07-05 21:24:23 +02:00
const char *beamshadername;
2023-05-06 23:07:04 +02:00
int beamshader;
byte modulate[4];
qboolean prevptvalid = false;
int i;
s1 = &cent->currentState;
// If this isn't the parent of the beam, then return
2023-07-05 21:24:23 +02:00
if (!s1->surfaces[0]) {
2023-05-06 23:07:04 +02:00
return;
}
// Subdivide up the segments
CG_MultiBeamSubdivide(cent);
// This is the top of the beam ent list, build up a renderer beam based on all the children
beamshadername = CG_ConfigString(CS_IMAGES + s1->surfaces[1]); // index for shader configstring
2023-07-05 21:24:23 +02:00
beamshader = cgi.R_RegisterShader(beamshadername);
2023-05-06 23:07:04 +02:00
//beamshader = cgi.R_RegisterShader( "<default>" );
2023-07-05 21:24:23 +02:00
for (i = 0; i < 4; i++) {
2023-05-06 23:07:04 +02:00
modulate[i] = cent->color[i] * 255;
2023-07-05 21:24:23 +02:00
}
2023-05-06 23:07:04 +02:00
2023-07-05 21:24:23 +02:00
if (ptctr < 3) {
2023-05-06 23:07:04 +02:00
return;
}
2023-07-05 21:24:23 +02:00
prevpt = subpoints[0];
2023-05-06 23:07:04 +02:00
prevptvalid = false;
2023-07-05 21:24:23 +02:00
for (i = 1; i < ptctr; i++) {
2023-05-06 23:07:04 +02:00
currpt = subpoints[i];
// Generate the up vector
v1 = prevpt - cg.refdef.vieworg;
v2 = currpt - cg.refdef.vieworg;
2023-04-30 00:02:16 +02:00
#if 0
2023-05-06 23:07:04 +02:00
cgi.R_DebugLine(prevpt, currpt, 1, 1, 1, 1);
Vector pt = prevpt + up * 5;
cgi.R_DebugLine(prevpt, pt, 0, 0, 1, 1);
2023-04-30 00:02:16 +02:00
#endif
2023-05-06 23:07:04 +02:00
up.CrossProduct(v1, v2);
up.normalize();
// Calculate the first points
currpt1 = currpt + (up * s1->scale);
currpt2 = currpt + (up * -s1->scale);
2023-07-05 21:24:23 +02:00
if (!prevptvalid) {
prevpt1 = prevpt + up * s1->scale;
prevpt2 = prevpt + up * -s1->scale;
2023-05-06 23:07:04 +02:00
prevptvalid = true;
}
RenderSegment(currpt1, currpt2, prevpt1, prevpt2, modulate, beamshader, s1->renderfx);
2023-07-05 21:24:23 +02:00
prevpt = currpt;
2023-05-06 23:07:04 +02:00
prevpt1 = currpt1;
prevpt2 = currpt2;
}
}
2023-04-30 00:02:16 +02:00
2023-07-05 21:24:23 +02:00
void CG_BuildRendererBeam(
2023-05-06 23:07:04 +02:00
Vector start,
Vector end,
float angleVar,
int numSubdivisions,
byte color[4],
int beamshader,
float scale,
float overlap,
int owner,
float life,
int flags,
float startalpha,
float alphastep,
int renderfx
)
{
2023-07-05 21:24:23 +02:00
Vector p1, p2, v1, v2, dir, prevpt1, prevpt2, nextpt, mid, delta, up;
int i, ii, jj;
polyVert_t points[4];
float length;
int segnum = 0;
int beamnum = 0;
float alphafactor;
int picW;
2023-05-06 23:07:04 +02:00
2023-07-05 21:24:23 +02:00
// Create or increment the number of beams for this owner and check to
2023-05-06 23:07:04 +02:00
// see if we should add a new beam
2023-07-05 21:24:23 +02:00
if (flags & BEAM_PERSIST_EFFECT) {
2023-05-06 23:07:04 +02:00
beamnum = CreateNewBeamEntity(owner, life);
2023-07-05 21:24:23 +02:00
if (beamnum < 0) {
2023-05-06 23:07:04 +02:00
return;
2023-07-05 21:24:23 +02:00
}
2023-05-06 23:07:04 +02:00
}
// For debugging texture coords
2023-07-05 21:24:23 +02:00
//beamshader = cgi.R_RegisterShader( "<default>" );
2023-05-06 23:07:04 +02:00
picW = cgi.R_GetShaderWidth(beamshader);
// calcluate length of beam segment
2023-07-05 21:24:23 +02:00
delta = end - start;
2023-05-06 23:07:04 +02:00
length = delta.length();
length /= numSubdivisions;
// get the dir of beam
dir = delta;
dir.normalize();
// Calculate the first up vector
v1 = start - cg.refdef.vieworg;
v2 = end - cg.refdef.vieworg;
up.CrossProduct(v1, v2);
up.normalize();
// Calculate the first points
prevpt1 = start + (up * scale);
prevpt2 = start + (up * -scale);
2023-07-05 21:24:23 +02:00
p1 = start;
2023-05-06 23:07:04 +02:00
// go through and calculate each point of the beam and offset it by the anglevar
2023-07-05 21:24:23 +02:00
for (i = 1; i <= numSubdivisions; i++) {
2023-05-06 23:07:04 +02:00
// Calculate the next point along the beam
p2 = start + (dir * i * length);
// Random variance on the next point ( except if it's the last )
2023-07-05 21:24:23 +02:00
if (i != numSubdivisions) {
if (flags & BEAM_WAVE_EFFECT) {
2023-05-06 23:07:04 +02:00
float phase = p2.x + p2.y;
p2.z += sin(phase + cg.time) * angleVar;
2023-07-05 21:24:23 +02:00
} else if (flags & BEAM_USE_NOISE) {
2023-05-06 23:07:04 +02:00
p2.x += cgi.R_Noise(p2.x, p2.y, p2.z, cg.time) * angleVar;
p2.y += cgi.R_Noise(p2.x, p2.y, p2.z, cg.time) * angleVar;
p2.z += cgi.R_Noise(p2.x, p2.y, p2.z, cg.time) * angleVar;
2023-07-05 21:24:23 +02:00
} else {
2023-05-06 23:07:04 +02:00
p2.x += Q_crandom(&seed) * angleVar;
p2.y += Q_crandom(&seed) * angleVar;
p2.z += Q_crandom(&seed) * angleVar;
2023-04-30 00:02:16 +02:00
}
2023-05-06 23:07:04 +02:00
}
// Create the up vec for the beam which is parallel to the viewplane
v1 = p1 - cg.refdef.vieworg;
v2 = p2 - cg.refdef.vieworg;
up.CrossProduct(v1, v2);
up.normalize();
// Build the quad
VectorMA(p2, scale, up, points[0].xyz);
VectorCopy(prevpt1, points[1].xyz);
VectorCopy(prevpt2, points[2].xyz);
VectorMA(p2, -scale, up, points[3].xyz);
if (flags & BEAM_TILESHADER) // Tile the shader across the beam
{
float startS = (length * (i - 1)) / (float)picW;
2023-07-05 21:24:23 +02:00
float endS = (length * (i)) / (float)picW;
points[0].st[0] = startS;
points[0].st[1] = 1;
points[1].st[0] = endS;
points[1].st[1] = 1;
points[2].st[0] = endS;
points[2].st[1] = 0;
points[3].st[0] = startS;
points[3].st[1] = 0;
} else {
points[0].st[0] = 1;
points[0].st[1] = 1;
points[1].st[0] = 0;
points[1].st[1] = 1;
points[2].st[0] = 0;
points[2].st[1] = 0;
points[3].st[0] = 1;
points[3].st[1] = 0;
2023-05-06 23:07:04 +02:00
}
2023-07-05 21:24:23 +02:00
if (!alphastep) {
2023-05-06 23:07:04 +02:00
alphafactor = 1.0f;
2023-07-05 21:24:23 +02:00
} else {
2023-05-06 23:07:04 +02:00
alphafactor = startalpha + (alphastep * i);
2023-07-05 21:24:23 +02:00
}
2023-05-06 23:07:04 +02:00
// Set the color of the verts
2023-07-05 21:24:23 +02:00
for (ii = 0; ii < 4; ii++) {
for (jj = 0; jj < 4; jj++) {
2023-05-06 23:07:04 +02:00
points[ii].modulate[jj] = color[jj] * alphafactor;
2023-04-30 00:02:16 +02:00
}
2023-05-06 23:07:04 +02:00
}
2023-07-05 21:24:23 +02:00
if (flags & BEAM_PERSIST_EFFECT) {
2023-05-06 23:07:04 +02:00
// Save the segment for backup for drawing faded out
AddBeamSegmentToList(owner, points, beamnum, segnum++, renderfx);
2023-07-05 21:24:23 +02:00
} else {
2023-05-06 23:07:04 +02:00
// Add it to the ref
cgi.R_AddPolyToScene(beamshader, 4, points, renderfx);
}
// Subtract off the overlap
2023-07-05 21:24:23 +02:00
if (overlap) {
2023-05-06 23:07:04 +02:00
p2 = p2 + (dir * -overlap);
}
// Save off the last point to use as the first point on the next quad
VectorMA(p2, scale, up, prevpt1);
VectorMA(p2, -scale, up, prevpt2);
p1 = p2;
}
}
2023-04-30 00:02:16 +02:00
2023-07-05 21:24:23 +02:00
void CG_BuildRendererBeam_Fast(
2023-05-07 17:45:07 +02:00
Vector start,
Vector end,
float angleVar,
int numSubdivisions,
byte color[4],
int beamshader,
float scale,
float overlap,
int owner,
float life,
int flags,
float startalpha,
float alphastep,
int renderfx
)
{
2023-07-05 21:24:23 +02:00
int ii, jj;
2023-05-07 17:45:07 +02:00
polyVert_t points[4];
2023-07-05 21:24:23 +02:00
int beamnum;
float alphafactor;
2023-05-07 17:45:07 +02:00
2023-07-05 21:24:23 +02:00
// Create or increment the number of beams for this owner and check to
2023-05-07 17:45:07 +02:00
// see if we should add a new beam
2023-07-05 21:24:23 +02:00
if (flags & BEAM_PERSIST_EFFECT) {
2023-05-07 17:45:07 +02:00
beamnum = CreateNewBeamEntity(owner, life);
2023-07-05 21:24:23 +02:00
if (beamnum < 0) {
2023-05-07 17:45:07 +02:00
return;
2023-07-05 21:24:23 +02:00
}
2023-05-07 17:45:07 +02:00
}
VectorMA(end, scale, cg.refdef.viewaxis[1], points[0].xyz);
VectorMA(start, scale, cg.refdef.viewaxis[1], points[1].xyz);
VectorMA(start, -scale, cg.refdef.viewaxis[1], points[2].xyz);
VectorMA(end, -scale, cg.refdef.viewaxis[1], points[3].xyz);
points[0].st[0] = 1.0;
points[0].st[1] = 1.0;
points[1].st[0] = 0.0;
points[1].st[1] = 1.0;
points[2].st[0] = 0.0;
points[2].st[1] = 0.0;
points[3].st[0] = 1.0;
points[3].st[1] = 0.0;
2023-07-05 21:24:23 +02:00
if (!alphastep) {
for (ii = 0; ii < 4; ++ii) {
2023-05-07 17:45:07 +02:00
for (jj = 0; jj < 4; ++jj) {
points[ii].modulate[jj] = color[jj];
}
}
2023-07-05 21:24:23 +02:00
} else {
2023-05-07 17:45:07 +02:00
alphafactor = startalpha + alphastep;
2023-07-05 21:24:23 +02:00
for (ii = 0; ii < 4; ++ii) {
2023-05-07 17:45:07 +02:00
for (jj = 0; jj < 4; ++jj) {
points[ii].modulate[jj] = (int)((float)color[jj] * alphafactor);
}
}
}
if (flags & BEAM_PERSIST_EFFECT) {
AddBeamSegmentToList(owner, points, beamnum, 0, renderfx);
2023-07-05 21:24:23 +02:00
} else {
2023-05-07 17:45:07 +02:00
cgi.R_AddPolyToScene(beamshader, 4, points, renderfx);
}
}
2023-07-05 21:24:23 +02:00
void CG_CreateModelBeam(beam_t *b, vec3_t org, vec3_t dist, float total_length, vec3_t ndir, vec3_t left, vec3_t up)
{
2023-07-05 21:24:23 +02:00
dtiki_t *tiki;
vec3_t mins, maxs;
int single_beam_length;
refEntity_t ent;
int count;
int j;
float factor[3];
float t;
vec3_t angles;
int i;
// Find the length of a single beam
tiki = cgi.R_Model_GetHandle(b->hModel);
// Calculate the bounds of the model to get it's length
cgi.TIKI_CalculateBounds(tiki, 1.0, mins, maxs);
single_beam_length = maxs[0] - mins[0];
// Create the beam entity
memset(&ent, 0, sizeof(ent));
count = 0;
// Initialize the factors
2023-07-05 21:24:23 +02:00
for (j = 0; j < 3; j++) {
factor[j] = 0.3f * crandom();
2023-07-05 21:24:23 +02:00
}
t = 0;
2023-07-05 21:24:23 +02:00
while (t >= 0 && t < 1) {
float dot;
vec3_t pdir;
float delta;
vec3_t distance_point;
count++;
// Set the origin of the current beam using the last calculated org
VectorCopy(org, ent.origin);
// Advance the org one beam length in the new direction ( dist is the newly calculated direction )
2023-07-05 21:24:23 +02:00
for (j = 0; j < 3; j++) {
org[j] += dist[j] * (single_beam_length - b->overlap);
2023-07-05 21:24:23 +02:00
}
// Offset the org by a random amount to simulate lightning
VectorMA(org, single_beam_length * factor[2], up, org);
VectorMA(org, single_beam_length * factor[1], left, org);
// Calculate (t) - how far this new point is along the overall distance
VectorSubtract(org, b->start, pdir);
dot = DotProduct(pdir, ndir);
2023-07-05 21:24:23 +02:00
t = dot / total_length;
// Calculate point at current distance along center beam
VectorMA(b->start, total_length * t, ndir, distance_point);
// Allow any variations
2023-07-05 21:24:23 +02:00
if (t > 0.1 && t < 0.9) {
for (j = 0; j < 3; j++) {
delta = org[j] - distance_point[j];
2023-07-05 21:24:23 +02:00
if (delta > b->max_offset) {
org[j] = distance_point[j] + b->max_offset;
factor[j] = -0.3 * crandom();
2023-07-05 21:24:23 +02:00
} else if (delta < -b->max_offset) {
org[j] = distance_point[j] - b->max_offset;
factor[j] = 0.3 * crandom();
2023-07-05 21:24:23 +02:00
} else {
factor[j] = 0.3 * crandom();
2023-07-05 21:24:23 +02:00
}
2023-04-30 00:02:16 +02:00
}
2023-07-05 21:24:23 +02:00
} else // Clamp to mins
{
2023-07-05 21:24:23 +02:00
for (j = 0; j < 3; j++) {
delta = org[j] - distance_point[j];
2023-07-05 21:24:23 +02:00
if (delta > b->min_offset) {
org[j] -= 0.4 * single_beam_length;
2023-05-01 15:10:46 +02:00
factor[j] = -0.2f;
2023-07-05 21:24:23 +02:00
} else if (delta < -b->min_offset) {
org[j] += 0.4 * single_beam_length;
2023-05-01 15:10:46 +02:00
factor[j] = 0.2f;
2023-07-05 21:24:23 +02:00
} else {
factor[j] = 0;
2023-07-05 21:24:23 +02:00
}
2023-04-30 00:02:16 +02:00
}
}
2023-04-30 00:02:16 +02:00
// Calculate the new dist vector so we can get pitch and yaw for this beam
VectorSubtract(org, ent.origin, dist);
2023-04-30 00:02:16 +02:00
// Set the pitch and the yaw based off this new vector
vectoangles(dist, angles);
2023-04-30 00:02:16 +02:00
// Fill in the ent fields
2023-07-05 21:24:23 +02:00
ent.hModel = b->hModel;
ent.scale = b->scale;
ent.renderfx = b->renderfx;
2023-04-30 00:02:16 +02:00
2023-07-05 21:24:23 +02:00
for (i = 0; i < 4; i++) {
ent.shaderRGBA[i] = b->shaderRGBA[i];
2023-07-05 21:24:23 +02:00
}
VectorCopy(ent.origin, ent.oldorigin);
AnglesToAxis(angles, ent.axis);
// Add in this beam to the ref
cgi.R_AddRefEntityToScene(&ent, ENTITYNUM_NONE);
}
}
2023-04-30 00:02:16 +02:00
2023-05-06 23:07:04 +02:00
void CG_AddBeams(void)
{
2023-07-05 21:24:23 +02:00
int i, ii;
beam_t *b;
beam_t *bNext;
vec3_t delta;
vec3_t angles;
vec3_t forward, left, up;
float length;
byte color[4];
float fade;
for (i = 0, b = cl_active_beams; b; i++, b = bNext) {
2023-05-07 17:45:07 +02:00
bNext = b->next;
2023-05-06 23:07:04 +02:00
// If no model is set or the endtime < current time remove the whole beam entity
2023-07-05 21:24:23 +02:00
if (!b->hModel || b->endtime < cg.time) {
2023-05-06 23:07:04 +02:00
RemoveBeamList(b->entity);
2023-07-05 21:24:23 +02:00
b->entity = ENTITYNUM_NONE;
2023-04-30 00:02:16 +02:00
b->endtime = 0;
2023-05-06 23:07:04 +02:00
if (b->next) {
b->next->prev = b->prev;
2023-04-30 00:02:16 +02:00
}
2023-05-06 23:07:04 +02:00
if (b->prev) {
b->prev->next = b->next;
}
if (b == cl_active_beams) {
cl_active_beams = b->next;
2023-04-30 00:02:16 +02:00
}
2023-05-06 23:07:04 +02:00
if (cl_free_beams) {
cl_free_beams->prev = b;
}
2023-07-05 21:24:23 +02:00
b->next = cl_free_beams;
b->prev = 0;
2023-05-06 23:07:04 +02:00
cl_free_beams = b;
continue;
}
// Fade the beam based on it's life
fade = (float)(b->endtime - cg.time) / (float)b->life;
2023-07-05 21:24:23 +02:00
if (b->flags & BEAM_FADE) {
for (ii = 0; ii < 4; ii++) {
color[ii] = b->shaderRGBA[ii] * fade;
2023-07-05 21:24:23 +02:00
}
} else {
for (ii = 0; ii < 4; ii++) {
color[ii] = b->shaderRGBA[ii];
2023-07-05 21:24:23 +02:00
}
}
2023-05-06 23:07:04 +02:00
// Check to see if the beam should be toggled
2023-07-05 21:24:23 +02:00
if (b->flags & BEAM_TOGGLE) {
if (cg.time > b->toggletime) {
2023-05-06 23:07:04 +02:00
b->active = !b->active;
2023-07-05 21:24:23 +02:00
if (b->flags & BEAM_RANDOM_TOGGLEDELAY) {
2023-05-06 23:07:04 +02:00
b->toggletime = cg.time + random() * b->toggledelay;
2023-07-05 21:24:23 +02:00
} else {
2023-05-06 23:07:04 +02:00
b->toggletime = cg.time + b->toggledelay;
2023-07-05 21:24:23 +02:00
}
2023-05-06 23:07:04 +02:00
}
}
2023-07-05 21:24:23 +02:00
if (!b->active) {
2023-05-06 23:07:04 +02:00
CG_AddBeamsFromList(b->entity, b->beamshader);
continue;
}
2023-07-05 21:24:23 +02:00
if ((b->flags & BEAM_PERSIST_EFFECT) && (b->update_time > cg.time)) {
2023-05-06 23:07:04 +02:00
CG_AddBeamsFromList(b->entity, b->beamshader);
continue;
}
b->update_time = cg.time + b->delay;
2023-04-30 00:02:16 +02:00
2023-07-05 21:24:23 +02:00
if (!b->active) {
2023-05-06 23:07:04 +02:00
continue;
2023-07-05 21:24:23 +02:00
}
2023-05-06 23:07:04 +02:00
2023-07-05 21:24:23 +02:00
if (b->flags & BEAM_USEMODEL) {
2023-04-30 00:02:16 +02:00
// Calculate the direction
2023-05-06 23:07:04 +02:00
VectorSubtract(b->start, b->end, delta);
// Calculate the beam length
length = VectorLength(delta);
// Get the perpendicular vectors to this vector
vectoangles(delta, angles);
AngleVectors(angles, forward, left, up);
CG_CreateModelBeam(b, b->start, delta, length, forward, left, up);
2023-07-05 21:24:23 +02:00
} else {
2023-05-06 23:07:04 +02:00
// Do a sphere effect
2023-07-05 21:24:23 +02:00
if (b->flags & BEAM_SPHERE_EFFECT) {
2023-05-06 23:07:04 +02:00
int k;
// Calculate the direction
VectorSubtract(b->start, b->end, delta);
// Calculate the beam length
length = VectorLength(delta);
2023-07-05 21:24:23 +02:00
for (k = 0; k < b->numspherebeams; k++) {
2023-05-06 23:07:04 +02:00
Vector offset(crandom(), crandom(), crandom());
Vector start(b->start + offset * b->sphereradius);
Vector end(b->start + offset * length);
2023-07-05 21:24:23 +02:00
CG_BuildRendererBeam(
start,
2023-05-06 23:07:04 +02:00
end,
b->max_offset,
b->numSubdivisions,
color,
b->beamshader,
b->scale,
b->overlap,
b->entity,
b->life,
b->flags,
b->alpha,
b->alphastep,
b->renderfx
);
}
2023-07-05 21:24:23 +02:00
} else if (b->flags & BEAM_INVERTED) {
2023-05-07 17:45:07 +02:00
vec3_t vCurrStart, vCurrEnd;
vec3_t vDir;
2023-07-05 21:24:23 +02:00
float fLength;
2023-05-07 17:45:07 +02:00
// Calculate the direction
2023-05-07 17:58:41 +02:00
VectorSubtract(b->end, b->start, vDir);
2023-05-07 17:45:07 +02:00
VectorMA(b->start, 1.0 - fade, vDir, vCurrEnd);
fLength = VectorNormalize(vDir);
VectorMA(vCurrEnd, -b->toggledelay, vDir, vCurrStart);
2023-07-05 21:24:23 +02:00
CG_BuildRendererBeam(
vCurrStart,
2023-05-07 17:45:07 +02:00
vCurrEnd,
b->max_offset,
b->numSubdivisions,
color,
b->beamshader,
b->scale,
b->overlap,
b->entity,
b->life,
b->flags,
b->alpha,
b->alphastep,
b->renderfx
);
2023-07-05 21:24:23 +02:00
} else if (b->flags & BEAM_INVERTED_FAST) {
2023-05-07 17:45:07 +02:00
vec3_t vCurrStart, vCurrEnd;
vec3_t vDir;
// Calculate the direction
2023-05-07 17:58:41 +02:00
VectorSubtract(b->end, b->start, vDir);
2023-05-07 17:45:07 +02:00
VectorMA(b->start, 1.0 - fade, vDir, vCurrEnd);
VectorNormalizeFast(vDir);
VectorMA(vCurrEnd, -b->toggledelay, vDir, vCurrStart);
2023-07-05 21:24:23 +02:00
CG_BuildRendererBeam_Fast(
vCurrStart,
2023-05-07 17:45:07 +02:00
vCurrEnd,
b->max_offset,
b->numSubdivisions,
color,
b->beamshader,
b->scale,
b->overlap,
b->entity,
b->life,
b->flags,
b->alpha,
b->alphastep,
b->renderfx
);
2023-07-05 21:24:23 +02:00
} else {
2023-05-06 23:07:04 +02:00
//cgi.DPrintf( "%2f %2f %2f\n", b->start[0],b->start[1],b->start[2] );
2023-07-05 21:24:23 +02:00
CG_BuildRendererBeam(
b->start,
2023-05-06 23:07:04 +02:00
b->end,
b->max_offset,
b->numSubdivisions,
color,
b->beamshader,
b->scale,
b->overlap,
b->entity,
b->life,
b->flags,
b->alpha,
b->alphastep,
b->renderfx
);
2023-04-30 00:02:16 +02:00
}
2023-07-05 21:24:23 +02:00
if (b->flags & BEAM_PERSIST_EFFECT) {
2023-05-06 23:07:04 +02:00
CG_AddBeamsFromList(b->entity, b->beamshader);
2023-07-05 21:24:23 +02:00
}
2023-05-06 23:07:04 +02:00
}
}
}
2023-04-30 00:02:16 +02:00
2023-07-05 21:24:23 +02:00
void CG_CreateBeam(
2023-07-10 23:07:57 +02:00
const vec3_t start,
const vec3_t dir,
int owner,
qhandle_t hModel,
float alpha,
float scale,
int flags,
float length,
int life,
qboolean create,
const vec3_t endpointvec,
int min_offset,
int max_offset,
int overlap,
int numSubdivisions,
int delay,
const char *beamshadername,
float modulate[4],
int numspherebeams,
float sphereradius,
int toggledelay,
float endalpha,
int renderfx,
const char *name
2023-05-06 23:07:04 +02:00
)
{
2023-07-05 21:24:23 +02:00
int i;
beam_t *b;
vec3_t end;
trace_t trace;
2023-05-06 23:07:04 +02:00
// Check to see if endpoint is specified
2023-07-05 21:24:23 +02:00
if (endpointvec) {
2023-05-06 23:07:04 +02:00
VectorCopy(endpointvec, end);
2023-07-05 21:24:23 +02:00
} else {
2023-05-06 23:07:04 +02:00
// Trace to find the endpoint with a shot
VectorMA(start, length, dir, end);
CG_Trace(&trace, start, vec3_origin, vec3_origin, end, 0, MASK_SHOT, false, true, "Create Beam");
VectorCopy(trace.endpos, end);
}
// If we aren't creating a beam, then search the beams for this one already active
2023-07-05 21:24:23 +02:00
if (!create) {
for (i = 0, b = cl_active_beams; b; i++, b = b->next) {
if (b->entity == owner) {
if (name && b->name == name) {
b->endtime = cg.time + life;
b->hModel = hModel;
b->scale = scale;
b->flags = flags;
b->overlap = overlap;
b->min_offset = min_offset;
b->max_offset = max_offset;
b->alpha = alpha;
b->beamshader = cgi.R_RegisterShader(beamshadername);
2023-05-06 23:07:04 +02:00
b->numSubdivisions = numSubdivisions;
2023-07-05 21:24:23 +02:00
b->delay = delay;
b->life = life;
b->numspherebeams = numspherebeams;
b->sphereradius = sphereradius;
b->renderfx = renderfx;
2023-05-06 23:07:04 +02:00
// take the alpha from the entity if less than 1, else grab it from the client commands version
2023-07-05 21:24:23 +02:00
if (alpha < 1) {
2023-05-06 23:07:04 +02:00
b->shaderRGBA[3] = alpha * 255;
2023-07-05 21:24:23 +02:00
} else {
2023-05-07 17:45:07 +02:00
b->shaderRGBA[3] = modulate[3] * 255;
2023-07-05 21:24:23 +02:00
}
2023-05-06 23:07:04 +02:00
// Modulation based off the color
2023-07-05 21:24:23 +02:00
for (i = 0; i < 3; i++) {
2023-05-07 17:45:07 +02:00
b->shaderRGBA[i] = modulate[i] * (float)b->shaderRGBA[3];
2023-07-05 21:24:23 +02:00
}
2023-05-06 23:07:04 +02:00
b->alphastep = ((float)(endalpha - alpha) / (float)b->numSubdivisions);
VectorCopy(start, b->start);
VectorCopy(end, b->end);
return;
}
2023-04-30 00:02:16 +02:00
}
2023-05-06 23:07:04 +02:00
}
}
2023-04-30 00:02:16 +02:00
2023-05-06 23:07:04 +02:00
// find a free beam
2023-07-05 21:24:23 +02:00
if (cl_free_beams) {
b = cl_free_beams;
2023-05-06 23:07:04 +02:00
cl_free_beams = cl_free_beams->next;
2023-04-30 00:02:16 +02:00
2023-05-06 23:07:04 +02:00
if (cl_active_beams) {
cl_active_beams->prev = b;
}
2023-07-05 21:24:23 +02:00
b->next = cl_active_beams;
b->prev = 0;
2023-05-06 23:07:04 +02:00
cl_active_beams = b;
2023-07-05 21:24:23 +02:00
b->entity = owner;
b->endtime = cg.time + life;
b->hModel = hModel;
b->alpha = alpha;
b->scale = scale;
b->flags = flags;
b->overlap = overlap;
b->min_offset = min_offset;
b->max_offset = max_offset;
b->beamshader = cgi.R_RegisterShader(beamshadername);
2023-05-06 23:07:04 +02:00
b->numSubdivisions = numSubdivisions;
2023-07-05 21:24:23 +02:00
b->delay = delay;
b->update_time = 0; //cg.time + delay;
b->life = life;
b->numspherebeams = numspherebeams;
b->sphereradius = sphereradius;
b->active = true;
b->toggledelay = toggledelay;
b->renderfx = renderfx;
b->name = name;
2023-05-06 23:07:04 +02:00
// take the alpha from the entity if less than 1, else grab it from the client commands version
2023-07-05 21:24:23 +02:00
if (alpha < 1) {
2023-05-06 23:07:04 +02:00
b->shaderRGBA[3] = alpha * 255;
2023-07-05 21:24:23 +02:00
} else {
b->shaderRGBA[3] = modulate[3] * 255;
2023-07-05 21:24:23 +02:00
}
2023-05-06 23:07:04 +02:00
// Modulation based off the color
2023-07-05 21:24:23 +02:00
for (i = 0; i < 3; i++) {
b->shaderRGBA[i] = modulate[i] * (float)b->shaderRGBA[3];
2023-07-05 21:24:23 +02:00
}
2023-05-06 23:07:04 +02:00
b->alphastep = ((float)(endalpha - alpha) / (float)b->numSubdivisions);
VectorCopy(start, b->start);
VectorCopy(end, b->end);
return;
}
return;
}
void CG_KillBeams(int entity_number)
{
2023-07-05 21:24:23 +02:00
int i;
beam_t *b;
2023-05-06 23:07:04 +02:00
2023-07-05 21:24:23 +02:00
for (i = 0, b = cl_beams; i < MAX_BEAMS; i++, b++) {
if (b->entity == entity_number) {
b->entity = ENTITYNUM_NONE;
2023-05-06 23:07:04 +02:00
b->endtime = 0;
if (b->next) {
b->next->prev = b->prev;
}
if (b->prev) {
b->prev->next = b->next;
}
if (b == cl_active_beams) {
cl_active_beams = b->next;
}
if (cl_free_beams) {
cl_free_beams->prev = b;
}
2023-07-05 21:24:23 +02:00
b->next = cl_free_beams;
2023-05-06 23:07:04 +02:00
cl_free_beams = b;
2023-07-05 21:24:23 +02:00
b->prev = 0;
2023-05-06 23:07:04 +02:00
}
}
}
2023-04-30 00:02:16 +02:00
2023-07-05 21:24:23 +02:00
void CG_RestartBeams(int timedelta)
2023-05-06 23:07:04 +02:00
{
2023-07-05 21:24:23 +02:00
int i;
beam_t *b;
2023-05-06 23:07:04 +02:00
2023-07-05 21:24:23 +02:00
for (i = 0, b = cl_beams; i < MAX_BEAMS; i++, b++) {
if (b->active && (b->update_time > cg.time)) {
2023-05-06 23:07:04 +02:00
b->endtime -= timedelta;
b->update_time -= timedelta;
2023-07-05 21:24:23 +02:00
if (b->toggletime) {
2023-05-06 23:07:04 +02:00
b->toggletime -= timedelta;
2023-07-05 21:24:23 +02:00
}
2023-05-06 23:07:04 +02:00
}
}
}
2023-04-30 00:02:16 +02:00
2023-07-05 21:24:23 +02:00
void CG_Rope(centity_t *cent)
2023-05-06 23:07:04 +02:00
{
Vector prevpt, currpt;
2023-07-05 21:24:23 +02:00
entityState_t *s1;
2023-05-06 23:07:04 +02:00
Vector top, mid, bottom, up, v1, v2;
Vector currpt1, currpt2, prevpt1, prevpt2;
2023-07-05 21:24:23 +02:00
const char *beamshadername;
2023-05-06 23:07:04 +02:00
int beamshader;
byte modulate[4];
float picH, length, endT;
int i, j;
polyVert_t points[4];
s1 = &cent->currentState;
2023-07-05 21:24:23 +02:00
top = s1->origin2;
mid = cent->lerpOrigin;
2023-05-06 23:07:04 +02:00
bottom = cent->lerpOrigin;
bottom.z -= s1->alpha;
// This is the top of the beam ent list, build up a renderer beam based on all the children
beamshadername = CG_ConfigString(CS_IMAGES + s1->surfaces[0]); // index for shader configstring
2023-07-05 21:24:23 +02:00
beamshader = cgi.R_RegisterShader(beamshadername);
2023-05-06 23:07:04 +02:00
picH = cgi.R_GetShaderHeight(beamshader);
2023-07-05 21:24:23 +02:00
for (i = 0; i < 4; i++) {
2023-05-06 23:07:04 +02:00
modulate[i] = cent->color[i] * 255;
2023-07-05 21:24:23 +02:00
}
2023-05-06 23:07:04 +02:00
// Generate the up vector
v1 = top - cg.refdef.vieworg;
v2 = bottom - cg.refdef.vieworg;
up.CrossProduct(v1, v2);
up.normalize();
// Set the color of the verts
2023-07-05 21:24:23 +02:00
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
2023-05-06 23:07:04 +02:00
points[i].modulate[j] = modulate[j];
}
}
// set the s coordinates
points[0].st[0] = 1;
points[1].st[0] = 1;
points[2].st[0] = 0;
points[3].st[0] = 0;
// Calculate the first points
prevpt1 = top + (up * s1->scale);
prevpt2 = top + (up * -s1->scale);
// draw the top section
currpt1 = mid + (up * s1->scale);
currpt2 = mid + (up * -s1->scale);
length = Vector(mid - top).length();
VectorCopy(currpt1, points[0].xyz);
VectorCopy(prevpt1, points[1].xyz);
VectorCopy(prevpt2, points[2].xyz);
VectorCopy(currpt2, points[3].xyz);
2023-07-05 21:24:23 +02:00
endT = length / picH;
2023-05-06 23:07:04 +02:00
points[0].st[1] = endT;
points[3].st[1] = endT;
points[1].st[1] = 0;
points[2].st[1] = 0;
// Add a segment to the list
cgi.R_AddPolyToScene(beamshader, 4, points, s1->renderfx);
2023-07-05 21:24:23 +02:00
if (s1->alpha > 0) {
2023-05-06 23:07:04 +02:00
// draw the bottom section
prevpt1 = currpt1;
prevpt2 = currpt2;
currpt1 = bottom + (up * s1->scale);
currpt2 = bottom + (up * -s1->scale);
VectorCopy(currpt1, points[0].xyz);
VectorCopy(prevpt1, points[1].xyz);
VectorCopy(prevpt2, points[2].xyz);
VectorCopy(currpt2, points[3].xyz);
// add on the rest of the rope
length += s1->alpha;
// use previous T value for the start of this segment
points[1].st[1] = endT;
points[2].st[1] = endT;
2023-07-05 21:24:23 +02:00
endT = length / picH;
2023-05-06 23:07:04 +02:00
points[0].st[1] = endT;
points[3].st[1] = endT;
// Add a segment to the list
cgi.R_AddPolyToScene(beamshader, 4, points, s1->renderfx);
}
}