mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-26 05:41:52 +00:00
fb214142a3
lots of hexen2 fixes fixed clipped decals again, still not using any... fixed zips over 2g rewrote bloom to use glsl. should be slightly more usable now. lots more hexen2 fixes git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@3957 fc73d0e0-1445-4013-8a0c-d673dee63da5
968 lines
24 KiB
C
968 lines
24 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];
|
|
char hmapname[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];
|
|
|
|
shader_t *skyshader;
|
|
shader_t *shader;
|
|
mesh_t mesh[SECTIONS*SECTIONS];
|
|
mesh_t *amesh[SECTIONS*SECTIONS];
|
|
mesh_t skymesh;
|
|
mesh_t *askymesh;
|
|
|
|
qboolean modified[SECTIONS*SECTIONS];
|
|
} heightmap_t;
|
|
|
|
#define DISPLISTS
|
|
//#define MULTITEXTURE //ATI suck. I don't know about anyone else (this goes at 1/5th the speed).
|
|
|
|
void GL_DrawHeightmapModel (batch_t **batches, 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, v;
|
|
float subsize;
|
|
int minx, miny;
|
|
vec3_t mins, maxs;
|
|
model_t *m = e->model;
|
|
heightmap_t *hm = m->terrain;
|
|
mesh_t *mesh;
|
|
batch_t *b;
|
|
|
|
if (e->model == cl.worldmodel)
|
|
{
|
|
b = BE_GetTempBatch();
|
|
if (b)
|
|
{
|
|
b->ent = e;
|
|
b->shader = hm->skyshader;
|
|
b->flags = 0;
|
|
b->mesh = &hm->askymesh;
|
|
b->mesh[0] = &hm->skymesh;
|
|
b->meshes = 1;
|
|
b->buildmeshes = NULL;
|
|
b->skin = &b->shader->defaulttextures;
|
|
b->texture = NULL;
|
|
// vbo = b->vbo = hm->vbo[x+y*SECTIONS];
|
|
b->vbo = NULL;
|
|
|
|
b->next = batches[b->shader->sort];
|
|
batches[b->shader->sort] = b;
|
|
}
|
|
}
|
|
|
|
subsize = hm->terrainsize/SECTIONS;
|
|
for (x = 0; x < hm->numsegs; x++)
|
|
{
|
|
mins[0] = (x+0)*subsize*hm->terrainscale;
|
|
maxs[0] = (x+1)*subsize*hm->terrainscale;
|
|
for (y = 0; y < hm->numsegs; y++)
|
|
{
|
|
mins[1] = (y+0)*subsize*hm->terrainscale;
|
|
maxs[1] = (y+1)*subsize*hm->terrainscale;
|
|
|
|
mesh = &hm->mesh[x+y*SECTIONS];
|
|
if (hm->modified[x+y*SECTIONS])
|
|
{
|
|
minx = x*subsize;
|
|
miny = y*subsize;
|
|
|
|
hm->modified[x+y*SECTIONS] = false;
|
|
|
|
hm->mins[x+y*SECTIONS] = 65536 * hm->heightscale;
|
|
hm->maxs[x+y*SECTIONS] = 0;
|
|
|
|
if (!mesh->xyz_array)
|
|
mesh->xyz_array = BZ_Malloc(sizeof(vecV_t) * (subsize+1)*(subsize+1));
|
|
if (!mesh->st_array)
|
|
mesh->st_array = BZ_Malloc(sizeof(vec2_t) * (subsize+1)*(subsize+1));
|
|
if (!mesh->lmst_array)
|
|
mesh->lmst_array = BZ_Malloc(sizeof(vec2_t) * (subsize+1)*(subsize+1));
|
|
mesh->numvertexes = 0;
|
|
/*64 quads across requires 65 verticies*/
|
|
for (vx = 0; vx <= subsize; vx++)
|
|
{
|
|
for (vy = 0; vy <= subsize; vy++)
|
|
{
|
|
v = mesh->numvertexes++;
|
|
mesh->xyz_array[v][0] = (vx+minx)*hm->terrainscale;
|
|
mesh->xyz_array[v][1] = (vy+miny)*hm->terrainscale;
|
|
mesh->xyz_array[v][2] = hm->heights[(vx+minx) + (vy+miny)*hm->terrainsize]*hm->heightscale;
|
|
|
|
if (hm->maxs[x+y*SECTIONS] < mesh->xyz_array[v][2])
|
|
hm->maxs[x+y*SECTIONS] = mesh->xyz_array[v][2];
|
|
if (hm->mins[x+y*SECTIONS] > mesh->xyz_array[v][2])
|
|
hm->mins[x+y*SECTIONS] = mesh->xyz_array[v][2];
|
|
|
|
mesh->st_array[v][0] = mesh->xyz_array[v][0] / 64;
|
|
mesh->st_array[v][1] = mesh->xyz_array[v][1] / 64;
|
|
|
|
mesh->lmst_array[v][0] = mesh->xyz_array[v][0] / 64;
|
|
mesh->lmst_array[v][1] = mesh->xyz_array[v][1] / 64;
|
|
}
|
|
}
|
|
|
|
if (!mesh->indexes)
|
|
mesh->indexes = BZ_Malloc(sizeof(index_t) * subsize*subsize*6);
|
|
|
|
mesh->numindexes = 0;
|
|
for (vx = 0; vx < subsize; vx++)
|
|
{
|
|
for (vy = 0; vy < subsize; vy++)
|
|
{
|
|
v = vx + vy*(subsize+1);
|
|
mesh->indexes[mesh->numindexes++] = v+0;
|
|
mesh->indexes[mesh->numindexes++] = v+1;
|
|
mesh->indexes[mesh->numindexes++] = v+subsize+1;
|
|
mesh->indexes[mesh->numindexes++] = v+1;
|
|
mesh->indexes[mesh->numindexes++] = v+1+subsize+1;
|
|
mesh->indexes[mesh->numindexes++] = v+subsize+1;
|
|
}
|
|
}
|
|
|
|
|
|
//GL_BuildVBO();
|
|
}
|
|
|
|
mins[2] = hm->mins[x+y*SECTIONS];
|
|
maxs[2] = hm->maxs[x+y*SECTIONS];
|
|
|
|
if (!BoundsIntersect(mins, maxs, r_refdef.vieworg, r_refdef.vieworg))
|
|
if (R_CullBox(mins, maxs))
|
|
continue;
|
|
|
|
b = BE_GetTempBatch();
|
|
if (!b)
|
|
continue;
|
|
b->ent = e;
|
|
b->shader = hm->shader;
|
|
b->flags = 0;
|
|
b->mesh = &hm->amesh[x+y*SECTIONS];
|
|
b->mesh[0] = mesh;
|
|
b->meshes = 1;
|
|
b->buildmeshes = NULL;
|
|
b->skin = &b->shader->defaulttextures;
|
|
b->texture = NULL;
|
|
// vbo = b->vbo = hm->vbo[x+y*SECTIONS];
|
|
b->vbo = NULL;
|
|
|
|
b->next = batches[b->shader->sort];
|
|
batches[b->shader->sort] = b;
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
vec3_t d1, d2;
|
|
|
|
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
|
|
d1[0] = hm->terrainscale;
|
|
d1[1] = 0;
|
|
d1[2] = hm->heights[(sx+1)+(sy+1)*hm->terrainsize] - hm->heights[(sx+0)+(sy+1)*hm->terrainsize];
|
|
d2[0] = 0;
|
|
d2[1] = hm->terrainscale;
|
|
d2[2] = hm->heights[(sx+1)+(sy+1)*hm->terrainsize] - hm->heights[(sx+1)+(sy+0)*hm->terrainsize];
|
|
}
|
|
else
|
|
{
|
|
//0, 1
|
|
//1, 0
|
|
//0, 0
|
|
d1[0] = hm->terrainscale;
|
|
d1[1] = 0;
|
|
d1[2] = hm->heights[(sx+0)+(sy+1)*hm->terrainsize] - hm->heights[(sx+0)+(sy+0)*hm->terrainsize];
|
|
d2[0] = 0;
|
|
d2[1] = hm->terrainscale;
|
|
d2[2] = hm->heights[(sx+1)+(sy+0)*hm->terrainsize] - hm->heights[(sx+0)+(sy+0)*hm->terrainsize];
|
|
}
|
|
|
|
CrossProduct(d1, d2, norm);
|
|
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, unsigned int contentmask, trace_t *trace)
|
|
{
|
|
vec3_t org;
|
|
vec3_t dir;
|
|
float 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 && dist)
|
|
{ //if less than 10 units, do at least 10 steps
|
|
VectorScale(dir, 1/10.0f, 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;
|
|
}
|
|
|
|
#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
|
|
|
|
enum
|
|
{
|
|
ter_reload,
|
|
ter_save,
|
|
ter_set,
|
|
ter_smooth,
|
|
ter_raise,
|
|
ter_lower
|
|
};
|
|
qboolean Heightmap_Edit(model_t *mod, int action, float *pos, float radius, float quant)
|
|
{
|
|
unsigned short *tmp;
|
|
heightmap_t *hm = mod->terrain;
|
|
int size;
|
|
int x, y, min[2], max[2];
|
|
float xd, yd, w;
|
|
vec2_t sc;
|
|
if (mod->type != mod_heightmap)
|
|
return false;
|
|
|
|
size = hm->terrainsize;
|
|
|
|
if (radius < 0.05)
|
|
radius = 0.05;
|
|
|
|
sc[0] = pos[0] / hm->terrainscale;
|
|
sc[1] = pos[1] / hm->terrainscale;
|
|
radius = radius / hm->terrainscale;
|
|
quant /= hm->heightscale;
|
|
|
|
min[0] = sc[0] - radius;
|
|
min[1] = sc[1] - radius;
|
|
max[0] = sc[0] + radius;
|
|
max[1] = sc[1] + radius;
|
|
|
|
if (min[0] < 0)
|
|
min[0] = 0;
|
|
if (min[1] < 0)
|
|
min[1] = 0;
|
|
|
|
if (max[0] > size)
|
|
max[0] = size;
|
|
if (max[1] < size)
|
|
max[1] = size;
|
|
|
|
switch(action)
|
|
{
|
|
case ter_reload:
|
|
tmp = (unsigned short*)COM_LoadTempFile(hm->hmapname);
|
|
if (tmp)
|
|
x = com_filesize/2;
|
|
else
|
|
x = 0;
|
|
if (x > size*size)
|
|
x = size*size;
|
|
while (x-- > 0)
|
|
{
|
|
hm->heights[x] = LittleShort(tmp[x]);
|
|
}
|
|
break;
|
|
case ter_save:
|
|
tmp = Hunk_TempAlloc(size*size*2);
|
|
for (x = 0; x < size*size; x++)
|
|
{
|
|
tmp[x] = LittleShort(hm->heights[x]);
|
|
}
|
|
COM_WriteFile(hm->hmapname, tmp, size*size*2);
|
|
break;
|
|
case ter_set:
|
|
for (x = min[0]; x < max[0]; x++)
|
|
{
|
|
for (y = min[1]; y < max[1]; y++)
|
|
{
|
|
xd = sc[0] - x;
|
|
yd = sc[1] - y;
|
|
if (sqrt(xd*xd+yd*yd) < radius)
|
|
{
|
|
hm->heights[x + y*size] = quant;
|
|
hm->modified[(int)(x/(hm->terrainscale)) + (int)(y/(hm->terrainscale))*SECTIONS] = true;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case ter_smooth:
|
|
case ter_raise:
|
|
for (x = min[0]; x < max[0]; x++)
|
|
{
|
|
for (y = min[1]; y < max[1]; y++)
|
|
{
|
|
xd = sc[0] - x;
|
|
yd = sc[1] - y;
|
|
w = sqrt(radius*radius - (xd*xd+yd*yd));
|
|
if (w > 0)
|
|
{
|
|
w *= quant/radius;
|
|
hm->heights[x + y*size] += w;
|
|
hm->modified[(int)(x/(hm->terrainscale)) + (int)(y/(hm->terrainscale))*SECTIONS] = true;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case ter_lower:
|
|
for (x = min[0]; x < max[0]; x++)
|
|
{
|
|
for (y = min[1]; y < max[1]; y++)
|
|
{
|
|
xd = sc[0] - x;
|
|
yd = sc[1] - y;
|
|
w = sqrt(radius*radius - (xd*xd+yd*yd));
|
|
if (w > 0)
|
|
{
|
|
w *= quant/radius;
|
|
/*don't drop below 0*/
|
|
if (hm->heights[x + y*size]-w < 0)
|
|
hm->heights[x + y*size] = 0;
|
|
else
|
|
hm->heights[x + y*size] -= w;
|
|
hm->modified[(int)(x/(hm->terrainscale)) + (int)(y/(hm->terrainscale))*SECTIONS] = true;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
qboolean GL_LoadHeightmapModel (model_t *mod, void *buffer)
|
|
{
|
|
heightmap_t *hm;
|
|
unsigned short *heightmap;
|
|
int size;
|
|
int x, y;
|
|
|
|
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);
|
|
if (!heightmap)
|
|
{
|
|
size = 1024;
|
|
heightmap = Hunk_TempAlloc(size*size*2);
|
|
|
|
for (x = 0; x < size; x++)
|
|
for (y = 0; y < size; y++)
|
|
{
|
|
heightmap[x+y*size] = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
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) + size*size*2);
|
|
memset(hm, 0, sizeof(*hm));
|
|
Q_strncpyz(hm->hmapname, heightmapname, sizeof(hm->hmapname));
|
|
hm->heights = (unsigned short*)(hm+1);
|
|
for (x = 0; x < size*size; x++)
|
|
{
|
|
hm->heights[x] = LittleShort(heightmap[x]);
|
|
}
|
|
hm->terrainsize = size;
|
|
hm->terrainscale = worldsize;
|
|
hm->heightscale = heightsize;
|
|
hm->numsegs = numsegs;
|
|
|
|
hm->shader = R_RegisterShader(basetexname,
|
|
"{\n"
|
|
"{\n"
|
|
"map maps/test/ground.jpg\n"
|
|
"}\n"
|
|
"}\n"
|
|
);
|
|
|
|
hm->skyshader = R_RegisterCustom(va("skybox_%s", skyname), Shader_DefaultSkybox, NULL);
|
|
|
|
|
|
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);
|
|
hm->modified[x+y*SECTIONS] = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
mod->funcs.NativeTrace = Heightmap_Trace;
|
|
mod->funcs.PointContents = Heightmap_PointContents;
|
|
|
|
mod->funcs.NativeContents = Heightmap_NativeBoxContents;
|
|
|
|
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
|