diff --git a/Makefile b/Makefile index a79e6e7..942c8bf 100644 --- a/Makefile +++ b/Makefile @@ -1,33 +1,11 @@ -# null, opengl -BUILD=null - -# e.g. null -> -DRW_NULL -BUILDDEF:=$(shell echo $(BUILD) | tr a-z A-Z | sed 's/^/-DRW_/') -BUILDDIR=build-$(BUILD) -SRCDIR=src -#SRC := $(patsubst %.cpp,$(SRCDIR)/%.cpp, $(wildcard *.cpp)) -SRC := $(wildcard $(SRCDIR)/*.cpp $(SRCDIR)/*/*.cpp) -OBJ := $(patsubst $(SRCDIR)/%.cpp,$(BUILDDIR)/%.o,$(SRC)) -DEP := $(patsubst $(SRCDIR)/%.cpp,$(BUILDDIR)/%.d,$(SRC)) -INC := -I/usr/local/include -CFLAGS=-Wall -Wextra -g $(BUILDDEF) -fno-diagnostics-show-caret \ +# null, gl3, ps2, d3d9 +BUILD := null +TARGET := librw-$(BUILD).a +CFLAGS := -Wall -Wextra -g -fno-diagnostics-show-caret \ -Wno-parentheses -Wno-invalid-offsetof \ -Wno-unused-parameter -Wno-sign-compare -LIB=librw-$(BUILD).a -$(LIB): $(OBJ) +include Make.common + +$(TARGET): $(OBJ) ar scr $@ $(OBJ) - -$(BUILDDIR)/%.o: $(SRCDIR)/%.cpp - @mkdir -p $(@D) - $(CXX) $(CFLAGS) $(INC) -c $< -o $@ - -$(BUILDDIR)/%.d: $(SRCDIR)/%.cpp - @mkdir -p $(@D) - $(CXX) -MM -MT '$(patsubst $(SRCDIR)/%.cpp,$(BUILDDIR)/%.o,$<)' $(CFLAGS) $(INC) $< > $@ - -clean: - echo $(SRC) - rm -rf $(BUILDDIR)/* - --include $(DEP) diff --git a/src/base.cpp b/src/base.cpp index 1a5ee9a..abc03fa 100644 --- a/src/base.cpp +++ b/src/base.cpp @@ -184,9 +184,9 @@ V3d Matrix::transPoint(const V3d &p) { V3d res = this->pos; - res = add(res, scale(this->right, p.x)); - res = add(res, scale(this->up, p.y)); - res = add(res, scale(this->at, p.z)); + res = add(res, rw::scale(this->right, p.x)); + res = add(res, rw::scale(this->up, p.y)); + res = add(res, rw::scale(this->at, p.z)); return res; } @@ -194,9 +194,9 @@ V3d Matrix::transVec(const V3d &v) { V3d res; - res = scale(this->right, v.x); - res = add(res, scale(this->up, v.y)); - res = add(res, scale(this->at, v.z)); + res = rw::scale(this->right, v.x); + res = add(res, rw::scale(this->up, v.y)); + res = add(res, rw::scale(this->at, v.z)); return res; } @@ -251,6 +251,87 @@ Matrix::transpose(Matrix *m1, Matrix *m2) matrixTranspose((float32*)m1, (float32*)m2); } +void +Matrix::rotate(V3d *axis, float32 angle, CombineOp op) +{ + Matrix tmp; + V3d v = normalize(*axis); + angle = angle*M_PI/180.0f; + float32 s = sin(angle); + float32 c = cos(angle); + float32 t = 1.0f - cos(angle); + + Matrix rot = identMat; + rot.right.x = c + v.x*v.x*t; + rot.right.y = v.x*v.y*t + v.z*s; + rot.right.z = v.z*v.x*t - v.y*s; + rot.up.x = v.x*v.y*t - v.z*s; + rot.up.y = c + v.y*v.y*t; + rot.up.z = v.y*v.z*t + v.x*s; + rot.at.x = v.z*v.x*t + v.y*s; + rot.at.y = v.y*v.z*t - v.x*s; + rot.at.z = c + v.z*v.z*t; + + switch(op){ + case COMBINEREPLACE: + *this = rot; + break; + case COMBINEPRECONCAT: + mult(&tmp, this, &rot); + *this = tmp; + break; + case COMBINEPOSTCONCAT: + mult(&tmp, &rot, this); + *this = tmp; + break; + } +} + +void +Matrix::translate(V3d *translation, CombineOp op) +{ + Matrix tmp; + Matrix trans = identMat; + trans.pos = *translation; + switch(op){ + case COMBINEREPLACE: + *this = trans; + break; + case COMBINEPRECONCAT: + mult(&tmp, this, &trans); + *this = tmp; + break; + case COMBINEPOSTCONCAT: + mult(&tmp, &trans, this); + *this = tmp; + break; + } +} + +void +Matrix::scale(V3d *scale, CombineOp op) +{ + Matrix tmp; + Matrix scl = identMat; + scl.right.x = scale->x; + scl.right.y = scale->y; + scl.right.z = scale->z; + switch(op){ + case COMBINEREPLACE: + *this = scl; + break; + case COMBINEPRECONCAT: + mult(&tmp, this, &scl); + *this = tmp; + break; + case COMBINEPOSTCONCAT: + mult(&tmp, &scl, this); + *this = tmp; + break; + } +} + + Matrix3 Matrix3::makeRotation(const Quat &q) diff --git a/src/camera.cpp b/src/camera.cpp index 8875433..c68ef01 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -26,6 +26,136 @@ defaultEndUpdateCB(Camera *cam) engine->endUpdate(cam); } +static void +buildPlanes(Camera *cam) +{ + V3d *c = cam->frustumCorners; + FrustumPlane *p = cam->frustumPlanes; + V3d v51 = sub(c[1], c[5]); + V3d v73 = sub(c[3], c[7]); + + /* Far plane */ + p[0].plane.normal = cam->getFrame()->getLTM()->at; + p[0].plane.distance = dot(p[0].plane.normal, c[4]); + p[0].closestX = p[0].plane.normal.x < 0.0f ? 0 : 1; + p[0].closestY = p[0].plane.normal.y < 0.0f ? 0 : 1; + p[0].closestZ = p[0].plane.normal.z < 0.0f ? 0 : 1; + + /* Near plane */ + p[1].plane.normal = neg(p[0].plane.normal); + p[1].plane.distance = dot(p[1].plane.normal, c[0]); + p[1].closestX = p[1].plane.normal.x < 0.0f ? 0 : 1; + p[1].closestY = p[1].plane.normal.y < 0.0f ? 0 : 1; + p[1].closestZ = p[1].plane.normal.z < 0.0f ? 0 : 1; + + /* Right plane */ + p[2].plane.normal = normalize(cross(v51, + sub(c[6], c[5]))); + p[2].plane.distance = dot(p[2].plane.normal, c[1]); + p[2].closestX = p[2].plane.normal.x < 0.0f ? 0 : 1; + p[2].closestY = p[2].plane.normal.y < 0.0f ? 0 : 1; + p[2].closestZ = p[2].plane.normal.z < 0.0f ? 0 : 1; + + /* Top plane */ + p[3].plane.normal = normalize(cross(sub(c[4], c[5]), + v51)); + p[3].plane.distance = dot(p[3].plane.normal, c[1]); + p[3].closestX = p[3].plane.normal.x < 0.0f ? 0 : 1; + p[3].closestY = p[3].plane.normal.y < 0.0f ? 0 : 1; + p[3].closestZ = p[3].plane.normal.z < 0.0f ? 0 : 1; + + /* Left plane */ + p[4].plane.normal = normalize(cross(v73, + sub(c[4], c[7]))); + p[4].plane.distance = dot(p[4].plane.normal, c[3]); + p[4].closestX = p[4].plane.normal.x < 0.0f ? 0 : 1; + p[4].closestY = p[4].plane.normal.y < 0.0f ? 0 : 1; + p[4].closestZ = p[4].plane.normal.z < 0.0f ? 0 : 1; + + /* Bottom plane */ + p[5].plane.normal = normalize(cross(sub(c[6], c[7]), + v73)); + p[5].plane.distance = dot(p[5].plane.normal, c[3]); + p[5].closestX = p[5].plane.normal.x < 0.0f ? 0 : 1; + p[5].closestY = p[5].plane.normal.y < 0.0f ? 0 : 1; + p[5].closestZ = p[5].plane.normal.z < 0.0f ? 0 : 1; +} + +static void +buildClipPersp(Camera *cam) +{ + Matrix *ltm = cam->getFrame()->getLTM(); + + /* First we calculate the 4 points on the view window. */ + V3d up = scale(ltm->up, cam->viewWindow.y); + V3d left = scale(ltm->right, cam->viewWindow.x); + V3d *c = cam->frustumCorners; + c[0] = add(add(ltm->at, up), left); // top left + c[1] = sub(add(ltm->at, up), left); // top right + c[2] = sub(sub(ltm->at, up), left); // bottom right + c[3] = add(sub(ltm->at, up), left); // bottom left + + /* Now Calculate near and far corners. */ + V3d off = sub(scale(ltm->up, cam->viewOffset.y), + scale(ltm->right, cam->viewOffset.x)); + for(int32 i = 0; i < 4; i++){ + V3d corner = sub(cam->frustumCorners[i], off); + V3d pos = add(ltm->pos, off); + c[i] = add(scale(corner, cam->nearPlane), pos); + c[i+4] = add(scale(corner, cam->farPlane), pos); + } + + buildPlanes(cam); +} + +static void +buildClipParallel(Camera *cam) +{ + Matrix *ltm = cam->getFrame()->getLTM(); + float32 nearoffx = -(1.0f - cam->nearPlane)*cam->viewOffset.x; + float32 nearoffy = (1.0f - cam->nearPlane)*cam->viewOffset.y; + float32 faroffx = -(1.0f - cam->farPlane)*cam->viewOffset.x; + float32 faroffy = (1.0f - cam->farPlane)*cam->viewOffset.y; + + V3d *c = cam->frustumCorners; + c[0].x = nearoffx + cam->viewWindow.x; + c[0].y = nearoffy + cam->viewWindow.y; + c[0].z = cam->nearPlane; + + c[1].x = nearoffx - cam->viewWindow.x; + c[1].y = nearoffy + cam->viewWindow.y; + c[1].z = cam->nearPlane; + + c[2].x = nearoffx - cam->viewWindow.x; + c[2].y = nearoffy - cam->viewWindow.y; + c[2].z = cam->nearPlane; + + c[3].x = nearoffx + cam->viewWindow.x; + c[3].y = nearoffy - cam->viewWindow.y; + c[3].z = cam->nearPlane; + + c[4].x = faroffx + cam->viewWindow.x; + c[4].y = faroffy + cam->viewWindow.y; + c[4].z = cam->farPlane; + + c[5].x = faroffx - cam->viewWindow.x; + c[5].y = faroffy + cam->viewWindow.y; + c[5].z = cam->farPlane; + + c[6].x = faroffx - cam->viewWindow.x; + c[6].y = faroffy - cam->viewWindow.y; + c[6].z = cam->farPlane; + + c[7].x = faroffx + cam->viewWindow.x; + c[7].y = faroffy - cam->viewWindow.y; + c[7].z = cam->farPlane; + + for(int32 i = 0; i < 8; i++) + c[i] = ltm->transPoint(c[i]); + + buildPlanes(cam); +} + static void cameraSync(ObjectWithFrame *obj) { @@ -85,6 +215,8 @@ cameraSync(ObjectWithFrame *obj) proj.at.y = -proj.pos.y + 0.5f; proj.at.z = 1.0f; proj.atw = 1.0f; + Matrix::mult(&cam->viewMatrix, &proj, &inv); + buildClipPersp(cam); }else{ proj.at.x = cam->viewOffset.x*xscl; proj.at.y = -cam->viewOffset.y*yscl; @@ -95,8 +227,10 @@ cameraSync(ObjectWithFrame *obj) proj.pos.y = -proj.at.y + 0.5f; proj.pos.z = 0.0f; proj.posw = 1.0f; + Matrix::mult(&cam->viewMatrix, &proj, &inv); + buildClipParallel(cam); } - Matrix::mult(&cam->viewMatrix, &proj, &inv); + cam->frustumBoundBox.calculate(cam->frustumCorners, 8); } void diff --git a/src/clump.cpp b/src/clump.cpp index be7936f..bef94d7 100644 --- a/src/clump.cpp +++ b/src/clump.cpp @@ -316,8 +316,10 @@ Clump::render(void) // static void -atomicSync(ObjectWithFrame*) +atomicSync(ObjectWithFrame *obj) { + // TODO: interpolate + obj->object.privateFlags |= Atomic::WORLDBOUNDDIRTY; } @@ -339,13 +341,17 @@ Atomic::create(void) atomic->object.object.init(Atomic::ID, 0); atomic->object.syncCB = atomicSync; atomic->geometry = nil; + atomic->boundingSphere.center.set(0.0f, 0.0f, 0.0f); + atomic->boundingSphere.radius = 0.0f; atomic->worldBoundingSphere.center.set(0.0f, 0.0f, 0.0f); atomic->worldBoundingSphere.radius = 0.0f; atomic->setFrame(nil); + atomic->object.object.privateFlags |= WORLDBOUNDDIRTY; atomic->clump = nil; atomic->pipeline = nil; atomic->renderCB = Atomic::defaultRenderCB; atomic->object.object.flags = Atomic::COLLISIONTEST | Atomic::RENDER; + // TODO: interpolator // World extension atomic->world = nil; @@ -363,9 +369,9 @@ Atomic::clone() if(atomic == nil) return nil; atomic->object.object.copy(&this->object.object); - atomic->object.object.privateFlags |= 1; + atomic->object.object.privateFlags |= WORLDBOUNDDIRTY; if(this->geometry) - atomic->setGeometry(this->geometry); + atomic->setGeometry(this->geometry, 0); atomic->pipeline = this->pipeline; s_plglist.copy(atomic, this); return atomic; @@ -393,28 +399,34 @@ Atomic::removeFromClump(void) } void -Atomic::setGeometry(Geometry *geo) +Atomic::setGeometry(Geometry *geo, uint32 flags) { if(this->geometry) this->geometry->destroy(); if(geo) geo->refCount++; this->geometry = geo; - // TODO: bounding stuff + if(flags & SAMEBOUNDINGSPHERE) + return; + if(geo){ + this->boundingSphere = geo->morphTargets[0].boundingSphere; + if(this->getFrame()) // TODO: && getWorld??? + this->getFrame()->updateObjects(); + } } Sphere* Atomic::getWorldBoundingSphere(void) { Sphere *s = &this->worldBoundingSphere; + // TODO: if we ever support morphing, check interpolation if(!this->getFrame()->dirty() && (this->object.object.privateFlags & WORLDBOUNDDIRTY) == 0) return s; Matrix *ltm = this->getFrame()->getLTM(); // TODO: support scaling - // TODO: if we ever support morphing, fix this: - s->center = ltm->transPoint(this->geometry->morphTargets[0].boundingSphere.center); - s->radius = this->geometry->morphTargets[0].boundingSphere.radius; + s->center = ltm->transPoint(this->boundingSphere.center); + s->radius = this->boundingSphere.radius; this->object.object.privateFlags &= ~WORLDBOUNDDIRTY; return s; } @@ -445,10 +457,10 @@ Atomic::streamReadClump(Stream *stream, g = Geometry::streamRead(stream); if(g == nil) goto fail; - atomic->setGeometry(g); + atomic->setGeometry(g, 0); g->destroy(); }else - atomic->setGeometry(geometryList[buf[1]]); + atomic->setGeometry(geometryList[buf[1]], 0); atomic->object.object.flags = buf[2]; atomicRights[0] = 0; diff --git a/src/frame.cpp b/src/frame.cpp index 262d8af..19b43b9 100644 --- a/src/frame.cpp +++ b/src/frame.cpp @@ -248,6 +248,27 @@ Frame::syncDirty(void) Frame::dirtyList.init(); } +void +Frame::rotate(V3d *axis, float32 angle, CombineOp op) +{ + this->matrix.rotate(axis, angle, op); + updateObjects(); +} + +void +Frame::translate(V3d *trans, CombineOp op) +{ + this->matrix.translate(trans, op); + updateObjects(); +} + +void +Frame::scale(V3d *scl, CombineOp op) +{ + this->matrix.scale(scl, op); + updateObjects(); +} + void Frame::updateObjects(void) { diff --git a/src/geometry.cpp b/src/geometry.cpp index 7e9a707..3b52292 100644 --- a/src/geometry.cpp +++ b/src/geometry.cpp @@ -431,9 +431,26 @@ Geometry::generateTriangles(int8 *adc) } } +static void +dumpMesh(Mesh *m) +{ + for(int32 i = 0; i < m->numIndices-2; i++) +// if(i % 2) +// printf("%3d %3d %3d\n", +// m->indices[i+1], +// m->indices[i], +// m->indices[i+2]); +// else + printf("%d %d %d\n", + m->indices[i], + m->indices[i+1], + m->indices[i+2]); +} + void Geometry::buildMeshes(void) { +//dumpMesh(this->meshHeader->mesh); delete this->meshHeader; Triangle *tri; @@ -467,9 +484,8 @@ Geometry::buildMeshes(void) h->mesh[tri->matId].numIndices = idx; tri++; } - }else{ - assert(0 && "can't tristrip\n"); - } + }else + this->buildTristrips(); } /* The idea is that even in meshes where winding is not preserved @@ -508,9 +524,11 @@ Geometry::correctTristripWinding(void) /* Entering strip now, * make sure winding is correct */ inStrip = 1; - if(newmesh->numIndices % 2) - newmesh->indices[newmesh->numIndices++] = + if(newmesh->numIndices % 2){ + newmesh->indices[newmesh->numIndices] = newmesh->indices[newmesh->numIndices-1]; + newmesh->numIndices++; + } } newmesh->indices[newmesh->numIndices++] = mesh->indices[j]; } diff --git a/src/gl/gl3plugins.cpp b/src/gl/gl3plugins.cpp index b58bae5..1694088 100644 --- a/src/gl/gl3plugins.cpp +++ b/src/gl/gl3plugins.cpp @@ -200,8 +200,8 @@ matfxRenderCB(Atomic *atomic, InstanceDataHeader *header) InstanceData *inst = header->inst; int32 n = header->numMeshes; - rw::setRenderState(ALPHATESTFUNC, 1); - rw::setRenderState(ALPHATESTREF, 50); +// rw::setRenderState(ALPHATESTFUNC, 1); +// rw::setRenderState(ALPHATESTREF, 50); int32 fx; while(n--){ @@ -460,8 +460,8 @@ skinRenderCB(Atomic *atomic, InstanceDataHeader *header) InstanceData *inst = header->inst; int32 n = header->numMeshes; - rw::setRenderState(ALPHATESTFUNC, 1); - rw::setRenderState(ALPHATESTREF, 50); +// rw::setRenderState(ALPHATESTFUNC, 1); +// rw::setRenderState(ALPHATESTREF, 50); skinShader->use(); diff --git a/src/gl/gl3render.cpp b/src/gl/gl3render.cpp index 143f6de..7dc5dd9 100644 --- a/src/gl/gl3render.cpp +++ b/src/gl/gl3render.cpp @@ -84,8 +84,8 @@ defaultRenderCB(Atomic *atomic, InstanceDataHeader *header) InstanceData *inst = header->inst; int32 n = header->numMeshes; - rw::setRenderState(ALPHATESTFUNC, 1); - rw::setRenderState(ALPHATESTREF, 50); +// rw::setRenderState(ALPHATESTFUNC, 1); +// rw::setRenderState(ALPHATESTREF, 50); simpleShader->use(); diff --git a/src/prim.cpp b/src/prim.cpp new file mode 100644 index 0000000..c8a43d6 --- /dev/null +++ b/src/prim.cpp @@ -0,0 +1,37 @@ +#include +#include + +#include "rwbase.h" +#include "rwerror.h" +#include "rwplg.h" +#include "rwpipeline.h" +#include "rwobjects.h" +#include "rwengine.h" + +#define PLUGIN_ID 0 + +namespace rw { + +void +BBox::calculate(V3d *points, int32 n) +{ + this->inf = points[0]; + this->sup = points[0]; + while(--n){ + points++; + if(points->x < this->inf.x) + this->inf.x = points->x; + if(points->y < this->inf.y) + this->inf.x = points->y; + if(points->z < this->inf.z) + this->inf.x = points->z; + if(points->x > this->sup.x) + this->sup.x = points->x; + if(points->y > this->sup.y) + this->sup.x = points->y; + if(points->z > this->sup.z) + this->sup.x = points->z; + } +} + +} diff --git a/src/rwbase.h b/src/rwbase.h index e3c10c8..0f8b4f3 100644 --- a/src/rwbase.h +++ b/src/rwbase.h @@ -89,6 +89,7 @@ struct V2d this->x = x; this->y = y; } }; +inline V2d neg(const V2d &a) { return V2d(-a.x, -a.y); } inline V2d add(const V2d &a, const V2d &b) { return V2d(a.x+b.x, a.y+b.y); } inline V2d sub(const V2d &a, const V2d &b) { return V2d(a.x-b.x, a.y-b.y); } inline V2d scale(const V2d &a, float32 r) { return V2d(a.x*r, a.y*r); } @@ -104,6 +105,7 @@ struct V3d this->x = x; this->y = y; this->z = z; } }; +inline V3d neg(const V3d &a) { return V3d(-a.x, -a.y, -a.z); } inline V3d add(const V3d &a, const V3d &b) { return V3d(a.x+b.x, a.y+b.y, a.z+b.z); } inline V3d sub(const V3d &a, const V3d &b) { return V3d(a.x-b.x, a.y-b.y, a.z-b.z); } inline V3d scale(const V3d &a, float32 r) { return V3d(a.x*r, a.y*r, a.z*r); } @@ -146,6 +148,12 @@ inline V3d rotate(const V3d &v, const Quat &q) { return mult(mult(q, Quat(v)), c Quat lerp(const Quat &q, const Quat &p, float32 r); Quat slerp(const Quat &q, const Quat &p, float32 a); +enum CombineOp +{ + COMBINEREPLACE, + COMBINEPRECONCAT, + COMBINEPOSTCONCAT, +}; struct Matrix { @@ -171,6 +179,10 @@ struct Matrix static bool32 invert(Matrix *m1, Matrix *m2); static void invertOrthonormal(Matrix *m1, Matrix *m2); static void transpose(Matrix *m1, Matrix *m2); + // some more RW like helpers + void rotate(V3d *axis, float32 angle, CombineOp op); + void translate(V3d *translation, CombineOp op); + void scale(V3d *scl, CombineOp op); }; struct Matrix3 @@ -204,6 +216,20 @@ struct Sphere float32 radius; }; +struct Plane +{ + V3d normal; + float32 distance; +}; + +struct BBox +{ + V3d sup; + V3d inf; + + void calculate(V3d *points, int32 n); +}; + enum PrimitiveType { PRIMTYPENONE = 0, diff --git a/src/rwobjects.h b/src/rwobjects.h index 3081f5d..bf3b422 100644 --- a/src/rwobjects.h +++ b/src/rwobjects.h @@ -123,6 +123,9 @@ struct Frame : PluginBase bool32 dirty(void) { return !!(this->root->object.privateFlags & HIERARCHYSYNC); } Matrix *getLTM(void); + void rotate(V3d *axis, float32 angle, CombineOp op); + void translate(V3d *trans, CombineOp op); + void scale(V3d *trans, CombineOp op); void updateObjects(void); @@ -409,6 +412,7 @@ struct Geometry : PluginBase void allocateData(void); void generateTriangles(int8 *adc = nil); void buildMeshes(void); + void buildTristrips(void); void correctTristripWinding(void); void removeUnusedMaterials(void); @@ -447,9 +451,13 @@ struct Atomic : PluginBase // private WORLDBOUNDDIRTY = 0x01 }; + enum { + SAMEBOUNDINGSPHERE = 0x01, // for setGeometry + }; ObjectWithFrame object; Geometry *geometry; + Sphere boundingSphere; Sphere worldBoundingSphere; Clump *clump; LLLink inClump; @@ -470,7 +478,7 @@ struct Atomic : PluginBase static Atomic *fromClump(LLLink *lnk){ return LLLinkGetData(lnk, Atomic, inClump); } void removeFromClump(void); - void setGeometry(Geometry *geo); + void setGeometry(Geometry *geo, uint32 flags); Sphere *getWorldBoundingSphere(void); ObjPipeline *getPipeline(void); void render(void) { this->renderCB(this); } @@ -518,6 +526,8 @@ struct Light : PluginBase float32 getAngle(void); void setColor(float32 r, float32 g, float32 b); int32 getType(void){ return this->object.object.subType; } + void setFlags(uint32 flags) { this->object.object.flags = flags; } + uint32 getFlags(void) { return this->object.object.flags; } static Light *streamRead(Stream *stream); bool streamWrite(Stream *stream); uint32 streamGetSize(void); @@ -535,6 +545,17 @@ struct Light : PluginBase }; }; +struct FrustumPlane +{ + Plane plane; + /* Used for BBox tests: + * 0 = inf is closer to normal direction + * 1 = sup is closer to normal direction */ + uint8 closestX; + uint8 closestY; + uint8 closestZ; +}; + struct Camera : PluginBase { enum { ID = 4 }; @@ -553,6 +574,10 @@ struct Camera : PluginBase Matrix viewMatrix; float32 zScale, zShift; + FrustumPlane frustumPlanes[6]; + V3d frustumCorners[8]; + BBox frustumBoundBox; + // clump link handled by plugin in RW Clump *clump; LLLink inClump; diff --git a/src/tristrip.cpp b/src/tristrip.cpp new file mode 100644 index 0000000..4df066d --- /dev/null +++ b/src/tristrip.cpp @@ -0,0 +1,452 @@ +#include +#include +#include +#include +#include + +#include "rwbase.h" +#include "rwerror.h" +#include "rwplg.h" +#include "rwpipeline.h" +#include "rwobjects.h" + +#define PLUGIN_ID 2 + +namespace rw { + +struct GraphEdge +{ + int32 node; /* index of the connected node */ + uint32 isConnected : 1; /* is connected to other node */ + uint32 otherEdge : 2; /* edge number on connected node */ + uint32 isStrip : 1; /* is strip edge */ +}; + +struct StripNode +{ + uint16 v[3]; /* vertex indices */ + uint8 parent : 2; /* tunnel parent node (edge index) */ + uint8 visited : 1; /* visited in breadth first search */ + uint8 stripVisited : 1; /* strip starting at this node was visited during search */ + uint8 isEnd : 1; /* is in end list */ + GraphEdge e[3]; + int32 stripId; /* index of start node */ +// int asdf; + LLLink inlist; +}; + +struct StripMesh +{ + int32 numNodes; + StripNode *nodes; + LinkList loneNodes; /* nodes not connected to any others */ + LinkList endNodes; /* strip start/end nodes */ +}; + +static void +printNode(StripMesh *sm, StripNode *n) +{ + printf("%3ld: %3d %3d.%d %3d.%d %3d.%d || %3d %3d %3d\n", + n - sm->nodes, + n->stripId, + n->e[0].node, + n->e[0].isStrip, + n->e[1].node, + n->e[1].isStrip, + n->e[2].node, + n->e[2].isStrip, + n->v[0], + n->v[1], + n->v[2]); +} + +static void +printLone(StripMesh *sm) +{ + FORLIST(lnk, sm->loneNodes) + printNode(sm, LLLinkGetData(lnk, StripNode, inlist)); +} + +static void +printEnds(StripMesh *sm) +{ + FORLIST(lnk, sm->endNodes) + printNode(sm, LLLinkGetData(lnk, StripNode, inlist)); +} + +static void +printSmesh(StripMesh *sm) +{ + for(int32 i = 0; i < sm->numNodes; i++) + printNode(sm, &sm->nodes[i]); +} + +static void +collectFaces(Geometry *geo, StripMesh *sm, uint16 m) +{ + StripNode *n; + Triangle *t; + sm->numNodes = 0; + for(int32 i = 0; i < geo->numTriangles; i++){ + t = &geo->triangles[i]; + if(t->matId == m){ + n = &sm->nodes[sm->numNodes++]; + n->v[0] = t->v[0]; + n->v[1] = t->v[1]; + n->v[2] = t->v[2]; + n->e[0].node = 0; + n->e[1].node = 0; + n->e[2].node = 0; + n->e[0].isConnected = 0; + n->e[1].isConnected = 0; + n->e[2].isConnected = 0; + n->e[0].isStrip = 0; + n->e[1].isStrip = 0; + n->e[2].isStrip = 0; + n->parent = 0; + n->visited = 0; + n->stripVisited = 0; + n->isEnd = 0; + n->stripId = -1; + n->inlist.init(); + } + } +} + +/* Find Triangle that has edge e. */ +static GraphEdge +findEdge(StripMesh *sm, int32 e[2]) +{ + StripNode *n; + GraphEdge ge = { 0, 0, 0, 0 }; + for(int32 i = 0; i < sm->numNodes; i++){ + n = &sm->nodes[i]; + for(int32 j = 0; j < 3; j++){ + if(n->e[j].isConnected) + continue; + if(e[0] == n->v[j] && + e[1] == n->v[(j+1) % 3]){ + ge.node = i; + ge.isConnected = 1; + ge.otherEdge = j; + return ge; + } + } + } + return ge; +} + +/* Connect nodes sharing an edge, preserving winding */ +static void +connectNodes(StripMesh *sm) +{ + StripNode *n, *nn; + int32 e[2]; + GraphEdge ge; + for(int32 i = 0; i < sm->numNodes; i++){ + n = &sm->nodes[i]; + for(int32 j = 0; j < 3; j++){ + if(n->e[j].isConnected) + continue; + + /* flip edge and search for node */ + e[1] = n->v[j]; + e[0] = n->v[(j+1) % 3]; + ge = findEdge(sm, e); + /* found node, now connect */ + if(ge.isConnected){ + n->e[j].node = ge.node; + n->e[j].isConnected = 1; + n->e[j].otherEdge = ge.otherEdge; + n->e[j].isStrip = 0; + nn = &sm->nodes[ge.node]; + nn->e[ge.otherEdge].node = i; + nn->e[ge.otherEdge].isConnected = 1; + nn->e[ge.otherEdge].otherEdge = j; + nn->e[ge.otherEdge].isStrip = 0; + } + } + } +} + +static int32 +numConnections(StripNode *n) +{ + return n->e[0].isConnected + + n->e[1].isConnected + + n->e[2].isConnected; +} + +static int32 +numStripEdges(StripNode *n) +{ + return n->e[0].isStrip + + n->e[1].isStrip + + n->e[2].isStrip; +} + +#define IsEnd(n) (numConnections(n) > 0 && numStripEdges(n) < 2) + +static void +complementEdge(StripMesh *sm, GraphEdge *e) +{ + e->isStrip = !e->isStrip; + e = &sm->nodes[e->node].e[e->otherEdge]; + e->isStrip = !e->isStrip; +} + +/* While possible extend a strip from a starting node until + * we find a node already in a strip. N.B. this function does + * make no attempts to connect to an already existing strip. */ +static void +extendStrip(StripMesh *sm, StripNode *start) +{ + StripNode *n, *nn; + n = start; + if(numConnections(n) == 0){ + sm->loneNodes.append(&n->inlist); + return; + } + sm->endNodes.append(&n->inlist); + n->isEnd = 1; +tail: + /* Find the next node to connect to on any of the three edges */ + for(int32 i = 0; i < 3; i++){ + if(!n->e[i].isConnected) + continue; + nn = &sm->nodes[n->e[i].node]; + if(nn->stripId >= 0) + continue; + + /* found one */ + nn->stripId = n->stripId; + complementEdge(sm, &n->e[i]); + n = nn; + goto tail; + } + if(n != start){ + sm->endNodes.append(&n->inlist); + n->isEnd = 1; + } +} + +static void +buildStrips(StripMesh *sm) +{ + StripNode *n; + for(int32 i = 0; i < sm->numNodes; i++){ + n = &sm->nodes[i]; + if(n->stripId >= 0) + continue; + n->stripId = i; + extendStrip(sm, n); + } +} + +static StripNode* +findTunnel(StripMesh *sm, StripNode *n) +{ + LinkList searchNodes; + StripNode *nn; + int edgetype; + int isEnd; + + searchNodes.init(); + edgetype = 0; + for(;;){ + for(int32 i = 0; i < 3; i++){ + /* Find a node connected by the right edgetype */ + if(!n->e[i].isConnected || + n->e[i].isStrip != edgetype) + continue; + nn = &sm->nodes[n->e[i].node]; + + /* If the node has been visited already, + * there's a shorter path. */ + if(nn->visited) + continue; + + /* Don't allow non-strip edge between nodes of the same + * strip to prevent loops. + * Actually these edges are allowed under certain + * circumstances, but they require complex checks. */ + if(edgetype == 0 && + n->stripId == nn->stripId) + continue; + + isEnd = IsEnd(nn); + + /* Can't add end nodes to two lists, so skip. */ + if(isEnd && edgetype == 1) + continue; + + nn->parent = n->e[i].otherEdge; + nn->visited = 1; + sm->nodes[nn->stripId].stripVisited = 1; + + /* Search complete. */ + if(isEnd && edgetype == 0) + return nn; + + /* Found a valid node. */ + searchNodes.append(&nn->inlist); + } + if(searchNodes.isEmpty()) + return nil; + n = LLLinkGetData(searchNodes.link.next, StripNode, inlist); + n->inlist.remove(); + edgetype = !edgetype; + } +} + +static void +resetGraph(StripMesh *sm) +{ + StripNode *n; + for(int32 i = 0; i < sm->numNodes; i++){ + n = &sm->nodes[i]; + n->visited = 0; + n->stripVisited = 0; + } +} + +static StripNode* +walkStrip(StripMesh *sm, StripNode *start) +{ + StripNode *n, *nn; + int32 last; + +//printf("stripend: "); +//printNode(sm, start); + + n = start; + last = -1; + for(;;n = nn){ + n->visited = 0; + n->stripVisited = 0; + if(n->isEnd) + n->inlist.remove(); + n->isEnd = 0; + + if(IsEnd(n) && n != start) + return n; + + /* find next node */ + nn = nil; + for(int32 i = 0; i < 3; i++){ + if(!n->e[i].isStrip || i == last) + continue; + nn = &sm->nodes[n->e[i].node]; + last = n->e[i].otherEdge; + nn->stripId = n->stripId; + break; + } +//printf(" next: "); +//printNode(sm, nn); + if(nn == nil) + return nil; + } +} + +static void +applyTunnel(StripMesh *sm, StripNode *end, StripNode *start) +{ + LinkList tmplist; + StripNode *n, *nn; + + for(n = end; n != start; n = &sm->nodes[n->e[n->parent].node]){ +//printf(" "); +//printNode(sm, n); + complementEdge(sm, &n->e[n->parent]); + } +//printf(" "); +//printNode(sm, start); + +//printSmesh(sm); +//printf("-------\n"); + tmplist.init(); + while(!sm->endNodes.isEmpty()){ + n = LLLinkGetData(sm->endNodes.link.next, StripNode, inlist); + /* take out of end list */ + n->inlist.remove(); + n->isEnd = 0; + /* no longer an end node */ + if(!IsEnd(n)) + continue; + // TODO: only walk strip if it was touched + /* set new id, walk strip and find other end */ + n->stripId = n - sm->nodes; + nn = walkStrip(sm, n); + tmplist.append(&n->inlist); + n->isEnd = 1; + if(nn && n != nn){ + tmplist.append(&nn->inlist); + nn->isEnd = 1; + } + } + /* Move new end nodes to the real list. */ + sm->endNodes = tmplist; + sm->endNodes.link.next->prev = &sm->endNodes.link; + sm->endNodes.link.prev->next = &sm->endNodes.link; +} + +static void +tunnel(StripMesh *sm) +{ + StripNode *n, *nn; + +again: + FORLIST(lnk, sm->endNodes){ + n = LLLinkGetData(lnk, StripNode, inlist); +// printf("searching %p %d\n", n, numStripEdges(n)); + nn = findTunnel(sm, n); +// printf(" %p %p\n", n, nn); + + if(nn){ + applyTunnel(sm, nn, n); + resetGraph(sm); + /* applyTunnel changes sm->endNodes, so we have to + * jump out of the loop. */ + goto again; + } + resetGraph(sm); + } + printf("tunneling done!\n"); +} + +/* + * For each material: + * 1. build dual graph (collectFaces, connectNodes) + * 2. make some simple strip (buildStrips) + * 3. apply tunnel operator (tunnel) + */ +void +Geometry::buildTristrips(void) +{ + StripMesh smesh; + + printf("%ld\n", sizeof(StripNode)); + + smesh.nodes = new StripNode[this->numTriangles]; + for(int32 i = 0; i < this->numMaterials; i++){ + smesh.loneNodes.init(); + smesh.endNodes.init(); + collectFaces(this, &smesh, i); + connectNodes(&smesh); + buildStrips(&smesh); +printSmesh(&smesh); +printf("-------\n"); +//printLone(&smesh); +//printf("-------\n"); +//printEnds(&smesh); +//printf("-------\n"); + tunnel(&smesh); +//printf("-------\n"); +//printEnds(&smesh); + } + delete[] smesh.nodes; + + exit(1); +} + +}