Files
BlitzNext/Runtime/blitz3d/meshmodel.cpp
T
Michael Fabian 'Xaymar' Dirks 24788185aa runtime: CMake-ify
2019-01-18 15:55:06 +01:00

321 lines
7.4 KiB
C++

#include "std.hpp"
#include "meshmodel.hpp"
#include "meshcollider.hpp"
extern gxGraphics *gx_graphics;
struct MeshModel::Rep : public Surface::Monitor {
int ref_cnt;
mutable Box box, cullBox;
mutable MeshCollider *collider;
mutable int box_valid, coll_valid, norms_valid;
SurfaceList surfaces;
vector<Transform> bone_tforms;
Rep() :
ref_cnt(1), collider(0), box_valid(-1), coll_valid(-1), norms_valid(-1) {
geom_changes = brush_changes = 0;
}
~Rep() {
delete collider;
for (int k = 0; k < surfaces.size(); ++k) delete surfaces[k];
}
Surface *createSurface(const Brush &b) {
Surface *t = new Surface(this);
surfaces.push_back(t);
t->setBrush(b);
return t;
}
Surface *findSurface(const Brush &b) {
for (int k = 0; k < surfaces.size(); ++k) {
Surface *s = surfaces[k];
if (s->getBrush() < b || b < s->getBrush()) continue;
return s;
}
return 0;
}
void paint(const Brush &b) {
for (int k = 0; k < surfaces.size(); ++k) {
Surface *s = surfaces[k];
s->setBrush(b);
}
}
void add(Rep *t) {
if (cullBox.empty() && !t->cullBox.empty()) {
setCullBox(t->cullBox);
}
for (int k = 0; k < t->surfaces.size(); ++k) {
Surface *src = t->surfaces[k];
Surface *dest = findSurface(src->getBrush());
if (!dest) dest = createSurface(src->getBrush());
int j;
for (j = 0; j < src->numTriangles(); ++j) {
Surface::Triangle t = src->getTriangle(j);
t.verts[0] += dest->numVertices();
t.verts[1] += dest->numVertices();
t.verts[2] += dest->numVertices();
dest->addTriangle(t);
}
for (j = 0; j < src->numVertices(); ++j) {
dest->addVertex(src->getVertex(j));
}
}
}
void transform(const Transform &t) {
Matrix co = t.m.cofactor();
for (int k = 0; k < surfaces.size(); ++k) {
Surface *s = surfaces[k];
for (int j = 0; j < s->numVertices(); ++j) {
const Vector &v = s->getVertex(j).coords;
const Vector &n = s->getVertex(j).normal;
s->setCoords(j, t*v);
s->setNormal(j, co*n);
}
}
}
void flip() {
for (int k = 0; k < surfaces.size(); ++k) {
Surface *s = surfaces[k];
int j;
for (j = 0; j < s->numVertices(); ++j) {
s->setNormal(j, -s->getVertex(j).normal);
}
for (j = 0; j < s->numTriangles(); ++j) {
Surface::Triangle t = s->getTriangle(j);
std::swap(t.verts[1], t.verts[2]);
s->setTriangle(j, t);
}
}
}
void setCullBox(const Box &t) {
cullBox = t;
}
void updateNormals() {
if (norms_valid != geom_changes) {
for (int k = 0; k < surfaces.size(); ++k) {
Surface *s = surfaces[k];
s->updateNormals();
}
norms_valid = geom_changes;
}
}
const Box &getBox()const {
if (box_valid != geom_changes) {
box.clear();
for (int k = 0; k < surfaces.size(); ++k) {
Surface *s = surfaces[k];
for (int j = 0; j < s->numVertices(); ++j) {
box.update(s->getVertex(j).coords);
}
}
box_valid = geom_changes;
}
return box;
}
const Box &getCullBox()const {
return cullBox.empty() ? getBox() : cullBox;
}
MeshCollider *getCollider()const {
if (coll_valid != geom_changes) {
delete collider;
vector<MeshCollider::Vertex> verts;
vector<MeshCollider::Triangle> tris;
for (int k = 0; k < surfaces.size(); ++k) {
Surface *s = surfaces[k];
int j;
for (j = 0; j < s->numTriangles(); ++j) {
MeshCollider::Triangle q;
q.verts[0] = s->getTriangle(j).verts[0] + verts.size();
q.verts[1] = s->getTriangle(j).verts[1] + verts.size();
q.verts[2] = s->getTriangle(j).verts[2] + verts.size();
q.surface = s;
q.index = j;
tris.push_back(q);
}
for (j = 0; j < s->numVertices(); ++j) {
MeshCollider::Vertex q;
q.coords = s->getVertex(j).coords;
verts.push_back(q);
}
}
collider = new MeshCollider(verts, tris);
coll_valid = geom_changes;
}
return collider;
}
};
MeshModel::MeshModel() :
rep(new Rep()), brush_changes(0) {}
MeshModel::MeshModel(const MeshModel &t) : Model(t),
rep(t.rep), brush_changes(rep->brush_changes - 1) {
++rep->ref_cnt;
surf_bones.resize(t.surf_bones.size());
/*
if( t.surf_bones.size() ){
surf_bones.resize( t.surf_bones.size() );
if( rep->bone_tforms.size() ){
setRenderSpace( RENDER_SPACE_WORLD );
}
}
*/
}
MeshModel::~MeshModel() {
if (!--rep->ref_cnt) delete rep;
}
void MeshModel::updateNormals() {
rep->updateNormals();
}
void MeshModel::setCullBox(const Box &box) {
rep->setCullBox(box);
}
void MeshModel::setRenderBrush(const Brush &b) {
--brush_changes;
render_brush = b;
}
void MeshModel::createBones() {
setRenderSpace(RENDER_SPACE_WORLD);
const vector<Object*> &bones = getAnimator()->getObjects();
surf_bones.resize(bones.size());
rep->bone_tforms.resize(bones.size());
for (int k = 0; k < bones.size(); ++k) {
rep->bone_tforms[k] = -bones[k]->GetWorldTransform();
}
}
bool MeshModel::render(const RenderContext &rc) {
const Box &b = rep->getCullBox();
if (b.empty()) return false;
static Frustum model_frustum;
new(&model_frustum) Frustum(rc.getWorldFrustum(), -getRenderTform());
if (!model_frustum.cull(b)) return false;
if (brush_changes != rep->brush_changes) {
brushes.clear();
for (int k = 0; k < rep->surfaces.size(); ++k) {
Surface *s = rep->surfaces[k];
brushes.push_back(Brush(s->getBrush(), render_brush));
}
brush_changes = rep->brush_changes;
}
if (!surf_bones.size()) {
for (int k = 0; k < rep->surfaces.size(); ++k) {
Surface *s = rep->surfaces[k];
if (gxMesh *mesh = s->getMesh()) {
enqueue(mesh, 0, s->numVertices(), 0, s->numTriangles(), brushes[k]);
}
}
return false;
}
//OK, its boned!
const vector<Object*> &bones = getAnimator()->getObjects();
int k;
for (k = 0; k < bones.size(); ++k) {
Transform t =
bones[k]->getRenderTform() * rep->bone_tforms[k];
surf_bones[k].coord_tform = t;
surf_bones[k].normal_tform = t.m.cofactor();
}
bool trans = false;
for (k = 0; k < rep->surfaces.size(); ++k) {
Surface *s = rep->surfaces[k];
if (brushes[k].getBlend() == gxScene::BLEND_REPLACE) {
if (gxMesh *mesh = s->getMesh(surf_bones)) {
enqueue(mesh, 0, s->numVertices(), 0, s->numTriangles(), brushes[k]);
}
} else {
trans = true;
}
}
return trans;
}
void MeshModel::renderQueue(int type) {
if (type == QUEUE_TRANSPARENT && surf_bones.size()) {
for (int k = 0; k < rep->surfaces.size(); ++k) {
Surface *s = rep->surfaces[k];
if (brushes[k].getBlend() != gxScene::BLEND_REPLACE) {
if (gxMesh *mesh = s->getMesh(surf_bones)) {
enqueue(mesh, 0, s->numVertices(), 0, s->numTriangles(), brushes[k]);
}
}
}
}
Model::renderQueue(type);
}
Surface *MeshModel::createSurface(const Brush &b) {
return rep->createSurface(b);
--brush_changes;
}
void MeshModel::flipTriangles() {
rep->flip();
}
void MeshModel::transform(const Transform &t) {
rep->transform(t);
}
void MeshModel::add(const MeshModel &t) {
rep->add(t.rep);
}
const MeshModel::SurfaceList &MeshModel::getSurfaces()const {
return rep->surfaces;
}
void MeshModel::paint(const Brush &b) {
rep->paint(b);
}
const Box &MeshModel::getBox()const {
return rep->getBox();
}
MeshCollider *MeshModel::getCollider()const {
return rep->getCollider();
}
Surface *MeshModel::findSurface(const Brush &b)const {
return rep->findSurface(b);
}
bool MeshModel::collide(const Line &line, float radius, Collision *curr_coll, const Transform &t) {
return getCollider()->collide(line, radius, curr_coll, t);
}
bool MeshModel::intersects(const MeshModel &m)const {
return getCollider()->intersects(*m.getCollider(), -m.GetWorldTransform()*GetWorldTransform());
}