Files
BlitzNext/blitz3d/q3bsprep.cpp
T
Michael Fabian Dirks 581c640149 Uh, Updates?
2016-10-03 17:11:15 +02:00

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);
}
}
}