mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-26 22:01:50 +00:00
22bb395305
Fixed svc_setangles and sv_bigcoords. Model code is now responsible for transforming traces instead of it being generic. This fixes rotating things getting stuck in players in hexen2. The renderer now generates a list of surfaces to draw. Backend now performs rotations/scaling per entity. This fixes sorting order, at least when not using realtime lights. Hidden items in the hexen2 inventory that you do not have. Added colourmapping for hexen2. Should be easier to click on menu items for hexen2. git-svn-id: https://svn.code.sf.net/p/fteqw/code/branches/wip@3602 fc73d0e0-1445-4013-8a0c-d673dee63da5
807 lines
22 KiB
C
807 lines
22 KiB
C
#include "quakedef.h"
|
|
|
|
#if defined(TERRAIN) && !defined(SERVERONLY) //fixme
|
|
#ifdef GLQUAKE
|
|
#include "glquake.h"
|
|
#endif
|
|
#include "shader.h"
|
|
|
|
//heightmaps work thusly:
|
|
//there is one raw heightmap file
|
|
//the file is split to 4*4 sections.
|
|
//each section is textured independantly (remember banshees are capped at 512 pixels)
|
|
//there's a detailtexture blended over the top to fake the detail.
|
|
//it's built into 16 seperate display lists, these display lists are individually culled, but the drivers are expected to optimise them too.
|
|
//Tei claims 14x speedup with a single display list. hopefully we can achieve the same speed by culling per-texture.
|
|
//we get 20->130
|
|
//perhaps we should build it with multitexture? (no - slower on ati)
|
|
|
|
#define SECTIONS 8
|
|
|
|
typedef struct {
|
|
char path[MAX_QPATH];
|
|
unsigned short *heights;
|
|
int terrainsize;
|
|
float terrainscale;
|
|
float heightscale;
|
|
int numsegs;
|
|
texid_t detailtexture;
|
|
texid_t textures[SECTIONS*SECTIONS];
|
|
int displaylist[SECTIONS*SECTIONS]; //display lists are famous for being stupidly fast with heightmaps.
|
|
unsigned short mins[SECTIONS*SECTIONS], maxs[SECTIONS*SECTIONS];
|
|
} heightmap_t;
|
|
|
|
#ifdef GLQUAKE
|
|
#define DISPLISTS
|
|
//#define MULTITEXTURE //ATI suck. I don't know about anyone else (this goes at 1/5th the speed).
|
|
|
|
void GL_DrawHeightmapModel (entity_t *e)
|
|
{
|
|
//a 512*512 heightmap
|
|
//will draw 2 tris per square, drawn twice for detail
|
|
//so a million triangles per frame if the whole thing is visible.
|
|
|
|
//with 130 to 180fps, display lists rule!
|
|
int x, y, vx, vy;
|
|
float subsize;
|
|
int minx, miny;
|
|
vec3_t mins, maxs;
|
|
model_t *m = e->model;
|
|
heightmap_t *hm = m->terrain;
|
|
|
|
if (e->model == cl.worldmodel)
|
|
{
|
|
qglColor4f(1, 1, 1, 1);
|
|
R_DrawSkyChain(NULL);
|
|
}
|
|
else
|
|
qglColor4fv(e->shaderRGBAf);
|
|
GL_CullFace(SHADER_CULL_BACK);
|
|
|
|
for (x = 0; x < hm->numsegs; x++)
|
|
{
|
|
mins[0] = (x+0)*hm->terrainscale*hm->terrainsize/hm->numsegs;
|
|
maxs[0] = (x+1)*hm->terrainscale*hm->terrainsize/hm->numsegs;
|
|
for (y = 0; y < hm->numsegs; y++)
|
|
{
|
|
mins[1] = (y+0)*hm->terrainscale*hm->terrainsize/hm->numsegs;
|
|
maxs[1] = (y+1)*hm->terrainscale*hm->terrainsize/hm->numsegs;
|
|
mins[2] = 0;//hm->mins[x+y*SECTIONS];
|
|
mins[2] = 65535;//hm->maxs[x+y*SECTIONS];
|
|
|
|
// if (!BoundsIntersect(mins, maxs, r_refdef.vieworg, r_refdef.vieworg))
|
|
// if (R_CullBox(mins, maxs))
|
|
// continue;
|
|
|
|
#ifdef DISPLISTS
|
|
if (!hm->displaylist[x+y*SECTIONS])
|
|
{
|
|
hm->displaylist[x+y*SECTIONS] = qglGenLists(1);
|
|
qglNewList(hm->displaylist[x+y*SECTIONS], GL_COMPILE_AND_EXECUTE);
|
|
#endif
|
|
#ifdef MULTITEXTURE
|
|
if (qglActiveTextureARB)
|
|
{
|
|
GL_MBind(0, hm->textures[x+y*SECTIONS]);
|
|
GL_MBind(1, hm->detailtexture);
|
|
qglEnable(GL_TEXTURE_2D);
|
|
|
|
subsize = hm->terrainsize/SECTIONS;
|
|
minx = x*subsize;
|
|
miny = y*subsize;
|
|
|
|
|
|
qglBegin(GL_QUADS);
|
|
for (vx = 0; vx < subsize; vx++)
|
|
{
|
|
for (vy = 0; vy < subsize; vy++)
|
|
{
|
|
qglMultiTexCoord2fARB(GL_TEXTURE0_ARB, vx/subsize, (vy+1)/subsize);
|
|
qglMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0, 1);
|
|
qglVertex3f((vx+minx)*hm->terrainscale, (vy+miny+1)*hm->terrainscale, hm->heights[vx + (vy+1)*hm->terrainsize]*hm->heightscale);
|
|
qglMultiTexCoord2fARB(GL_TEXTURE0_ARB, (vx+1)/subsize, (vy+1)/subsize);
|
|
qglMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1, 1);
|
|
qglVertex3f((vx+minx+1)*hm->terrainscale, (vy+miny+1)*hm->terrainscale, hm->heights[vx+1 + (vy+1)*hm->terrainsize]*hm->heightscale);
|
|
qglMultiTexCoord2fARB(GL_TEXTURE0_ARB, (vx+1)/subsize, vy/subsize);
|
|
qglMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1, 0);
|
|
qglVertex3f((vx+minx+1)*hm->terrainscale, (vy+miny)*hm->terrainscale, hm->heights[vx+1 + vy*hm->terrainsize]*hm->heightscale);
|
|
qglMultiTexCoord2fARB(GL_TEXTURE0_ARB, vx/subsize, vy/subsize);
|
|
qglMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0, 0);
|
|
qglVertex3f((vx+minx)*hm->terrainscale, (vy+miny)*hm->terrainscale, hm->heights[vx + vy*hm->terrainsize]*hm->heightscale);
|
|
}
|
|
}
|
|
qglEnd();
|
|
qglDisable(GL_TEXTURE_2D);
|
|
qglActiveTextureARB(GL_TEXTURE0_ARB);
|
|
}
|
|
else
|
|
#endif
|
|
{ //single texture
|
|
GL_Bind(hm->textures[x+y*SECTIONS]);
|
|
qglBegin(GL_QUADS);
|
|
subsize = hm->terrainsize/hm->numsegs;
|
|
minx = x*subsize;
|
|
miny = y*subsize;
|
|
for (vx = 0; vx < subsize; vx++)
|
|
{
|
|
for (vy = 0; vy < subsize; vy++)
|
|
{
|
|
qglTexCoord2f(vx/subsize, (vy+1)/subsize);
|
|
qglVertex3f((vx+minx)*hm->terrainscale, (vy+miny+1)*hm->terrainscale, hm->heights[vx+minx + (vy+miny+1)*hm->terrainsize]*hm->heightscale);
|
|
qglTexCoord2f((vx+1)/subsize, (vy+1)/subsize);
|
|
qglVertex3f((vx+minx+1)*hm->terrainscale, (vy+miny+1)*hm->terrainscale, hm->heights[vx+minx+1 + (vy+miny+1)*hm->terrainsize]*hm->heightscale);
|
|
qglTexCoord2f((vx+1)/subsize, vy/subsize);
|
|
qglVertex3f((vx+minx+1)*hm->terrainscale, (vy+miny)*hm->terrainscale, hm->heights[vx+minx+1 + (vy+miny)*hm->terrainsize]*hm->heightscale);
|
|
qglTexCoord2f(vx/subsize, vy/subsize);
|
|
qglVertex3f((vx+minx)*hm->terrainscale, (vy+miny)*hm->terrainscale, hm->heights[vx+minx + (vy+miny)*hm->terrainsize]*hm->heightscale);
|
|
}
|
|
}
|
|
qglEnd();
|
|
|
|
GL_Bind(hm->detailtexture);
|
|
qglEnable(GL_BLEND);
|
|
|
|
qglBlendFunc (GL_ZERO, GL_SRC_COLOR);
|
|
qglBegin(GL_QUADS);
|
|
for (vx = 0; vx < subsize; vx++)
|
|
{
|
|
for (vy = 0; vy < subsize; vy++)
|
|
{
|
|
qglTexCoord2f(0, 1);
|
|
qglVertex3f((vx+minx)*hm->terrainscale, (vy+miny+1)*hm->terrainscale, hm->heights[vx+minx + (vy+miny+1)*hm->terrainsize]*hm->heightscale);
|
|
qglTexCoord2f(1, 1);
|
|
qglVertex3f((vx+minx+1)*hm->terrainscale, (vy+miny+1)*hm->terrainscale, hm->heights[vx+minx+1 + (vy+miny+1)*hm->terrainsize]*hm->heightscale);
|
|
qglTexCoord2f(1, 0);
|
|
qglVertex3f((vx+minx+1)*hm->terrainscale, (vy+miny)*hm->terrainscale, hm->heights[vx+minx+1 + (vy+miny)*hm->terrainsize]*hm->heightscale);
|
|
qglTexCoord2f(0, 0);
|
|
qglVertex3f((vx+minx)*hm->terrainscale, (vy+miny)*hm->terrainscale, hm->heights[vx+minx + (vy+miny)*hm->terrainsize]*hm->heightscale);
|
|
}
|
|
}
|
|
qglEnd();
|
|
qglBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
qglDisable(GL_BLEND);
|
|
}
|
|
#ifdef DISPLISTS
|
|
qglEndList();
|
|
}
|
|
else
|
|
{
|
|
qglCallList(hm->displaylist[x+y*SECTIONS]);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
unsigned int Heightmap_PointContentsHM(heightmap_t *hm, float clipmipsz, vec3_t org)
|
|
{
|
|
float x, y;
|
|
float z, tz;
|
|
int sx, sy;
|
|
|
|
x = org[0]/hm->terrainscale;
|
|
y = org[1]/hm->terrainscale;
|
|
z = (org[2]+clipmipsz)/hm->heightscale;
|
|
|
|
if (z < 0)
|
|
return FTECONTENTS_SOLID;
|
|
if (z > 65535)
|
|
{
|
|
if (z > 65535+64 || clipmipsz) //top 64 units are sky
|
|
return FTECONTENTS_SOLID;
|
|
else
|
|
return FTECONTENTS_SKY;
|
|
}
|
|
if (x < 0)
|
|
{
|
|
if (x <= -1 || clipmipsz)
|
|
return FTECONTENTS_SOLID;
|
|
else
|
|
return FTECONTENTS_SKY;
|
|
}
|
|
if (y < 0)
|
|
{
|
|
if (x <= -1 || clipmipsz)
|
|
return FTECONTENTS_SOLID;
|
|
else
|
|
return FTECONTENTS_SKY;
|
|
}
|
|
if (x >= hm->terrainsize-1)
|
|
{
|
|
if (x >= hm->terrainsize || clipmipsz)
|
|
return FTECONTENTS_SOLID;
|
|
else
|
|
return FTECONTENTS_SKY;
|
|
}
|
|
if (y >= hm->terrainsize-1)
|
|
{
|
|
if (y >= hm->terrainsize || clipmipsz)
|
|
return FTECONTENTS_SOLID;
|
|
else
|
|
return FTECONTENTS_SKY;
|
|
}
|
|
|
|
sx = x; x-=sx;
|
|
sy = y; y-=sy;
|
|
|
|
//made of two triangles:
|
|
#if 1
|
|
if (x+y>1) //the 1, 1 triangle
|
|
{
|
|
float v1, v2, v3;
|
|
v3 = 1-y;
|
|
v2 = x+y-1;
|
|
v1 = 1-x;
|
|
//0, 1
|
|
//1, 1
|
|
//1, 0
|
|
tz = (hm->heights[(sx+0)+(sy+1)*hm->terrainsize]*v1 +
|
|
hm->heights[(sx+1)+(sy+1)*hm->terrainsize]*v2 +
|
|
hm->heights[(sx+1)+(sy+0)*hm->terrainsize]*v3);
|
|
}
|
|
else
|
|
{
|
|
float v1, v2, v3;
|
|
v1 = y;
|
|
v2 = x;
|
|
v3 = 1-y-x;
|
|
|
|
//0, 1
|
|
//1, 0
|
|
//0, 0
|
|
tz = (hm->heights[(sx+0)+(sy+1)*hm->terrainsize]*v1 +
|
|
hm->heights[(sx+1)+(sy+0)*hm->terrainsize]*v2 +
|
|
hm->heights[(sx+0)+(sy+0)*hm->terrainsize]*v3);
|
|
}
|
|
#else
|
|
{
|
|
float t, b;
|
|
//square?
|
|
//:(
|
|
|
|
t = (hm->heights[sx+sy*hm->terrainsize]*(1-x) + hm->heights[sx+1+sy*hm->terrainsize]*x);
|
|
b = (hm->heights[sx+(sy+1)*hm->terrainsize]*(1-x) + hm->heights[sx+1+(sy+1)*hm->terrainsize]*x);
|
|
tz = t*(1-y) + b*y;
|
|
}
|
|
#endif
|
|
if (z <= tz)
|
|
return FTECONTENTS_SOLID; //contained within
|
|
return FTECONTENTS_EMPTY;
|
|
}
|
|
|
|
unsigned int Heightmap_PointContents(model_t *model, vec3_t axis[3], vec3_t org)
|
|
{
|
|
heightmap_t *hm = model->terrain;
|
|
return Heightmap_PointContentsHM(hm, 0, org);
|
|
}
|
|
unsigned int Heightmap_NativeBoxContents(model_t *model, int hulloverride, int frame, vec3_t axis[3], vec3_t org, vec3_t mins, vec3_t maxs)
|
|
{
|
|
heightmap_t *hm = model->terrain;
|
|
return Heightmap_PointContentsHM(hm, mins[2], org);
|
|
}
|
|
|
|
void Heightmap_Normal(heightmap_t *hm, vec3_t org, vec3_t norm)
|
|
{
|
|
float x, y;
|
|
float z;
|
|
int sx, sy;
|
|
|
|
x = org[0]/hm->terrainscale;
|
|
y = org[1]/hm->terrainscale;
|
|
z = org[2];
|
|
|
|
sx = x; x-=sx;
|
|
sy = y; y-=sy;
|
|
|
|
if (x+y>1) //the 1, 1 triangle
|
|
{
|
|
//0, 1
|
|
//1, 1
|
|
//1, 0
|
|
x = hm->heights[(sx+1)+(sy+1)*hm->terrainsize] - hm->heights[(sx+0)+(sy+1)*hm->terrainsize];
|
|
y = hm->heights[(sx+1)+(sy+1)*hm->terrainsize] - hm->heights[(sx+1)+(sy+0)*hm->terrainsize];
|
|
}
|
|
else
|
|
{
|
|
//0, 1
|
|
//1, 0
|
|
//0, 0
|
|
x = hm->heights[(sx+1)+(sy+0)*hm->terrainsize] - hm->heights[(sx+0)+(sy+0)*hm->terrainsize];
|
|
y = hm->heights[(sx+0)+(sy+1)*hm->terrainsize] - hm->heights[(sx+0)+(sy+0)*hm->terrainsize];
|
|
}
|
|
|
|
norm[0] = (-x*hm->heightscale)/hm->terrainscale;
|
|
norm[1] = (-y*hm->heightscale)/hm->terrainscale;
|
|
norm[2] = 1.0f/(float)sqrt(norm[0]*norm[0] + norm[1]*norm[1] + 1);
|
|
norm[0] *= norm[2];
|
|
norm[1] *= norm[2];
|
|
VectorNormalize(norm);
|
|
}
|
|
|
|
#if 0
|
|
typedef struct {
|
|
vec3_t start;
|
|
vec3_t end;
|
|
vec3_t mins;
|
|
vec3_t maxs;
|
|
vec3_t impact;
|
|
heightmap_t *hm;
|
|
int contents;
|
|
} hmtrace_t;
|
|
#define Closestf(res,n,min,max) res = ((n>0)?min:max)
|
|
#define Closest(res,n,min,max) Closestf(res[0],n[0],min[0],max[0]);Closestf(res[1],n[1],min[1],max[1]);Closestf(res[2],n[2],min[2],max[2])
|
|
void Heightmap_Trace_Square(hmtrace_t *tr, int sx, int sy)
|
|
{
|
|
float normf = 0.70710678118654752440084436210485;
|
|
float pd, sd, ed, bd;
|
|
int tris, x, y;
|
|
vec3_t closest;
|
|
vec3_t point;
|
|
|
|
pd = normf*(x+y);
|
|
sd = normf*tr->start[0]+normf*tr->start[1];
|
|
ed = normf*tr->end[0]+normf*tr->end[1];
|
|
bd = normf*tr->maxs[0]+normf*tr->maxs[1]; //assume mins is this but negative
|
|
//see which of the two triangles in the square it travels over.
|
|
|
|
tris = sd<=pd || ed<=pd;
|
|
if (sd>=pd || ed>=pd)
|
|
tris |= 2;
|
|
|
|
point[0] = sx+1;
|
|
point[1] = sy;
|
|
point[2] = tr->hm->heights[sx+1+sy*tr->hm->terrainsize];
|
|
|
|
if (tris & 1)
|
|
{ //triangle with 0, 0
|
|
vec3_t norm;
|
|
float d1, d2, dc;
|
|
|
|
x = tr->hm->heights[(sx+1)+(sy+0)*tr->hm->terrainsize] - tr->hm->heights[(sx+0)+(sy+0)*tr->hm->terrainsize];
|
|
y = tr->hm->heights[(sx+0)+(sy+1)*tr->hm->terrainsize] - tr->hm->heights[(sx+0)+(sy+0)*tr->hm->terrainsize];
|
|
|
|
norm[0] = (-x*tr->hm->heightscale)/tr->hm->terrainscale;
|
|
norm[1] = (-y*tr->hm->heightscale)/tr->hm->terrainscale;
|
|
norm[2] = 1.0f/(float)sqrt(norm[0]*norm[0] + norm[1]*norm[1] + 1);
|
|
Closest(closest, norm, tr->mins, tr->maxs);
|
|
dc = DotProduct(norm, closest) - DotProduct(norm, point);
|
|
d1 = DotProduct(norm, tr->start) + dc;
|
|
d2 = DotProduct(norm, tr->end) + dc;
|
|
|
|
if (d1>=0 && d2<=0)
|
|
{ //intersects
|
|
tr->contents = FTECONTENTS_SOLID;
|
|
|
|
d1 = (d1-d2)/(d1+d2);
|
|
d2 = 1-d1;
|
|
|
|
tr->impact[0] = tr->end[0]*d1+tr->start[0]*d2;
|
|
tr->impact[1] = tr->end[1]*d1+tr->start[1]*d2;
|
|
tr->impact[2] = tr->end[2]*d1+tr->start[2]*d2;
|
|
}
|
|
}
|
|
if (tris & 2)
|
|
{ //triangle with 1, 1
|
|
vec3_t norm;
|
|
float d1, d2, dc;
|
|
norm[0] = (-x*tr->hm->heightscale)/tr->hm->terrainscale;
|
|
norm[1] = (-y*tr->hm->heightscale)/tr->hm->terrainscale;
|
|
norm[2] = 1.0f/(float)sqrt(norm[0]*norm[0] + norm[1]*norm[1] + 1);
|
|
Closest(closest, norm, tr->mins, tr->maxs);
|
|
dc = DotProduct(norm, closest) - DotProduct(norm, point);
|
|
d1 = DotProduct(norm, tr->start) + dc;
|
|
d2 = DotProduct(norm, tr->end) + dc;
|
|
|
|
if (d1>=0 && d2<=0)
|
|
{ //intersects
|
|
tr->contents = FTECONTENTS_SOLID;
|
|
|
|
d1 = (d1-d2)/(d1+d2);
|
|
d2 = 1-d1;
|
|
|
|
tr->impact[0] = tr->end[0]*d1+tr->start[0]*d2;
|
|
tr->impact[1] = tr->end[1]*d1+tr->start[1]*d2;
|
|
tr->impact[2] = tr->end[2]*d1+tr->start[2]*d2;
|
|
}
|
|
}
|
|
}
|
|
void Heightmap_Trace_Y(hmtrace_t *tr, int x, int min, int max)
|
|
{
|
|
int mid;
|
|
if (min == max)
|
|
{ //end
|
|
Heightmap_Trace_Square(tr, x, min);
|
|
return;
|
|
}
|
|
mid = ((max-min)>>1)+min;
|
|
if (tr->start[1] < min+tr->mins[1] && tr->end[1] < min+tr->mins[1])
|
|
{ //both on one size.
|
|
Heightmap_Trace_Y(tr, x, min, mid);
|
|
return;
|
|
}
|
|
if (tr->start[1] > max+tr->maxs[1] && tr->end[1] > max+tr->maxs[1])
|
|
{ //both on one size.
|
|
Heightmap_Trace_Y(tr, x, mid, max);
|
|
return;
|
|
}
|
|
|
|
//crosses this line.
|
|
if (tr->start[1] > tr->end[1])
|
|
{
|
|
Heightmap_Trace_Y(tr, x, min, mid);
|
|
if (!tr->contents)
|
|
Heightmap_Trace_Y(tr, x, mid, max);
|
|
}
|
|
else
|
|
{
|
|
Heightmap_Trace_Y(tr, x, mid, max);
|
|
if (!tr->contents)
|
|
Heightmap_Trace_Y(tr, x, min, mid);
|
|
}
|
|
}
|
|
void Heightmap_Trace_X(hmtrace_t *tr, int min, int max)
|
|
{
|
|
int mid;
|
|
if (min == max)
|
|
{ //end
|
|
//FIXME: we don't have to check ALL squares like this, we could use a much smaller range.
|
|
Heightmap_Trace_Y(tr, min, 0, tr->hm->terrainsize);
|
|
return;
|
|
}
|
|
mid = ((max-min)>>1)+min;
|
|
if (tr->start[0] < min+tr->mins[0] && tr->end[0] < min+tr->mins[0])
|
|
{ //both on one size.
|
|
Heightmap_Trace_X(tr, min, mid);
|
|
return;
|
|
}
|
|
if (tr->start[0] > max+tr->maxs[0] && tr->end[0] > max+tr->maxs[0])
|
|
{ //both on one size.
|
|
Heightmap_Trace_X(tr, mid, max);
|
|
return;
|
|
}
|
|
|
|
//crosses this line.
|
|
if (tr->start[0] > tr->end[0])
|
|
{
|
|
Heightmap_Trace_X(tr, min, mid);
|
|
if (!tr->contents)
|
|
Heightmap_Trace_X(tr, mid, max);
|
|
}
|
|
else
|
|
{
|
|
Heightmap_Trace_X(tr, mid, max);
|
|
if (!tr->contents)
|
|
Heightmap_Trace_X(tr, min, mid);
|
|
}
|
|
}
|
|
|
|
/*
|
|
Heightmap_TraceRecurse
|
|
Traces an arbitary box through a heightmap. (interface with outside)
|
|
|
|
Why is recursion good?
|
|
1: it is consistant with bsp models. :)
|
|
2: it allows us to use any size model we want
|
|
3: we don't have to work out the height of the terrain every X units, but can be more precise.
|
|
|
|
Obviously, we don't care all that much about 1
|
|
*/
|
|
qboolean Heightmap_Trace(model_t *model, int forcehullnum, int frame, vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, trace_t *trace)
|
|
{
|
|
hmtrace_t hmtrace;
|
|
hmtrace.hm = model->terrain;
|
|
|
|
hmtrace.start[0] = start[0]/hmtrace.hm->terrainscale;
|
|
hmtrace.start[1] = start[1]/hmtrace.hm->terrainscale;
|
|
hmtrace.start[2] = (start[2])/hmtrace.hm->heightscale;
|
|
hmtrace.end[0] = end[0]/hmtrace.hm->terrainscale;
|
|
hmtrace.end[1] = end[1]/hmtrace.hm->terrainscale;
|
|
hmtrace.end[2] = (end[2])/hmtrace.hm->heightscale;
|
|
hmtrace.mins[0] = mins[0]/hmtrace.hm->terrainscale;
|
|
hmtrace.mins[1] = mins[1]/hmtrace.hm->terrainscale;
|
|
hmtrace.mins[2] = (mins[2])/hmtrace.hm->heightscale;
|
|
hmtrace.maxs[0] = maxs[0]/hmtrace.hm->terrainscale;
|
|
hmtrace.maxs[1] = maxs[1]/hmtrace.hm->terrainscale;
|
|
hmtrace.maxs[2] = (maxs[2])/hmtrace.hm->heightscale;
|
|
|
|
//FIXME: we don't have to check ALL squares like this, we could use a much smaller range.
|
|
Heightmap_Trace_X(&hmtrace, 0, hmtrace.hm->terrainsize);
|
|
}
|
|
#else
|
|
/*
|
|
Heightmap_Trace
|
|
Traces a line through a heightmap, sampling the terrain at various different positions.
|
|
This is inprecise, only supports points (or vertical lines), and can often travel though sticky out bits of terrain.
|
|
*/
|
|
qboolean Heightmap_Trace(model_t *model, int forcehullnum, int frame, vec3_t axis[3], vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, trace_t *trace)
|
|
{
|
|
vec3_t org;
|
|
vec3_t dir;
|
|
int distleft;
|
|
float dist;
|
|
heightmap_t *hm = model->terrain;
|
|
memset(trace, 0, sizeof(trace_t));
|
|
|
|
if (Heightmap_PointContentsHM(hm, mins[2], start) == FTECONTENTS_SOLID)
|
|
{
|
|
trace->fraction = 0;
|
|
trace->startsolid = true;
|
|
trace->allsolid = true;
|
|
VectorCopy(start, trace->endpos);
|
|
return true;
|
|
}
|
|
VectorCopy(start, org);
|
|
VectorSubtract(end, start, dir);
|
|
dist = VectorNormalize(dir);
|
|
/* if (dist < 10)
|
|
{ //if less than 10 units, do at least 10 steps
|
|
VectorScale(dir, 10/dist, dir);
|
|
dist = 10;
|
|
}*/
|
|
distleft = dist;
|
|
|
|
while(distleft>=0)
|
|
{
|
|
VectorAdd(org, dir, org);
|
|
if (Heightmap_PointContentsHM(hm, mins[2], org) == FTECONTENTS_SOLID)
|
|
{ //go back to the previous safe spot
|
|
VectorSubtract(org, dir, org);
|
|
break;
|
|
}
|
|
distleft--;
|
|
}
|
|
|
|
trace->contents = Heightmap_PointContentsHM(hm, mins[2], end);
|
|
|
|
if (distleft < 0 && trace->contents != FTECONTENTS_SOLID)
|
|
{ //all the way
|
|
trace->fraction = 1;
|
|
VectorCopy(end, trace->endpos);
|
|
}
|
|
else
|
|
{ //we didn't get all the way there. :(
|
|
VectorSubtract(org, start, dir);
|
|
trace->fraction = Length(dir)/dist;
|
|
if (trace->fraction > 1)
|
|
trace->fraction = 1;
|
|
VectorCopy(org, trace->endpos);
|
|
}
|
|
|
|
trace->plane.normal[0] = 0;
|
|
trace->plane.normal[1] = 0;
|
|
trace->plane.normal[2] = 1;
|
|
Heightmap_Normal(model->terrain, trace->endpos, trace->plane.normal);
|
|
|
|
return trace->fraction != 1;
|
|
}
|
|
qboolean Heightmap_NativeTrace(struct model_s *model, int hulloverride, int frame, vec3_t axis[3], vec3_t p1, vec3_t p2, vec3_t mins, vec3_t maxs, unsigned int against, struct trace_s *trace)
|
|
{
|
|
return Heightmap_Trace(model, hulloverride, frame, axis, p1, p2, mins, maxs, trace);
|
|
}
|
|
|
|
#endif
|
|
unsigned int Heightmap_FatPVS (model_t *mod, vec3_t org, qbyte *pvsbuffer, unsigned int pvssize, qboolean add)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#ifndef CLIENTONLY
|
|
qboolean Heightmap_EdictInFatPVS (model_t *mod, struct pvscache_s *edict, qbyte *pvsdata)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void Heightmap_FindTouchedLeafs (model_t *mod, pvscache_t *ent, float *mins, float *maxs)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
void Heightmap_LightPointValues (model_t *mod, vec3_t point, vec3_t res_diffuse, vec3_t res_ambient, vec3_t res_dir)
|
|
{
|
|
}
|
|
void Heightmap_StainNode (mnode_t *node, float *parms)
|
|
{
|
|
}
|
|
void Heightmap_MarkLights (dlight_t *light, int bit, mnode_t *node)
|
|
{
|
|
}
|
|
|
|
qbyte *Heightmap_LeafnumPVS (model_t *model, int num, qbyte *buffer, unsigned int buffersize)
|
|
{
|
|
static qbyte heightmappvs = 255;
|
|
return &heightmappvs;
|
|
}
|
|
int Heightmap_LeafForPoint (model_t *model, vec3_t point)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
//Heightmap_NativeBoxContents
|
|
|
|
qboolean GL_LoadHeightmapModel (model_t *mod, void *buffer)
|
|
{
|
|
heightmap_t *hm;
|
|
unsigned short *heightmap;
|
|
int size;
|
|
int x;
|
|
|
|
float skyrotate;
|
|
vec3_t skyaxis;
|
|
char heightmapname[MAX_QPATH];
|
|
char detailtexname[MAX_QPATH];
|
|
char basetexname[MAX_QPATH];
|
|
char exttexname[MAX_QPATH];
|
|
char entfile[MAX_QPATH];
|
|
char skyname[MAX_QPATH];
|
|
float worldsize = 64;
|
|
float heightsize = 1/16;
|
|
int numsegs = 1;
|
|
|
|
*heightmapname = '\0';
|
|
*detailtexname = '\0';
|
|
*basetexname = '\0';
|
|
*exttexname = '\0';
|
|
*entfile = '\0';
|
|
strcpy(skyname, "night");
|
|
|
|
skyrotate = 0;
|
|
skyaxis[0] = 0;
|
|
skyaxis[1] = 0;
|
|
skyaxis[2] = 0;
|
|
|
|
buffer = COM_Parse(buffer);
|
|
if (strcmp(com_token, "terrain"))
|
|
{
|
|
Con_Printf(CON_ERROR "%s wasn't terrain map\n", mod->name); //shouldn't happen
|
|
return false;
|
|
}
|
|
|
|
if (qrenderer != QR_OPENGL)
|
|
return false;
|
|
|
|
for(;;)
|
|
{
|
|
buffer = COM_Parse(buffer);
|
|
if (!buffer)
|
|
break;
|
|
|
|
if (!strcmp(com_token, "heightmap"))
|
|
{
|
|
buffer = COM_Parse(buffer);
|
|
Q_strncpyz(heightmapname, com_token, sizeof(heightmapname));
|
|
}
|
|
else if (!strcmp(com_token, "detail"))
|
|
{
|
|
buffer = COM_Parse(buffer);
|
|
Q_strncpyz(detailtexname, com_token, sizeof(detailtexname));
|
|
}
|
|
else if (!strcmp(com_token, "texturegridbase"))
|
|
{
|
|
buffer = COM_Parse(buffer);
|
|
Q_strncpyz(basetexname, com_token, sizeof(basetexname));
|
|
}
|
|
else if (!strcmp(com_token, "texturegridext"))
|
|
{
|
|
buffer = COM_Parse(buffer);
|
|
Q_strncpyz(exttexname, com_token, sizeof(exttexname));
|
|
}
|
|
else if (!strcmp(com_token, "gridsize"))
|
|
{
|
|
buffer = COM_Parse(buffer);
|
|
worldsize = atof(com_token);
|
|
}
|
|
else if (!strcmp(com_token, "heightsize"))
|
|
{
|
|
buffer = COM_Parse(buffer);
|
|
heightsize = atof(com_token);
|
|
}
|
|
else if (!strcmp(com_token, "entfile"))
|
|
{
|
|
buffer = COM_Parse(buffer);
|
|
Q_strncpyz(entfile, com_token, sizeof(entfile));
|
|
}
|
|
else if (!strcmp(com_token, "skybox"))
|
|
{
|
|
buffer = COM_Parse(buffer);
|
|
Q_strncpyz(skyname, com_token, sizeof(skyname));
|
|
}
|
|
else if (!strcmp(com_token, "skyrotate"))
|
|
{
|
|
buffer = COM_Parse(buffer);
|
|
skyaxis[0] = atof(com_token);
|
|
buffer = COM_Parse(buffer);
|
|
skyaxis[1] = atof(com_token);
|
|
buffer = COM_Parse(buffer);
|
|
skyaxis[2] = atof(com_token);
|
|
skyrotate = VectorNormalize(skyaxis);
|
|
}
|
|
else if (!strcmp(com_token, "texturesegments"))
|
|
{
|
|
buffer = COM_Parse(buffer);
|
|
numsegs = atoi(com_token);
|
|
}
|
|
else
|
|
{
|
|
Con_Printf(CON_ERROR "%s, unrecognised token in terrain map\n", mod->name);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (numsegs > SECTIONS)
|
|
{
|
|
Con_Printf(CON_ERROR "%s, heightmap uses too many sections max is %i\n", mod->name, SECTIONS);
|
|
return false;
|
|
}
|
|
|
|
|
|
mod->type = mod_heightmap;
|
|
|
|
heightmap = (unsigned short*)COM_LoadTempFile(heightmapname);
|
|
|
|
size = sqrt(com_filesize/2);
|
|
if (size % numsegs)
|
|
{
|
|
Con_Printf(CON_ERROR "%s, heightmap is not a multiple of %i\n", mod->name, numsegs);
|
|
return false;
|
|
}
|
|
|
|
hm = Hunk_Alloc(sizeof(*hm) + com_filesize);
|
|
memset(hm, 0, sizeof(*hm));
|
|
hm->heights = (unsigned short*)(hm+1);
|
|
for (x = 0; x < size*size; x++)
|
|
{
|
|
hm->heights[x] = LittleShort(heightmap[x]);
|
|
}
|
|
memcpy(hm->heights, heightmap, com_filesize);
|
|
hm->terrainsize = size;
|
|
hm->terrainscale = worldsize;
|
|
hm->heightscale = heightsize;
|
|
hm->numsegs = numsegs;
|
|
|
|
mod->entities = COM_LoadHunkFile(entfile);
|
|
|
|
if (qrenderer != QR_NONE)
|
|
{
|
|
hm->detailtexture = R_LoadHiResTexture(detailtexname, "", IF_NOGAMMA);
|
|
|
|
for (x = 0; x < numsegs; x++)
|
|
{
|
|
int y;
|
|
|
|
for (y = 0; y < numsegs; y++)
|
|
{
|
|
hm->textures[x+y*SECTIONS] = R_LoadHiResTexture(va("%s%02ix%02i%s", basetexname, x, y, exttexname), "", IF_CLAMP|IF_NOGAMMA);
|
|
}
|
|
}
|
|
}
|
|
|
|
mod->funcs.Trace = Heightmap_Trace;
|
|
mod->funcs.PointContents = Heightmap_PointContents;
|
|
|
|
mod->funcs.NativeContents = Heightmap_NativeBoxContents;
|
|
mod->funcs.NativeTrace = Heightmap_NativeTrace;
|
|
|
|
mod->funcs.LightPointValues = Heightmap_LightPointValues;
|
|
mod->funcs.StainNode = Heightmap_StainNode;
|
|
mod->funcs.MarkLights = Heightmap_MarkLights;
|
|
|
|
mod->funcs.LeafnumForPoint = Heightmap_LeafForPoint;
|
|
mod->funcs.LeafPVS = Heightmap_LeafnumPVS;
|
|
|
|
#ifndef CLIENTONLY
|
|
mod->funcs.FindTouchedLeafs = Heightmap_FindTouchedLeafs;
|
|
mod->funcs.EdictInFatPVS = Heightmap_EdictInFatPVS;
|
|
mod->funcs.FatPVS = Heightmap_FatPVS;
|
|
#endif
|
|
/* mod->hulls[0].funcs.HullPointContents = Heightmap_PointContents;
|
|
mod->hulls[1].funcs.HullPointContents = Heightmap_PointContents;
|
|
mod->hulls[2].funcs.HullPointContents = Heightmap_PointContents;
|
|
mod->hulls[3].funcs.HullPointContents = Heightmap_PointContents;
|
|
*/
|
|
|
|
mod->terrain = hm;
|
|
|
|
return true;
|
|
}
|
|
#endif
|