started implementing tristrip; implemented camera frustum

This commit is contained in:
aap 2017-03-16 01:10:01 +01:00
parent c2780d56e9
commit 4941711964
12 changed files with 842 additions and 58 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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;

View File

@ -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)
{

View File

@ -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];
}

View File

@ -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();

View File

@ -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();

37
src/prim.cpp Normal file
View File

@ -0,0 +1,37 @@
#include <cstdio>
#include <cstdlib>
#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;
}
}
}

View File

@ -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,

View File

@ -123,6 +123,9 @@ struct Frame : PluginBase<Frame>
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<Geometry>
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<Atomic>
// 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<Atomic>
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<Light>
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<Light>
};
};
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<Camera>
{
enum { ID = 4 };
@ -553,6 +574,10 @@ struct Camera : PluginBase<Camera>
Matrix viewMatrix;
float32 zScale, zShift;
FrustumPlane frustumPlanes[6];
V3d frustumCorners[8];
BBox frustumBoundBox;
// clump link handled by plugin in RW
Clump *clump;
LLLink inClump;

452
src/tristrip.cpp Normal file
View File

@ -0,0 +1,452 @@
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cassert>
#include <cmath>
#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);
}
}