librw/tools/playground/tl_tests.cpp
2020-04-28 17:24:26 +02:00

663 lines
17 KiB
C++

#include <rw.h>
#include <skeleton.h>
extern bool dosoftras;
using namespace rw;
using namespace RWDEVICE;
void rastest_renderTriangles(RWDEVICE::Im2DVertex *scrverts, int32 verts, uint16 *indices, int32 numTris);
//
// This is a test to implement T&L in software and render with Im2D
//
namespace gen {
#define MAX_LIGHTS 8
struct Directional {
V3d at;
RGBAf color;
};
static Directional directionals[MAX_LIGHTS];
static int32 numDirectionals;
static RGBAf ambLight;
static void
enumLights(Matrix *lightmat)
{
int32 n;
World *world;
world = (World*)engine->currentWorld;
ambLight.red = 0.0;
ambLight.green = 0.0;
ambLight.blue = 0.0;
ambLight.alpha = 0.0;
numDirectionals = 0;
// only unpositioned lights right now
FORLIST(lnk, world->globalLights){
Light *l = Light::fromWorld(lnk);
if(l->getType() == Light::DIRECTIONAL){
if(numDirectionals >= MAX_LIGHTS)
continue;
n = numDirectionals++;
V3d::transformVectors(&directionals[n].at, &l->getFrame()->getLTM()->at, 1, lightmat);
directionals[n].color = l->color;
directionals[n].color.alpha = 0.0f;
}else if(l->getType() == Light::AMBIENT){
ambLight.red += l->color.red;
ambLight.green += l->color.green;
ambLight.blue += l->color.blue;
}
}
}
struct ObjSpace3DVertex
{
V3d objVertex;
V3d objNormal;
RGBA color;
TexCoords texCoords;
};
enum {
CLIPXLO = 0x01,
CLIPXHI = 0x02,
CLIPX = 0x03,
CLIPYLO = 0x04,
CLIPYHI = 0x08,
CLIPY = 0x0C,
CLIPZLO = 0x10,
CLIPZHI = 0x20,
CLIPZ = 0x30,
};
struct CamSpace3DVertex
{
V3d camVertex;
uint8 clipFlags;
RGBAf color;
TexCoords texCoords;
};
struct InstanceData
{
uint16 *indices;
int32 numIndices;
ObjSpace3DVertex *vertices;
int32 numVertices;
// int vertStride; // not really needed right now
Material *material;
Mesh *mesh;
};
struct InstanceDataHeader : public rw::InstanceDataHeader
{
uint32 serialNumber;
ObjSpace3DVertex *vertices;
uint16 *indices;
InstanceData *inst;
};
static void
instanceAtomic(Atomic *atomic)
{
static V3d zeroNorm = { 0.0f, 0.0f, 0.0f };
static RGBA black = { 0, 0, 0, 255 };
static TexCoords zeroTex = { 0.0f, 0.0f };
int i;
uint j;
int x, x1, x2, x3;
Geometry *geo;
MeshHeader *header;
Mesh *mesh;
InstanceDataHeader *insthead;
InstanceData *inst;
uint32 firstVert;
uint16 *srcindices, *dstindices;
geo = atomic->geometry;
if(geo->instData)
return;
header = geo->meshHeader;
int numTris;
if(header->flags & MeshHeader::TRISTRIP)
numTris = header->totalIndices - 2*header->numMeshes;
else
numTris = header->totalIndices / 3;
int size;
size = sizeof(InstanceDataHeader) + header->numMeshes*sizeof(InstanceData) +
geo->numVertices*sizeof(ObjSpace3DVertex) + numTris*6*sizeof(uint16);
insthead = (InstanceDataHeader*)rwNew(size, ID_GEOMETRY);
geo->instData = insthead;
insthead->platform = 0;
insthead->serialNumber = header->serialNum;
inst = (InstanceData*)(insthead+1);
insthead->inst = inst;
insthead->vertices = (ObjSpace3DVertex*)(inst+header->numMeshes);
dstindices = (uint16*)(insthead->vertices+geo->numVertices);
insthead->indices = dstindices;
// TODO: morphing
MorphTarget *mt = geo->morphTargets;
for(i = 0; i < geo->numVertices; i++){
insthead->vertices[i].objVertex = mt->vertices[i];
if(geo->flags & Geometry::NORMALS)
insthead->vertices[i].objNormal = mt->normals[i];
else
insthead->vertices[i].objNormal = zeroNorm;
if(geo->flags & Geometry::PRELIT)
insthead->vertices[i].color = geo->colors[i];
else
insthead->vertices[i].color = black;
if(geo->numTexCoordSets > 0)
insthead->vertices[i].texCoords = geo->texCoords[0][i];
else
insthead->vertices[i].texCoords = zeroTex;
}
mesh = header->getMeshes();
for(i = 0; i < header->numMeshes; i++){
findMinVertAndNumVertices(mesh->indices, mesh->numIndices,
&firstVert, &inst->numVertices);
inst->indices = dstindices;
inst->vertices = &insthead->vertices[firstVert];
inst->mesh = mesh;
inst->material = mesh->material;
srcindices = mesh->indices;
if(header->flags & MeshHeader::TRISTRIP){
inst->numIndices = 0;
x = 0;
for(j = 0; j < mesh->numIndices-2; j++){
x1 = srcindices[j+x];
x ^= 1;
x2 = srcindices[j+x];
x3 = srcindices[j+2];
if(x1 != x2 && x2 != x3 && x1 != x3){
dstindices[0] = x1;
dstindices[1] = x2;
dstindices[2] = x3;
dstindices += 3;
inst->numIndices += 3;
}
}
}else{
inst->numIndices = mesh->numIndices;
for(j = 0; j < mesh->numIndices; j += 3){
dstindices[0] = srcindices[j+0] - firstVert;
dstindices[1] = srcindices[j+1] - firstVert;
dstindices[2] = srcindices[j+2] - firstVert;
dstindices += 3;
}
}
inst++;
mesh++;
}
}
struct MeshState
{
int32 flags;
Matrix obj2cam;
Matrix obj2world;
int32 numVertices;
int32 numPrimitives;
SurfaceProperties surfProps;
RGBA matCol;
};
static void
cam2screen(Im2DVertex *scrvert, CamSpace3DVertex *camvert)
{
RGBA col;
float32 recipZ;
Camera *cam = (Camera*)engine->currentCamera;
int32 width = cam->frameBuffer->width;
int32 height = cam->frameBuffer->height;
recipZ = 1.0f/camvert->camVertex.z;
scrvert->setScreenX(camvert->camVertex.x * recipZ * width);
scrvert->setScreenY(camvert->camVertex.y * recipZ * height);
// scrvert->setScreenX(camvert->camVertex.x * recipZ * width/2 + width/4);
// scrvert->setScreenY(camvert->camVertex.y * recipZ * height/2 + height/4);
scrvert->setScreenZ(recipZ * cam->zScale + cam->zShift);
scrvert->setCameraZ(camvert->camVertex.z);
scrvert->setRecipCameraZ(recipZ);
scrvert->setU(camvert->texCoords.u, recipZ);
scrvert->setV(camvert->texCoords.v, recipZ);
convColor(&col, &camvert->color);
scrvert->setColor(col.red, col.green, col.blue, col.alpha);
}
static void
transform(MeshState *mstate, ObjSpace3DVertex *objverts, CamSpace3DVertex *camverts, Im2DVertex *scrverts)
{
int32 i;
float32 z;
Camera *cam = (Camera*)engine->currentCamera;
for(i = 0; i < mstate->numVertices; i++){
V3d::transformPoints(&camverts[i].camVertex, &objverts[i].objVertex, 1, &mstate->obj2cam);
convColor(&camverts[i].color, &objverts[i].color);
camverts[i].texCoords = objverts[i].texCoords;
camverts[i].clipFlags = 0;
z = camverts[i].camVertex.z;
// 0 < x < z
if(camverts[i].camVertex.x >= z) camverts[i].clipFlags |= CLIPXHI;
if(camverts[i].camVertex.x <= 0) camverts[i].clipFlags |= CLIPXLO;
// 0 < y < z
if(camverts[i].camVertex.y >= z) camverts[i].clipFlags |= CLIPYHI;
if(camverts[i].camVertex.y <= 0) camverts[i].clipFlags |= CLIPYLO;
// near < z < far
if(z >= cam->farPlane) camverts[i].clipFlags |= CLIPZHI;
if(z <= cam->nearPlane) camverts[i].clipFlags |= CLIPZLO;
cam2screen(&scrverts[i], &camverts[i]);
}
}
static void
light(MeshState *mstate, ObjSpace3DVertex *objverts, CamSpace3DVertex *camverts)
{
int32 i;
RGBAf colf;
RGBAf amb = ambLight;
amb = scale(ambLight, mstate->surfProps.ambient);
for(i = 0; i < mstate->numVertices; i++){
camverts[i].color = add(camverts[i].color, amb);
if((mstate->flags & Geometry::NORMALS) == 0)
continue;
for(int32 k = 0; k < numDirectionals; k++){
float32 f = dot(objverts[i].objNormal, neg(directionals[k].at));
if(f <= 0.0f) continue;
f *= mstate->surfProps.diffuse;
colf = scale(directionals[k].color, f);
camverts[i].color = add(camverts[i].color, colf);
}
}
}
static void
postlight(MeshState *mstate, CamSpace3DVertex *camverts, Im2DVertex *scrverts)
{
int32 i;
RGBA col;
RGBAf colf;
for(i = 0; i < mstate->numVertices; i++){
convColor(&colf, &mstate->matCol);
camverts[i].color = modulate(camverts[i].color, colf);
clamp(&camverts[i].color);
convColor(&col, &camverts[i].color);
scrverts[i].setColor(col.red, col.green, col.blue, col.alpha);
}
}
static int32
cullTriangles(MeshState *mstate, CamSpace3DVertex *camverts, uint16 *indices, uint16 *clipindices)
{
int32 i;
int32 x1, x2, x3;
int32 newNumPrims;
int32 numClip;
newNumPrims = 0;
numClip = 0;
for(i = 0; i < mstate->numPrimitives; i++, indices += 3){
x1 = indices[0];
x2 = indices[1];
x3 = indices[2];
// Only a simple frustum call
if(camverts[x1].clipFlags &
camverts[x2].clipFlags &
camverts[x3].clipFlags)
continue;
if(camverts[x1].clipFlags |
camverts[x2].clipFlags |
camverts[x3].clipFlags)
numClip++;
// The Triangle is in, probably
clipindices[0] = x1;
clipindices[1] = x2;
clipindices[2] = x3;
clipindices += 3;
newNumPrims++;
}
mstate->numPrimitives = newNumPrims;
return numClip;
}
static void
interpVertex(CamSpace3DVertex *out, CamSpace3DVertex *v1, CamSpace3DVertex *v2, float32 t)
{
float32 z;
float32 invt;
Camera *cam = (Camera*)engine->currentCamera;
invt = 1.0f - t;
out->camVertex = add(scale(v1->camVertex, invt), scale(v2->camVertex, t));
out->color = add(scale(v1->color, invt), scale(v2->color, t));
out->texCoords.u = v1->texCoords.u*invt + v2->texCoords.u*t;
out->texCoords.v = v1->texCoords.v*invt + v2->texCoords.v*t;
out->clipFlags = 0;
z = out->camVertex.z;
// 0 < x < z
if(out->camVertex.x >= z) out->clipFlags |= CLIPXHI;
if(out->camVertex.x <= 0) out->clipFlags |= CLIPXLO;
// 0 < y < z
if(out->camVertex.y >= z) out->clipFlags |= CLIPYHI;
if(out->camVertex.y <= 0) out->clipFlags |= CLIPYLO;
// near < z < far
if(z >= cam->farPlane) out->clipFlags |= CLIPZHI;
if(z <= cam->nearPlane) out->clipFlags |= CLIPZLO;
}
static void
clipTriangles(MeshState *mstate, CamSpace3DVertex *camverts, Im2DVertex *scrverts, uint16 *indices, uint16 *clipindices)
{
int32 i, j;
int32 x1, x2, x3;
int32 newNumPrims;
CamSpace3DVertex buf[18];
CamSpace3DVertex *in, *out, *tmp;
int32 nin, nout;
float32 t;
Camera *cam = (Camera*)engine->currentCamera;
newNumPrims = 0;
for(i = 0; i < mstate->numPrimitives; i++, indices += 3){
x1 = indices[0];
x2 = indices[1];
x3 = indices[2];
if((camverts[x1].clipFlags |
camverts[x2].clipFlags |
camverts[x3].clipFlags) == 0){
// all inside
clipindices[0] = x1;
clipindices[1] = x2;
clipindices[2] = x3;
clipindices += 3;
newNumPrims++;
continue;
}
// set up triangle
in = &buf[0];
out = &buf[9];
in[0] = camverts[x1];
in[1] = camverts[x2];
in[2] = camverts[x3];
nin = 3;
nout = 0;
#define V(a) in[a].camVertex.
// clip z near
for(j = 0; j < nin; j++){
x1 = j;
x2 = (j+1) % nin;
if((in[x1].clipFlags ^ in[x2].clipFlags) & CLIPZLO){
t = (cam->nearPlane - V(x1)z)/(V(x2)z - V(x1)z);
interpVertex(&out[nout++], &in[x1], &in[x2], t);
}
if((in[x2].clipFlags & CLIPZLO) == 0)
out[nout++] = in[x2];
}
// clip z far
nin = nout; nout = 0;
tmp = in; in = out; out = tmp;
for(j = 0; j < nin; j++){
x1 = j;
x2 = (j+1) % nin;
if((in[x1].clipFlags ^ in[x2].clipFlags) & CLIPZHI){
t = (cam->farPlane - V(x1)z)/(V(x2)z - V(x1)z);
interpVertex(&out[nout++], &in[x1], &in[x2], t);
}
if((in[x2].clipFlags & CLIPZHI) == 0)
out[nout++] = in[x2];
}
// clip y 0
nin = nout; nout = 0;
tmp = in; in = out; out = tmp;
for(j = 0; j < nin; j++){
x1 = j;
x2 = (j+1) % nin;
if((in[x1].clipFlags ^ in[x2].clipFlags) & CLIPYLO){
t = -V(x1)y/(V(x2)y - V(x1)y);
interpVertex(&out[nout++], &in[x1], &in[x2], t);
}
if((in[x2].clipFlags & CLIPYLO) == 0)
out[nout++] = in[x2];
}
// clip y z
nin = nout; nout = 0;
tmp = in; in = out; out = tmp;
for(j = 0; j < nin; j++){
x1 = j;
x2 = (j+1) % nin;
if((in[x1].clipFlags ^ in[x2].clipFlags) & CLIPYHI){
t = (V(x1)z - V(x1)y)/(V(x1)z - V(x1)y + V(x2)y - V(x2)z);
interpVertex(&out[nout++], &in[x1], &in[x2], t);
}
if((in[x2].clipFlags & CLIPYHI) == 0)
out[nout++] = in[x2];
}
// clip x 0
nin = nout; nout = 0;
tmp = in; in = out; out = tmp;
for(j = 0; j < nin; j++){
x1 = j;
x2 = (j+1) % nin;
if((in[x1].clipFlags ^ in[x2].clipFlags) & CLIPXLO){
t = -V(x1)x/(V(x2)x - V(x1)x);
interpVertex(&out[nout++], &in[x1], &in[x2], t);
}
if((in[x2].clipFlags & CLIPXLO) == 0)
out[nout++] = in[x2];
}
// clip x z
nin = nout; nout = 0;
tmp = in; in = out; out = tmp;
for(j = 0; j < nin; j++){
x1 = j;
x2 = (j+1) % nin;
if((in[x1].clipFlags ^ in[x2].clipFlags) & CLIPXHI){
t = (V(x1)z - V(x1)x)/(V(x1)z - V(x1)x + V(x2)x - V(x2)z);
interpVertex(&out[nout++], &in[x1], &in[x2], t);
}
if((in[x2].clipFlags & CLIPXHI) == 0)
out[nout++] = in[x2];
}
// Insert new triangles
x1 = mstate->numVertices;
for(j = 0; j < nout; j++){
x2 = mstate->numVertices++;
camverts[x2] = out[j];
cam2screen(&scrverts[x2], &camverts[x2]);
}
x2 = x1+1;
for(j = 0; j < nout-2; j++){
clipindices[0] = x1;
clipindices[1] = x2++;
clipindices[2] = x2;
clipindices += 3;
newNumPrims++;
}
}
mstate->numPrimitives = newNumPrims;
}
static void
submitTriangles(RWDEVICE::Im2DVertex *scrverts, int32 numVerts, uint16 *indices, int32 numTris)
{
rw::SetRenderStatePtr(rw::TEXTURERASTER, nil);
if(dosoftras)
rastest_renderTriangles(scrverts, numVerts, indices, numTris);
else{
//int i;
//for(i = 0; i < numVerts; i++){
// scrverts[i].x = (int)(scrverts[i].x*16.0f) / 16.0f;
// scrverts[i].y = (int)(scrverts[i].y*16.0f) / 16.0f;
//}
im2d::RenderIndexedPrimitive(rw::PRIMTYPETRILIST, scrverts, numVerts,
indices, numTris*3);
}
}
static void
drawMesh(MeshState *mstate, ObjSpace3DVertex *objverts, uint16 *indices)
{
CamSpace3DVertex *camverts;
Im2DVertex *scrverts;
uint16 *cullindices, *clipindices;
uint32 numClip;
camverts = rwNewT(CamSpace3DVertex, mstate->numVertices, MEMDUR_FUNCTION);
scrverts = rwNewT(Im2DVertex, mstate->numVertices, MEMDUR_FUNCTION);
cullindices = rwNewT(uint16, mstate->numPrimitives*3, MEMDUR_FUNCTION);
transform(mstate, objverts, camverts, scrverts);
numClip = cullTriangles(mstate, camverts, indices, cullindices);
// int32 i;
// for(i = 0; i < mstate->numVertices; i++){
// if(camverts[i].clipFlags & CLIPX)
// camverts[i].color.red = 255;
// if(camverts[i].clipFlags & CLIPY)
// camverts[i].color.green = 255;
// if(camverts[i].clipFlags & CLIPZ)
// camverts[i].color.blue = 255;
// }
light(mstate, objverts, camverts);
// mstate->matCol.red = 255;
// mstate->matCol.green = 255;
// mstate->matCol.blue = 255;
postlight(mstate, camverts, scrverts);
// each triangle can have a maximum of 9 vertices (7 triangles) after clipping
// so resize to whatever we may need
camverts = rwResizeT(CamSpace3DVertex, camverts, mstate->numVertices + numClip*9, MEMDUR_FUNCTION);
scrverts = rwResizeT(Im2DVertex, scrverts, mstate->numVertices + numClip*9, MEMDUR_FUNCTION);
clipindices = rwNewT(uint16, mstate->numPrimitives*3 + numClip*7*3, MEMDUR_FUNCTION);
clipTriangles(mstate, camverts, scrverts, cullindices, clipindices);
submitTriangles(scrverts, mstate->numVertices, clipindices, mstate->numPrimitives);
rwFree(camverts);
rwFree(scrverts);
rwFree(cullindices);
rwFree(clipindices);
}
static void
drawAtomic(Atomic *atomic)
{
MeshState mstate;
Matrix lightmat;
Geometry *geo;
MeshHeader *header;
InstanceData *inst;
int i;
Camera *cam = (Camera*)engine->currentCamera;
instanceAtomic(atomic);
mstate.obj2world = *atomic->getFrame()->getLTM();
mstate.obj2cam = mstate.obj2world;
mstate.obj2cam.transform(&cam->viewMatrix, COMBINEPOSTCONCAT);
Matrix::invert(&lightmat, &mstate.obj2world);
enumLights(&lightmat);
geo = atomic->geometry;
header = geo->meshHeader;
inst = ((InstanceDataHeader*)geo->instData)->inst;
for(i = 0; i < header->numMeshes; i++){
mstate.flags = geo->flags;
mstate.numVertices = inst->numVertices;
mstate.numPrimitives = inst->numIndices / 3;
mstate.surfProps = inst->material->surfaceProps;
mstate.matCol = inst->material->color;
drawMesh(&mstate, inst->vertices, inst->indices);
inst++;
}
}
void
tlTest(Clump *clump)
{
FORLIST(lnk, clump->atomics){
Atomic *a = Atomic::fromClump(lnk);
drawAtomic(a);
}
}
}
static Im2DVertex *clipverts;
static int32 numClipverts;
void
genIm3DTransform(void *vertices, int32 numVertices, Matrix *world)
{
Im3DVertex *objverts;
V3d pos;
Matrix xform;
Camera *cam;
int32 i;
objverts = (Im3DVertex*)vertices;
cam = (Camera*)engine->currentCamera;
int32 width = cam->frameBuffer->width;
int32 height = cam->frameBuffer->height;
xform = cam->viewMatrix;
if(world)
xform.transform(world, COMBINEPRECONCAT);
clipverts = rwNewT(Im2DVertex, numVertices, MEMDUR_EVENT);
numClipverts = numVertices;
for(i = 0; i < numVertices; i++){
V3d::transformPoints(&pos, &objverts[i].position, 1, &xform);
float32 recipZ = 1.0f/pos.z;
RGBA c = objverts[i].getColor();
clipverts[i].setScreenX(pos.x * recipZ * width);
clipverts[i].setScreenY(pos.y * recipZ * height);
clipverts[i].setScreenZ(recipZ * cam->zScale + cam->zShift);
clipverts[i].setCameraZ(pos.z);
clipverts[i].setRecipCameraZ(recipZ);
clipverts[i].setColor(c.red, c.green, c.blue, c.alpha);
clipverts[i].setU(objverts[i].u, recipZ);
clipverts[i].setV(objverts[i].v, recipZ);
}
}
void
genIm3DRenderIndexed(PrimitiveType prim, void *indices, int32 numIndices)
{
im2d::RenderIndexedPrimitive(prim, clipverts, numClipverts, indices, numIndices);
}
void
genIm3DEnd(void)
{
rwFree(clipverts);
clipverts = nil;
numClipverts = 0;
}