828 lines
19 KiB
C++
828 lines
19 KiB
C++
|
|
#include "q3bsprep.hpp"
|
|
#include "std.hpp"
|
|
|
|
/* 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);
|
|
}
|
|
}
|
|
}
|