763 lines
18 KiB
C++
763 lines
18 KiB
C++
|
|
#include "std.h"
|
|
#include "q3bsprep.h"
|
|
|
|
/* Quake3 File format types */
|
|
|
|
#pragma pack(push,1)
|
|
|
|
struct q3_plane {
|
|
Vector normal;
|
|
float distance;
|
|
};
|
|
|
|
struct q3_tex {
|
|
char name[64];
|
|
int flags, contents;
|
|
};
|
|
|
|
struct q3_vertex {
|
|
Vector coords;
|
|
float tex_coords[4];
|
|
Vector normal;
|
|
unsigned char color[4];
|
|
};
|
|
|
|
struct q3_node {
|
|
int plane;
|
|
int children[2];
|
|
int mins[3];
|
|
int maxs[3];
|
|
};
|
|
|
|
struct q3_face {
|
|
int texture;
|
|
int effect;
|
|
int type;
|
|
int vertex;
|
|
int n_verts;
|
|
int meshvert;
|
|
int n_meshverts;
|
|
int lm_index;
|
|
int lm_start[2];
|
|
int lm_size[2];
|
|
float lm_origin[3];
|
|
float lm_vecs[2][3];
|
|
float normal[3];
|
|
int patch_size[2];
|
|
};
|
|
|
|
struct q3_leaf {
|
|
int cluster;
|
|
int area;
|
|
int mins[3];
|
|
int maxs[3];
|
|
int leafface;
|
|
int n_leaffaces;
|
|
int leafbrush;
|
|
int n_leafbrushes;
|
|
};
|
|
|
|
struct q3_brush {
|
|
int brushside;
|
|
int n_brushsides;
|
|
int texture;
|
|
};
|
|
|
|
struct q3_brushside {
|
|
int plane;
|
|
int texture;
|
|
};
|
|
|
|
struct q3_direntry {
|
|
union {
|
|
int offset;
|
|
void *lump;
|
|
};
|
|
int length;
|
|
};
|
|
|
|
struct q3_header {
|
|
unsigned magic;
|
|
int version;
|
|
q3_direntry dir[17];
|
|
};
|
|
|
|
#pragma pack(pop)
|
|
|
|
/* Loading reps */
|
|
struct Surf {
|
|
Q3BSPSurf *surf;
|
|
int texture, lm_index;
|
|
vector<int> verts, tris;
|
|
};
|
|
|
|
struct FaceCmp {
|
|
bool operator()(const q3_face *a, const q3_face *b)const {
|
|
if (a->texture < b->texture) return true;
|
|
if (b->texture < a->texture) return false;
|
|
if (a->lm_index < b->lm_index) return true;
|
|
return false;
|
|
}
|
|
};
|
|
|
|
typedef map<q3_face*, Surf*, FaceCmp> FaceMap;
|
|
|
|
/* render reps */
|
|
|
|
struct Q3BSPFace;
|
|
|
|
struct Q3BSPSurf {
|
|
Brush brush;
|
|
gxMesh *mesh;
|
|
vector<Q3BSPFace*> r_faces;
|
|
int texture, lm_index;
|
|
};
|
|
|
|
struct Q3BSPFace {
|
|
union {
|
|
Surf *t_surf;
|
|
Q3BSPSurf *surf;
|
|
};
|
|
int vert, n_verts, tri, n_tris;
|
|
};
|
|
|
|
struct Q3BSPBrush {
|
|
vector<Plane> planes;
|
|
};
|
|
|
|
struct Q3BSPLeaf {
|
|
int cluster;
|
|
Box box;
|
|
vector<Q3BSPFace*> faces;
|
|
};
|
|
|
|
struct Q3BSPNode {
|
|
Box box;
|
|
Plane plane;
|
|
Q3BSPNode *nodes[2];
|
|
Q3BSPLeaf *leafs[2];
|
|
|
|
~Q3BSPNode() { delete nodes[0]; delete nodes[1]; delete leafs[0]; delete leafs[1]; }
|
|
};
|
|
|
|
static q3_header header;
|
|
static FaceMap face_map;
|
|
static vector<Surf*> t_surfs;
|
|
static vector<q3_vertex> p_verts; //patch vertices
|
|
static vector<Vector> p_coll_verts;
|
|
static vector<MeshCollider::Triangle> coll_tris;
|
|
|
|
static float gamma_adj;
|
|
|
|
static Vector r_eye;
|
|
static int r_cluster;
|
|
static Frustum r_frustum;
|
|
static Vector r_frustedges[12];
|
|
static map<int, Q3BSPFace*> q3face_map;
|
|
|
|
extern gxScene *gx_scene;
|
|
extern gxRuntime *gx_runtime;
|
|
extern gxGraphics *gx_graphics;
|
|
|
|
//#define SWAPTRIS
|
|
Vector static tf(const Vector &v) {
|
|
return Vector(-v.y, v.z, v.x);
|
|
}
|
|
|
|
#ifdef BETA
|
|
static void debuglog(const string &t) {
|
|
gx_runtime->debugLog(t.c_str());
|
|
}
|
|
#else
|
|
static void debuglog(const string &t) {}
|
|
#endif
|
|
|
|
static Surf *findSurf(q3_face *f) {
|
|
FaceMap::const_iterator it = face_map.find(f);
|
|
if (it != face_map.end()) return it->second;
|
|
Surf *s = new Surf;
|
|
s->texture = f->texture;
|
|
s->lm_index = f->lm_index;
|
|
face_map.insert(make_pair(f, s));
|
|
t_surfs.push_back(s);
|
|
return s;
|
|
}
|
|
|
|
void Q3BSPRep::createTextures() {
|
|
int n_texs = header.dir[1].length / sizeof(q3_tex);
|
|
q3_tex *q3tex = (q3_tex*)header.dir[1].lump;
|
|
for (int k = 0; k < n_texs; ++k) {
|
|
string t = string(q3tex->name);
|
|
char fl[32], co[32];
|
|
_itoa(q3tex->flags, fl, 16);
|
|
_itoa(q3tex->contents, co, 16);
|
|
debuglog(t + ", flags=0x" + fl + ", contents=0x" + co);
|
|
Texture tex(t + ".tga", 1);
|
|
if (!tex.getCanvas(0)) {
|
|
tex = Texture(t + ".jpg", 1);
|
|
if (!tex.getCanvas(0)) {
|
|
tex = Texture(t + ".png", 1);
|
|
if (!tex.getCanvas(0)) {
|
|
tex = Texture(t + ".dds", 1);
|
|
if (!tex.getCanvas(0)) debuglog("Failed!");
|
|
}
|
|
}
|
|
}
|
|
tex.setFlags(1);
|
|
textures.push_back(tex);
|
|
++q3tex;
|
|
}
|
|
}
|
|
|
|
void Q3BSPRep::createLightMaps() {
|
|
int n_lmaps = header.dir[14].length / (128 * 128 * 3);
|
|
unsigned char *rgb = (unsigned char*)header.dir[14].lump;
|
|
unsigned char adj[256];
|
|
int k;
|
|
for (k = 0; k < 256; ++k) adj[k] = pow(k / 255.0f, gamma_adj)*255.0f;
|
|
|
|
for (k = 0; k < n_lmaps; ++k) {
|
|
Texture tex(128, 128, 1 + 8 + 16 + 32, 1);
|
|
tex.setBlend(gxScene::BLEND_ADD);
|
|
gxCanvas *c = tex.getCanvas(0);
|
|
c->lock();
|
|
for (int y = 0; y < 128; ++y) {
|
|
for (int x = 0; x < 128; ++x) {
|
|
unsigned argb = 0xff000000 | (adj[rgb[0]] << 16) | (adj[rgb[1]] << 8) | adj[rgb[2]];
|
|
c->setPixelFast(x, y, argb);
|
|
rgb += 3;
|
|
}
|
|
}
|
|
c->unlock();
|
|
light_maps.push_back(tex);
|
|
}
|
|
}
|
|
|
|
void Q3BSPRep::createVis() {
|
|
int *vis = (int*)header.dir[16].lump;
|
|
int n_vecs = *vis++;
|
|
vis_sz = *vis++;
|
|
debuglog("vis: " + itoa(n_vecs) + "," + itoa(vis_sz));
|
|
vis_data = new char[n_vecs*vis_sz];
|
|
memcpy(vis_data, vis, n_vecs*vis_sz);
|
|
}
|
|
|
|
void Q3BSPRep::createCollider() {
|
|
vector<MeshCollider::Vertex> coll_verts;
|
|
int n_verts = header.dir[10].length / sizeof(q3_vertex);
|
|
q3_vertex *t = (q3_vertex*)header.dir[10].lump;
|
|
MeshCollider::Vertex cv;
|
|
int k;
|
|
for (k = 0; k < n_verts; ++k) {
|
|
cv.coords = tf(t->coords);
|
|
coll_verts.push_back(cv);
|
|
++t;
|
|
}
|
|
for (k = 0; k < p_coll_verts.size(); ++k) {
|
|
cv.coords = p_coll_verts[k];
|
|
coll_verts.push_back(cv);
|
|
}
|
|
#ifdef SWAPTRIS
|
|
for (k = 0; k < coll_tris.size(); ++k) {
|
|
std::swap(coll_tris[k].verts[1], coll_tris[k].verts[2]);
|
|
}
|
|
#endif
|
|
collider = new MeshCollider(coll_verts, coll_tris);
|
|
p_coll_verts.clear();
|
|
coll_verts.clear();
|
|
coll_tris.clear();
|
|
}
|
|
|
|
void Q3BSPRep::createSurfs() {
|
|
int k;
|
|
for (k = 0; k < t_surfs.size(); ++k) {
|
|
Surf *s = t_surfs[k];
|
|
gxMesh *mesh = gx_graphics->createMesh(s->verts.size(), s->tris.size() / 3, 0);
|
|
|
|
mesh->lock(true);
|
|
int j;
|
|
for (j = 0; j < s->verts.size(); ++j) {
|
|
q3_vertex *t;
|
|
int n = s->verts[j];
|
|
if (n >= 0) {
|
|
t = (q3_vertex*)header.dir[10].lump + n;
|
|
} else {
|
|
t = &p_verts[-n - 1];
|
|
}
|
|
float tex_coords[2][2] = { {t->tex_coords[2],t->tex_coords[3]},{t->tex_coords[0],t->tex_coords[1]} };
|
|
unsigned argb = 0xff000000 | (t->color[0] << 16) | (t->color[1] << 8) | t->color[2];
|
|
mesh->setVertex(j, tf(t->coords), tf(t->normal), argb, tex_coords);
|
|
}
|
|
for (j = 0; j < s->tris.size(); j += 3) {
|
|
#ifdef SWAPTRIS
|
|
mesh->setTriangle(j / 3, s->tris[j], s->tris[j + 2], s->tris[j + 1]);
|
|
#else
|
|
mesh->setTriangle(j / 3, s->tris[j], s->tris[j + 1], s->tris[j + 2]);
|
|
#endif
|
|
}
|
|
mesh->unlock();
|
|
|
|
Q3BSPSurf *surf = new Q3BSPSurf;
|
|
surf->texture = s->texture;
|
|
surf->lm_index = s->lm_index;
|
|
surf->mesh = mesh;
|
|
surfs.push_back(surf);
|
|
s->surf = surf;
|
|
}
|
|
for (k = 0; k < faces.size(); ++k) {
|
|
Q3BSPFace *f = faces[k];
|
|
f->surf = f->t_surf->surf;
|
|
f->tri /= 3; f->n_tris /= 3;
|
|
}
|
|
for (k = 0; k < t_surfs.size(); ++k) {
|
|
delete t_surfs[k];
|
|
}
|
|
face_map.clear();
|
|
t_surfs.clear();
|
|
p_verts.clear();
|
|
}
|
|
|
|
static void average(const q3_vertex &a, const q3_vertex &b, q3_vertex *c) {
|
|
c->coords = (a.coords + b.coords)*.5f;
|
|
c->normal = (a.normal + b.normal)*.5f;
|
|
for (int k = 0; k < 4; ++k) {
|
|
c->color[k] = (a.color[k] + b.color[k] + 1) / 2;
|
|
c->tex_coords[k] = (a.tex_coords[k] + b.tex_coords[k])*.5f;
|
|
}
|
|
}
|
|
|
|
static void subdivide(vector<q3_vertex> &verts, int level, int index, int step) {
|
|
if (!level) {
|
|
q3_vertex t1, t2;
|
|
average(verts[index], verts[index + step], &t1);
|
|
average(verts[index + step], verts[index + step * 2], &t2);
|
|
average(t1, t2, &verts[index + step]);
|
|
return;
|
|
}
|
|
average(verts[index], verts[index + step], &verts[index + step / 2]);
|
|
average(verts[index + step], verts[index + step * 2], &verts[index + step + step / 2]);
|
|
average(verts[index + step / 2], verts[index + step + step / 2], &verts[index + step]);
|
|
subdivide(verts, level - 1, index, step / 2);
|
|
subdivide(verts, level - 1, index + step, step / 2);
|
|
}
|
|
|
|
static void patchFace(Q3BSPFace *face, q3_face *q3face, bool draw, bool solid, int level) {
|
|
|
|
int k, x, y;
|
|
vector<q3_vertex> verts;
|
|
|
|
if (draw) {
|
|
int step = 1 << level;
|
|
int size_x = (q3face->patch_size[0] - 1)*step + 1;
|
|
int size_y = (q3face->patch_size[1] - 1)*step + 1;
|
|
verts.resize(size_x*size_y);
|
|
|
|
//seed initial verts
|
|
q3_vertex *t = (q3_vertex*)header.dir[10].lump + q3face->vertex;
|
|
for (y = 0; y < size_y; y += step) {
|
|
for (x = 0; x < size_x; x += step) {
|
|
verts[y*size_x + x] = *t++;
|
|
}
|
|
}
|
|
//subdivide!
|
|
for (y = 0; y < size_y; y += step) {
|
|
for (x = 0; x < size_x - 1; x += step * 2) {
|
|
subdivide(verts, level, y*size_x + x, step);
|
|
}
|
|
}
|
|
for (x = 0; x < size_x; ++x) {
|
|
for (y = 0; y < size_y - 1; y += step * 2) {
|
|
subdivide(verts, level, y*size_x + x, size_x*step);
|
|
}
|
|
}
|
|
|
|
Surf *surf = face->t_surf;
|
|
int vert = surf->verts.size() - face->vert;
|
|
|
|
//generate patch verts
|
|
for (k = 0; k < size_x*size_y; ++k) {
|
|
p_verts.push_back(verts[k]);
|
|
surf->verts.push_back(p_verts.size()); // Why was there a - here
|
|
}
|
|
face->n_verts += size_x*size_y;
|
|
|
|
//generate tris...
|
|
for (y = 0; y < size_y - 1; ++y) {
|
|
int n = y*size_x + vert;
|
|
for (x = 0; x < size_x - 1; ++n, ++x) {
|
|
surf->tris.push_back(n);
|
|
surf->tris.push_back(n + size_x);
|
|
surf->tris.push_back(n + 1);
|
|
surf->tris.push_back(n + size_x + 1);
|
|
surf->tris.push_back(n + 1);
|
|
surf->tris.push_back(n + size_x);
|
|
}
|
|
}
|
|
face->n_tris += (size_x - 1)*(size_y - 1) * 6;
|
|
}
|
|
|
|
if (solid) {
|
|
vector<q3_vertex> verts;
|
|
int step = 1;
|
|
int size_x = q3face->patch_size[0];
|
|
int size_y = q3face->patch_size[1];
|
|
verts.resize(size_x*size_y);
|
|
|
|
//seed initial verts
|
|
q3_vertex *t = (q3_vertex*)header.dir[10].lump + q3face->vertex;
|
|
for (k = 0; k < size_x*size_y; ++k) verts[k] = *t++;
|
|
//subdivide!
|
|
for (y = 0; y < size_y; y += step) {
|
|
for (x = 0; x < size_x - 1; x += step * 2) {
|
|
subdivide(verts, 0, y*size_x + x, step);
|
|
}
|
|
}
|
|
for (x = 0; x < size_x; ++x) {
|
|
for (y = 0; y < size_y - 1; y += step * 2) {
|
|
subdivide(verts, 0, y*size_x + x, size_x*step);
|
|
}
|
|
}
|
|
|
|
int vert = header.dir[10].length / sizeof(q3_vertex) + p_coll_verts.size();
|
|
|
|
//generate patch verts
|
|
for (k = 0; k < size_x*size_y; ++k) p_coll_verts.push_back(tf(verts[k].coords));
|
|
|
|
MeshCollider::Triangle ct;
|
|
ct.surface = 0; ct.index = 0;
|
|
//generate tris...
|
|
for (y = 0; y < size_y - 1; ++y) {
|
|
int n = y*size_x + vert;
|
|
for (x = 0; x < size_x - 1; ++n, ++x) {
|
|
ct.verts[0] = n;
|
|
ct.verts[1] = n + size_x;
|
|
ct.verts[2] = n + 1;
|
|
coll_tris.push_back(ct);
|
|
ct.verts[0] = n + size_x + 1;
|
|
ct.verts[1] = n + 1;
|
|
ct.verts[2] = n + size_x;
|
|
coll_tris.push_back(ct);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void meshFace(Q3BSPFace *face, q3_face *q3face, bool draw, bool solid) {
|
|
static map<int, int> vert_map;
|
|
vert_map.clear();
|
|
int *meshverts = (int*)header.dir[11].lump + q3face->meshvert;
|
|
MeshCollider::Triangle ct;
|
|
ct.surface = 0; ct.index = 0;
|
|
for (int j = 0; j < q3face->n_meshverts; j += 3) {
|
|
for (int q = 0; q < 3; ++q) {
|
|
int n = meshverts[j + q] + q3face->vertex;
|
|
if (draw) {
|
|
if (!vert_map.count(n)) {
|
|
vert_map[n] = face->t_surf->verts.size() - face->vert;
|
|
face->t_surf->verts.push_back(n);
|
|
++face->n_verts;
|
|
}
|
|
face->t_surf->tris.push_back(vert_map[n]);
|
|
++face->n_tris;
|
|
}
|
|
ct.verts[q] = n;
|
|
}
|
|
if (solid) coll_tris.push_back(ct);
|
|
}
|
|
}
|
|
|
|
static Q3BSPBrush *createBrush(int n) {
|
|
Q3BSPBrush *brush = new Q3BSPBrush;
|
|
q3_brush *q3brush = (q3_brush*)header.dir[8].lump + n;
|
|
q3_brushside *q3brushside = (q3_brushside*)header.dir[9].lump + q3brush->brushside;
|
|
Plane p;
|
|
for (int j = 0; j < q3brush->n_brushsides; ++j) {
|
|
q3_plane *q3plane = (q3_plane*)header.dir[2].lump + q3brushside[j].plane;
|
|
p.n = tf(q3plane->normal);
|
|
p.d = -q3plane->distance;
|
|
brush->planes.push_back(p);
|
|
}
|
|
return brush;
|
|
}
|
|
|
|
Q3BSPLeaf *Q3BSPRep::createLeaf(int n) {
|
|
q3_leaf *q3leaf = (q3_leaf*)header.dir[4].lump + n;
|
|
|
|
Q3BSPLeaf *leaf = new Q3BSPLeaf;
|
|
|
|
leaf->cluster = q3leaf->cluster;
|
|
|
|
Vector mins(q3leaf->mins[0], q3leaf->mins[1], q3leaf->mins[2]);
|
|
Vector maxs(q3leaf->maxs[0], q3leaf->maxs[1], q3leaf->maxs[2]);
|
|
leaf->box = Box(tf(mins));
|
|
leaf->box.update(tf(maxs));
|
|
int *leaffaces = (int*)header.dir[5].lump + q3leaf->leafface;
|
|
|
|
for (int k = 0; k < q3leaf->n_leaffaces; ++k) {
|
|
|
|
int face_n = leaffaces[k];
|
|
|
|
map<int, Q3BSPFace*>::const_iterator it = q3face_map.find(face_n);
|
|
if (it != q3face_map.end()) {
|
|
if (it->second) leaf->faces.push_back(it->second);
|
|
continue;
|
|
}
|
|
|
|
q3_face *q3face = (q3_face*)header.dir[13].lump + leaffaces[k];
|
|
|
|
if (q3face->type == 1 || q3face->type == 3) {
|
|
if (!q3face->n_meshverts || (q3face->n_meshverts % 3)) continue;
|
|
} else if (q3face->type != 2) continue;
|
|
|
|
bool draw = true, solid = true;
|
|
|
|
if (q3face->texture >= 0) {
|
|
q3_tex *q3tex = (q3_tex*)header.dir[1].lump + q3face->texture;
|
|
if (!(q3tex->contents & 1)) continue;
|
|
if (q3tex->flags & 0x84) draw = false;
|
|
}
|
|
|
|
if (!draw && !solid) continue;
|
|
|
|
Q3BSPFace *face = 0;
|
|
if (draw) {
|
|
Surf *surf = findSurf(q3face);
|
|
face = new Q3BSPFace;
|
|
face->t_surf = surf;
|
|
face->vert = surf->verts.size();
|
|
face->tri = surf->tris.size();
|
|
face->n_verts = face->n_tris = 0;
|
|
leaf->faces.push_back(face);
|
|
faces.push_back(face);
|
|
q3face_map.insert(make_pair(face_n, face));
|
|
}
|
|
|
|
if (q3face->type == 2) {
|
|
patchFace(face, q3face, draw, solid, 1);
|
|
} else {
|
|
meshFace(face, q3face, draw, solid);
|
|
}
|
|
}
|
|
|
|
return leaf;
|
|
}
|
|
|
|
Q3BSPNode *Q3BSPRep::createNode(int n) {
|
|
q3_node *q3node = (q3_node*)header.dir[3].lump + n;
|
|
q3_plane *q3plane = (q3_plane*)header.dir[2].lump + q3node->plane;
|
|
|
|
Q3BSPNode *node = new Q3BSPNode;
|
|
|
|
Vector mins(q3node->mins[0], q3node->mins[1], q3node->mins[2]);
|
|
Vector maxs(q3node->maxs[0], q3node->maxs[1], q3node->maxs[2]);
|
|
|
|
node->box = Box(tf(mins));
|
|
node->box.update(tf(maxs));
|
|
node->plane.n = tf(q3plane->normal);
|
|
node->plane.d = -q3plane->distance;
|
|
|
|
for (int k = 0; k < 2; ++k) {
|
|
if (q3node->children[k] >= 0) {
|
|
node->nodes[k] = createNode(q3node->children[k]);
|
|
node->leafs[k] = 0;
|
|
} else {
|
|
node->leafs[k] = createLeaf(-q3node->children[k] - 1);
|
|
node->nodes[k] = 0;
|
|
}
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
Q3BSPRep::Q3BSPRep(const string &f, float gam) :root_node(0), vis_sz(0), vis_data(0), use_lmap(true) {
|
|
|
|
gamma_adj = 1 - gam;
|
|
|
|
FILE *buf = fopen(f.c_str(), "rb"); if (!buf) return;
|
|
|
|
fread(&header, sizeof(header), 1, buf);
|
|
if (header.magic != 'PSBI' || header.version != 0x2e) {
|
|
fclose(buf); return;
|
|
}
|
|
|
|
debuglog("Header OK");
|
|
|
|
int k;
|
|
//load all lumps...
|
|
for (k = 0; k < 17; ++k) {
|
|
if (header.dir[k].offset && header.dir[k].length) {
|
|
fseek(buf, header.dir[k].offset, SEEK_SET);
|
|
header.dir[k].lump = new char[header.dir[k].length];
|
|
fread(header.dir[k].lump, header.dir[k].length, 1, buf);
|
|
} else {
|
|
header.dir[k].lump = 0;
|
|
}
|
|
}
|
|
|
|
//create root of BSP tree
|
|
root_node = createNode(0);
|
|
|
|
createCollider();
|
|
|
|
createTextures();
|
|
|
|
createLightMaps();
|
|
|
|
createSurfs();
|
|
|
|
createVis();
|
|
|
|
//unload all lumps...
|
|
for (k = 0; k < 17; ++k) {
|
|
delete[] header.dir[k].lump;
|
|
}
|
|
|
|
fclose(buf);
|
|
|
|
use_lmap = false;
|
|
setLighting(true);
|
|
|
|
q3face_map.clear();
|
|
}
|
|
|
|
Q3BSPRep::~Q3BSPRep() {
|
|
delete root_node;
|
|
delete[] vis_data;
|
|
int k;
|
|
for (k = 0; k < surfs.size(); ++k) {
|
|
gx_graphics->freeMesh(surfs[k]->mesh);
|
|
delete surfs[k];
|
|
}
|
|
for (k = 0; k < faces.size(); ++k) {
|
|
delete faces[k];
|
|
}
|
|
}
|
|
|
|
void Q3BSPRep::vis(Q3BSPNode *n) {
|
|
int i = n->plane.distance(r_eye) < 0;
|
|
if (n->nodes[i]) vis(n->nodes[i]);
|
|
else r_cluster = n->leafs[i]->cluster;
|
|
}
|
|
|
|
static bool cull(const Box &b, int *clip) {
|
|
for (int n = 0; n < 6; ++n) {
|
|
int mask = 1 << n;
|
|
if (!(*clip & mask)) continue;
|
|
const Plane &p = r_frustum.getPlane(n);
|
|
int q =
|
|
(p.distance(b.corner(0)) >= 0) + (p.distance(b.corner(1)) >= 0) +
|
|
(p.distance(b.corner(2)) >= 0) + (p.distance(b.corner(3)) >= 0) +
|
|
(p.distance(b.corner(4)) >= 0) + (p.distance(b.corner(5)) >= 0) +
|
|
(p.distance(b.corner(6)) >= 0) + (p.distance(b.corner(7)) >= 0);
|
|
if (!q) return false;
|
|
if (q == 8) *clip &= ~mask;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void Q3BSPRep::render(Q3BSPLeaf *l, int clip) {
|
|
int cluster = l->cluster;
|
|
if (cluster < 0) return;
|
|
|
|
if (r_cluster >= 0) {
|
|
if (!(vis_data[cluster*vis_sz + r_cluster / 8] & (1 << (r_cluster & 7)))) return;
|
|
}
|
|
|
|
if (clip && !cull(l->box, &clip)) return;
|
|
|
|
for (int k = 0; k < l->faces.size(); ++k) {
|
|
Q3BSPFace *f = l->faces[k];
|
|
if (Q3BSPSurf *s = f->surf) {
|
|
if (!s->r_faces.size()) r_surfs.push_back(s);
|
|
s->r_faces.push_back(f);
|
|
f->surf = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Q3BSPRep::render(Q3BSPNode *n, int clip) {
|
|
if (clip && !cull(n->box, &clip)) return;
|
|
|
|
//draw front to back...
|
|
int i = n->plane.distance(r_eye) < 0;
|
|
if (n->nodes[i]) render(n->nodes[i], clip);
|
|
else render(n->leafs[i], clip);
|
|
i ^= 1;
|
|
if (n->nodes[i]) render(n->nodes[i], clip);
|
|
else render(n->leafs[i], clip);
|
|
}
|
|
|
|
void Q3BSPRep::render(Model *model, const RenderContext &rc) {
|
|
r_eye = -model->getRenderTform() * rc.getCameraTform().v;
|
|
new(&r_frustum) Frustum(rc.getWorldFrustum(), -model->getRenderTform());
|
|
|
|
vis(root_node);
|
|
if (r_cluster == -1) debuglog("No cluster!");
|
|
render(root_node, 0x3f);
|
|
|
|
if (!r_surfs.size()) return;
|
|
|
|
gx_scene->setAmbient2(&ambient.x);
|
|
gx_scene->setWorldMatrix((gxScene::Matrix*)&model->getRenderTform());
|
|
|
|
int k;
|
|
for (k = 0; k < r_surfs.size(); ++k) {
|
|
Q3BSPSurf *s = r_surfs[k];
|
|
gx_scene->setRenderState(s->brush.getRenderState());
|
|
int j;
|
|
for (j = 0; j < s->r_faces.size(); ++j) {
|
|
Q3BSPFace *f = s->r_faces[j];
|
|
gx_scene->render(s->mesh, f->vert, f->n_verts, f->tri, f->n_tris);
|
|
f->surf = s;
|
|
}
|
|
s->r_faces.clear();
|
|
}
|
|
r_surfs.clear();
|
|
}
|
|
|
|
bool Q3BSPRep::collide(const Line &line, float radius, Collision *curr_coll, const Transform &t) {
|
|
return collider->collide(line, radius, curr_coll, t);
|
|
}
|
|
|
|
void Q3BSPRep::setAmbient(const Vector &t) {
|
|
ambient = t;
|
|
}
|
|
|
|
void Q3BSPRep::setLighting(bool lmap) {
|
|
if (lmap == use_lmap) return;
|
|
int fx = gxScene::FX_CONDLIGHT;
|
|
if (use_lmap = lmap) {
|
|
int k;
|
|
for (k = 0; k < surfs.size(); ++k) {
|
|
Q3BSPSurf *s = surfs[k];
|
|
if (s->lm_index >= 0) {
|
|
//has a lightmap...
|
|
s->brush.setFX(fx);
|
|
s->brush.setTexture(0, light_maps[s->lm_index], 0);
|
|
if (s->texture >= 0) {
|
|
s->brush.setTexture(1, textures[s->texture], 0);
|
|
}
|
|
} else {
|
|
s->brush.setFX(fx | gxScene::FX_EMISSIVE);
|
|
if (s->texture >= 0) {
|
|
s->brush.setTexture(0, textures[s->texture], 0);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
int k;
|
|
Texture tex;
|
|
for (k = 0; k < surfs.size(); ++k) {
|
|
Q3BSPSurf *s = surfs[k];
|
|
s->brush.setFX(fx | gxScene::FX_EMISSIVE);
|
|
if (s->texture >= 0) {
|
|
s->brush.setTexture(0, textures[s->texture], 0);
|
|
} else {
|
|
s->brush.setTexture(0, tex, 0);
|
|
}
|
|
s->brush.setTexture(1, tex, 0);
|
|
}
|
|
}
|
|
}
|