summaryrefslogtreecommitdiff
path: root/tools/playground
diff options
context:
space:
mode:
authorclaude-bot <[email protected]>2026-07-13 12:40:03 +0000
committerclaude-bot <[email protected]>2026-07-13 12:40:03 +0000
commit847962910f0bff071f3bf07c9abb87764fb6cac3 (patch)
treeddcd429e134c7fd5f72ddc97ced175de8d66fcd0 /tools/playground
downloadlibrw-master.tar.gz
librw-master.zip
Import aap/librw @ masterHEADmaster
Snapshot for re3/reVC vendoring, per @lzcnt. Source: https://github.com/aap/librw (master).
Diffstat (limited to 'tools/playground')
-rw-r--r--tools/playground/CMakeLists.txt22
-rw-r--r--tools/playground/camera.cpp134
-rw-r--r--tools/playground/camera.h26
-rw-r--r--tools/playground/files/Bm437_IBM_BIOS.FONbin0 -> 4064 bytes
-rw-r--r--tools/playground/files/Bm437_IBM_BIOS.tgabin0 -> 65554 bytes
-rw-r--r--tools/playground/files/Bm437_IBM_VGA8.FONbin0 -> 6112 bytes
-rw-r--r--tools/playground/files/Bm437_IBM_VGA8.tgabin0 -> 131090 bytes
-rw-r--r--tools/playground/files/foobar.tgabin0 -> 65554 bytes
-rw-r--r--tools/playground/files/maze.tgabin0 -> 49196 bytes
-rw-r--r--tools/playground/files/teapot.dffbin0 -> 42796 bytes
-rw-r--r--tools/playground/font.cpp181
-rw-r--r--tools/playground/main.cpp641
-rw-r--r--tools/playground/ras_test.cpp902
-rw-r--r--tools/playground/splines.cpp750
-rw-r--r--tools/playground/tl_tests.cpp766
15 files changed, 3422 insertions, 0 deletions
diff --git a/tools/playground/CMakeLists.txt b/tools/playground/CMakeLists.txt
new file mode 100644
index 0000000..120f7bc
--- /dev/null
+++ b/tools/playground/CMakeLists.txt
@@ -0,0 +1,22 @@
+add_executable(playground WIN32
+ camera.cpp
+ font.cpp
+ main.cpp
+ ras_test.cpp
+ splines.cpp
+ tl_tests.cpp
+)
+
+target_link_libraries(playground
+ PRIVATE
+ librw::skeleton
+ librw::librw
+)
+
+add_custom_command(
+ TARGET playground POST_BUILD
+ COMMAND "${CMAKE_COMMAND}" -E make_directory "$<TARGET_FILE_DIR:playground>/files"
+ COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/files" "$<TARGET_FILE_DIR:playground>/files"
+)
+
+librw_platform_target(playground)
diff --git a/tools/playground/camera.cpp b/tools/playground/camera.cpp
new file mode 100644
index 0000000..cf45f5f
--- /dev/null
+++ b/tools/playground/camera.cpp
@@ -0,0 +1,134 @@
+#include <cstdio>
+#include <cassert>
+
+#include <rw.h>
+
+#define PI 3.14159265359f
+#include "camera.h"
+
+using rw::Quat;
+using rw::V3d;
+
+void
+Camera::update(void)
+{
+ if(m_rwcam){
+ m_rwcam->setNearPlane(m_near);
+ m_rwcam->setFarPlane(m_far);
+ m_rwcam->setFOV(m_fov, m_aspectRatio);
+
+ rw::Frame *f = m_rwcam->getFrame();
+ if(f){
+ V3d forward = normalize(sub(m_target, m_position));
+ V3d left = normalize(cross(m_up, forward));
+ V3d nup = cross(forward, left);
+ f->matrix.right = left; // lol
+ f->matrix.up = nup;
+ f->matrix.at = forward;
+ f->matrix.pos = m_position;
+ f->matrix.optimize();
+ f->updateObjects();
+ }
+ }
+}
+
+void
+Camera::setTarget(V3d target)
+{
+ m_position = sub(m_position, sub(m_target, target));
+ m_target = target;
+}
+
+float
+Camera::getHeading(void)
+{
+ V3d dir = sub(m_target, m_position);
+ float a = atan2(dir.y, dir.x)-PI/2.0f;
+ return m_localup.z < 0.0f ? a-PI : a;
+}
+
+void
+Camera::turn(float yaw, float pitch)
+{
+ V3d dir = sub(m_target, m_position);
+ Quat r = Quat::rotation(yaw, rw::makeV3d(0.0f, 0.0f, 1.0f));
+ dir = rotate(dir, r);
+ m_localup = rotate(m_localup, r);
+
+ V3d right = normalize(cross(dir, m_localup));
+ r = Quat::rotation(pitch, right);
+ dir = rotate(dir, r);
+ m_localup = normalize(cross(right, dir));
+ if(m_localup.z >= 0.0) m_up.z = 1.0;
+ else m_up.z = -1.0f;
+
+ m_target = add(m_position, dir);
+}
+
+void
+Camera::orbit(float yaw, float pitch)
+{
+ V3d dir = sub(m_target, m_position);
+ Quat r = Quat::rotation(yaw, rw::makeV3d(0.0f, 0.0f, 1.0f));
+ dir = rotate(dir, r);
+ m_localup = rotate(m_localup, r);
+
+ V3d right = normalize(cross(dir, m_localup));
+ r = Quat::rotation(-pitch, right);
+ dir = rotate(dir, r);
+ m_localup = normalize(cross(right, dir));
+ if(m_localup.z >= 0.0) m_up.z = 1.0;
+ else m_up.z = -1.0f;
+
+ m_position = sub(m_target, dir);
+}
+
+void
+Camera::dolly(float dist)
+{
+ V3d dir = setlength(sub(m_target, m_position), dist);
+ m_position = add(m_position, dir);
+ m_target = add(m_target, dir);
+}
+
+void
+Camera::zoom(float dist)
+{
+ V3d dir = sub(m_target, m_position);
+ float curdist = length(dir);
+ if(dist >= curdist)
+ dist = curdist-0.01f;
+ dir = setlength(dir, dist);
+ m_position = add(m_position, dir);
+}
+
+void
+Camera::pan(float x, float y)
+{
+ V3d dir = normalize(sub(m_target, m_position));
+ V3d right = normalize(cross(dir, m_up));
+ V3d localup = normalize(cross(right, dir));
+ dir = add(scale(right, x), scale(localup, y));
+ m_position = add(m_position, dir);
+ m_target = add(m_target, dir);
+}
+
+float
+Camera::distanceTo(V3d v)
+{
+ return length(sub(m_position, v));
+}
+
+Camera::Camera()
+{
+ m_position.set(0.0f, 6.0f, 0.0f);
+ m_target.set(0.0f, 0.0f, 0.0f);
+ m_up.set(0.0f, 0.0f, 1.0f);
+ m_localup = m_up;
+ m_fov = 70.0f;
+ m_aspectRatio = 1.0f;
+ m_near = 0.1f;
+ m_far = 100.0f;
+ m_rwcam = NULL;
+}
+
diff --git a/tools/playground/camera.h b/tools/playground/camera.h
new file mode 100644
index 0000000..8a0315d
--- /dev/null
+++ b/tools/playground/camera.h
@@ -0,0 +1,26 @@
+class Camera
+{
+public:
+ rw::Camera *m_rwcam;
+ rw::V3d m_position;
+ rw::V3d m_target;
+ rw::V3d m_up;
+ rw::V3d m_localup;
+
+ float m_fov, m_aspectRatio;
+ float m_near, m_far;
+
+
+ void setTarget(rw::V3d target);
+ float getHeading(void);
+
+ void turn(float yaw, float pitch);
+ void orbit(float yaw, float pitch);
+ void dolly(float dist);
+ void zoom(float dist);
+ void pan(float x, float y);
+
+ void update(void);
+ float distanceTo(rw::V3d v);
+ Camera(void);
+};
diff --git a/tools/playground/files/Bm437_IBM_BIOS.FON b/tools/playground/files/Bm437_IBM_BIOS.FON
new file mode 100644
index 0000000..be2443f
--- /dev/null
+++ b/tools/playground/files/Bm437_IBM_BIOS.FON
Binary files differ
diff --git a/tools/playground/files/Bm437_IBM_BIOS.tga b/tools/playground/files/Bm437_IBM_BIOS.tga
new file mode 100644
index 0000000..50034b4
--- /dev/null
+++ b/tools/playground/files/Bm437_IBM_BIOS.tga
Binary files differ
diff --git a/tools/playground/files/Bm437_IBM_VGA8.FON b/tools/playground/files/Bm437_IBM_VGA8.FON
new file mode 100644
index 0000000..6ffa09a
--- /dev/null
+++ b/tools/playground/files/Bm437_IBM_VGA8.FON
Binary files differ
diff --git a/tools/playground/files/Bm437_IBM_VGA8.tga b/tools/playground/files/Bm437_IBM_VGA8.tga
new file mode 100644
index 0000000..521fb40
--- /dev/null
+++ b/tools/playground/files/Bm437_IBM_VGA8.tga
Binary files differ
diff --git a/tools/playground/files/foobar.tga b/tools/playground/files/foobar.tga
new file mode 100644
index 0000000..895bae0
--- /dev/null
+++ b/tools/playground/files/foobar.tga
Binary files differ
diff --git a/tools/playground/files/maze.tga b/tools/playground/files/maze.tga
new file mode 100644
index 0000000..4714c42
--- /dev/null
+++ b/tools/playground/files/maze.tga
Binary files differ
diff --git a/tools/playground/files/teapot.dff b/tools/playground/files/teapot.dff
new file mode 100644
index 0000000..7a65176
--- /dev/null
+++ b/tools/playground/files/teapot.dff
Binary files differ
diff --git a/tools/playground/font.cpp b/tools/playground/font.cpp
new file mode 100644
index 0000000..b001250
--- /dev/null
+++ b/tools/playground/font.cpp
@@ -0,0 +1,181 @@
+#include <rw.h>
+#include <skeleton.h>
+
+using namespace rw;
+
+struct Font
+{
+ Texture *tex;
+ int32 glyphwidth, glyphheight;
+ int32 numglyphs;
+};
+Font vga = { nil, 8, 16, 256 };
+Font bios = { nil, 8, 8, 256 };
+Font *curfont = &bios;
+
+#define NUMCHARS 100
+uint16 indices[NUMCHARS*6];
+RWDEVICE::Im2DVertex vertices[NUMCHARS*4];
+int32 curVert;
+int32 curIndex;
+
+void
+printScreen(const char *s, float32 x, float32 y)
+{
+ char c;
+ Camera *cam;
+ RWDEVICE::Im2DVertex *vert;
+ uint16 *ix;
+ curVert = 0;
+ curIndex = 0;
+ float32 u, v, du, dv;
+ float recipZ;
+
+ cam = (Camera*)engine->currentCamera;
+ vert = &vertices[curVert];
+ ix = &indices[curIndex];
+ du = curfont->glyphwidth/(float32)curfont->tex->raster->width;
+ dv = curfont->glyphheight/(float32)curfont->tex->raster->height;
+ recipZ = 1.0f/cam->nearPlane;
+ while(c = *s){
+ if(c >= curfont->numglyphs)
+ c = 0;
+ u = (c % 16)*curfont->glyphwidth / (float32)curfont->tex->raster->width;
+ v = (c / 16)*curfont->glyphheight / (float32)curfont->tex->raster->height;
+
+ vert->setScreenX(x);
+ vert->setScreenY(y);
+ vert->setScreenZ(rw::im2d::GetNearZ());
+ vert->setCameraZ(cam->nearPlane);
+ vert->setRecipCameraZ(recipZ);
+ vert->setColor(255, 255, 255, 255);
+ vert->setU(u, recipZ);
+ vert->setV(v, recipZ);
+ vert++;
+
+ vert->setScreenX(x+curfont->glyphwidth);
+ vert->setScreenY(y);
+ vert->setScreenZ(rw::im2d::GetNearZ());
+ vert->setCameraZ(cam->nearPlane);
+ vert->setRecipCameraZ(recipZ);
+ vert->setColor(255, 255, 255, 255);
+ vert->setU(u+du, recipZ);
+ vert->setV(v, recipZ);
+ vert++;
+
+ vert->setScreenX(x);
+ vert->setScreenY(y+curfont->glyphheight);
+ vert->setScreenZ(rw::im2d::GetNearZ());
+ vert->setCameraZ(cam->nearPlane);
+ vert->setRecipCameraZ(recipZ);
+ vert->setColor(255, 255, 255, 255);
+ vert->setU(u, recipZ);
+ vert->setV(v+dv, recipZ);
+ vert++;
+
+ vert->setScreenX(x+curfont->glyphwidth);
+ vert->setScreenY(y+curfont->glyphheight);
+ vert->setScreenZ(rw::im2d::GetNearZ());
+ vert->setCameraZ(cam->nearPlane);
+ vert->setRecipCameraZ(recipZ);
+ vert->setColor(255, 255, 255, 255);
+ vert->setU(u+du, recipZ);
+ vert->setV(v+dv, recipZ);
+ vert++;
+
+ *ix++ = curVert;
+ *ix++ = curVert+1;
+ *ix++ = curVert+2;
+ *ix++ = curVert+2;
+ *ix++ = curVert+1;
+ *ix++ = curVert+3;
+
+ curVert += 4;
+ curIndex += 6;
+ x += curfont->glyphwidth+1;
+
+ s++;
+ }
+
+ rw::SetRenderStatePtr(rw::TEXTURERASTER, curfont->tex->raster);
+ rw::SetRenderState(rw::TEXTUREADDRESS, rw::Texture::WRAP);
+ rw::SetRenderState(rw::TEXTUREFILTER, rw::Texture::NEAREST);
+
+ im2d::RenderIndexedPrimitive(rw::PRIMTYPETRILIST,
+ vertices, curVert, indices, curIndex);
+
+}
+
+void
+initFont(void)
+{
+ vga.tex = Texture::read("files/Bm437_IBM_VGA8", "");
+ bios.tex = Texture::read("files/Bm437_IBM_BIOS", "");
+
+/*
+ FILE *foo = fopen("font.c", "w");
+ assert(foo);
+ int x, y;
+ rw::Image *img = rw::readTGA("vga_font.tga");
+ assert(img);
+ for(y = 0; y < img->height; y++){
+ for(x = 0; x < img->width; x++)
+ fprintf(foo, "%d, ", !!img->pixels[y*img->width + x]);
+ fprintf(foo, "\n");
+ }
+*/
+}
+
+/*
+#define NUMGLYPHS 256
+#define GLYPHWIDTH 8
+#define GLYPHHEIGHT 16
+
+
+void
+convertFont(void)
+{
+ FILE *f;
+ Image *img;
+ uint8 data[NUMGLYPHS*GLYPHHEIGHT];
+ int32 i, x, y;
+ uint8 *px, *line, *glyph;
+// f = fopen("font0.bin", "rb");
+ f = fopen("files/Bm437_IBM_VGA8.FON", "rb");
+// f = fopen("files/Bm437_IBM_BIOS.FON", "rb");
+ if(f == nil)
+ return;
+fseek(f, 0x65A, 0);
+ fread(data, 1, NUMGLYPHS*GLYPHHEIGHT, f);
+ fclose(f);
+
+ img = Image::create(16*GLYPHWIDTH, NUMGLYPHS/16*GLYPHHEIGHT, 32);
+ img->allocate();
+ for(i = 0; i < NUMGLYPHS; i++){
+ glyph = &data[i*GLYPHHEIGHT];
+ x = (i % 16)*GLYPHWIDTH;
+ y = (i / 16)*GLYPHHEIGHT;
+ line = &img->pixels[x*4 + y*img->stride];
+ for(y = 0; y < GLYPHHEIGHT; y++){
+ px = line;
+ for(x = 0; x < 8; x++){
+ if(*glyph & 1<<(8-x)){
+ *px++ = 255;
+ *px++ = 255;
+ *px++ = 255;
+ *px++ = 255;
+ }else{
+ *px++ = 0;
+ *px++ = 0;
+ *px++ = 0;
+ *px++ = 0;
+ }
+ }
+ glyph++;
+ line += img->stride;
+ }
+ }
+// writeTGA(img, "files/Bm437_IBM_BIOS.tga");
+ writeTGA(img, "files/Bm437_IBM_VGA8.tga");
+}
+*/ \ No newline at end of file
diff --git a/tools/playground/main.cpp b/tools/playground/main.cpp
new file mode 100644
index 0000000..043b3c6
--- /dev/null
+++ b/tools/playground/main.cpp
@@ -0,0 +1,641 @@
+#include <rw.h>
+#include <skeleton.h>
+#include "camera.h"
+#include <assert.h>
+
+rw::V3d zero = { 0.0f, 0.0f, 0.0f };
+Camera *camera;
+struct SceneGlobals {
+ rw::World *world;
+ rw::Camera *camera;
+ rw::Clump *clump;
+} Scene;
+rw::Texture *tex, *tex2;
+rw::Raster *testras;
+rw::EngineOpenParams engineOpenParams;
+
+rw::Texture *frontbuffer;
+
+bool dosoftras = 0;
+
+namespace gen {
+void tlTest(rw::Clump *clump);
+}
+void genIm3DTransform(void *vertices, rw::int32 numVertices, rw::Matrix *xform);
+void genIm3DRenderIndexed(rw::PrimitiveType prim, void *indices, rw::int32 numIndices);
+void genIm3DEnd(void);
+void initFont(void);
+void printScreen(const char *s, float x, float y);
+
+void initsplines(void);
+void rendersplines(void);
+
+rw::Charset *testfont;
+
+//#include <Windows.h>
+
+void
+Init(void)
+{
+// AllocConsole();
+// freopen("CONIN$", "r", stdin);
+// freopen("CONOUT$", "w", stdout);
+// freopen("CONOUT$", "w", stderr);
+
+ sk::globals.windowtitle = "Clump viewer";
+ sk::globals.width = 640;
+ sk::globals.height = 448;
+ sk::globals.quit = 0;
+}
+
+bool
+attachPlugins(void)
+{
+ rw::ps2::registerPDSPlugin(40);
+ rw::ps2::registerPluginPDSPipes();
+
+ rw::registerMeshPlugin();
+ rw::registerNativeDataPlugin();
+ rw::registerAtomicRightsPlugin();
+ rw::registerMaterialRightsPlugin();
+ rw::xbox::registerVertexFormatPlugin();
+ rw::registerSkinPlugin();
+ rw::registerUserDataPlugin();
+ rw::registerHAnimPlugin();
+ rw::registerMatFXPlugin();
+ rw::registerUVAnimPlugin();
+ rw::ps2::registerADCPlugin();
+ return true;
+}
+
+void
+dumpUserData(rw::UserDataArray *ar)
+{
+ int i;
+ printf("name: %s\n", ar->name);
+ for(i = 0; i < ar->numElements; i++){
+ switch(ar->datatype){
+ case rw::USERDATAINT:
+ printf(" %d\n", ar->getInt(i));
+ break;
+ case rw::USERDATAFLOAT:
+ printf(" %f\n", ar->getFloat(i));
+ break;
+ case rw::USERDATASTRING:
+ printf(" %s\n", ar->getString(i));
+ break;
+ }
+ }
+}
+
+static rw::Frame*
+dumpFrameUserDataCB(rw::Frame *f, void*)
+{
+ using namespace rw;
+ int32 i;
+ UserDataArray *ar;
+ int32 n = UserDataArray::frameGetCount(f);
+ for(i = 0; i < n; i++){
+ ar = UserDataArray::frameGet(f, i);
+ dumpUserData(ar);
+ }
+ f->forAllChildren(dumpFrameUserDataCB, nil);
+ return f;
+}
+
+void
+dumpUserData(rw::Clump *clump)
+{
+ printf("Frames\n");
+ dumpFrameUserDataCB(clump->getFrame(), nil);
+}
+
+static rw::Frame*
+getHierCB(rw::Frame *f, void *data)
+{
+ using namespace rw;
+ HAnimData *hd = rw::HAnimData::get(f);
+ if(hd->hierarchy){
+ *(HAnimHierarchy**)data = hd->hierarchy;
+ return nil;
+ }
+ f->forAllChildren(getHierCB, data);
+ return f;
+}
+
+rw::HAnimHierarchy*
+getHAnimHierarchyFromClump(rw::Clump *clump)
+{
+ using namespace rw;
+ HAnimHierarchy *hier = nil;
+ getHierCB(clump->getFrame(), &hier);
+ return hier;
+}
+
+void
+setupAtomic(rw::Atomic *atomic)
+{
+ using namespace rw;
+ // just remove pipelines that we can't handle for now
+// if(atomic->pipeline && atomic->pipeline->platform != rw::platform)
+ atomic->pipeline = NULL;
+
+ // Attach hierarchy to atomic if we're skinned
+ HAnimHierarchy *hier = getHAnimHierarchyFromClump(atomic->clump);
+ if(hier)
+ Skin::setHierarchy(atomic, hier);
+}
+
+static void
+initHierFromFrames(rw::HAnimHierarchy *hier)
+{
+ using namespace rw;
+ int32 i;
+ for(i = 0; i < hier->numNodes; i++){
+ if(hier->nodeInfo[i].frame){
+ hier->matrices[hier->nodeInfo[i].index] = *hier->nodeInfo[i].frame->getLTM();
+ }else
+ assert(0);
+ }
+}
+
+void
+setupClump(rw::Clump *clump)
+{
+ using namespace rw;
+ HAnimHierarchy *hier = getHAnimHierarchyFromClump(clump);
+ if(hier){
+ hier->attach();
+ initHierFromFrames(hier);
+ }
+
+ FORLIST(lnk, clump->atomics){
+ rw::Atomic *a = rw::Atomic::fromClump(lnk);
+ setupAtomic(a);
+ }
+}
+
+#define MUL(x, y) ((x)*(y)/255)
+
+int
+calcVCfx(int fb, int col, int a, int iter)
+{
+ int prev = fb;
+ int col2 = col*2;
+ if(col2 > 255) col2 = 255;
+ for(int i = 0; i < iter; i++){
+ int tmp = MUL(fb, 255-a) + MUL(MUL(prev, col2), a);
+ tmp += MUL(prev, col);
+ tmp += MUL(prev, col);
+ prev = tmp > 255 ? 255 : tmp;
+ }
+ return prev;
+}
+
+int
+calcIIIfx(int fb, int col, int a, int iter)
+{
+ int prev = fb;
+ for(int i = 0; i < iter; i++){
+ int tmp = MUL(fb, 255-a) + MUL(MUL(prev, col), a);
+ prev = tmp > 255 ? 255 : tmp;
+ }
+ return prev;
+}
+
+void
+postfxtest(void)
+{
+ rw::Image *img = rw::Image::create(256, 256, 32);
+ img->allocate();
+ int x, y;
+ int iter;
+ static char filename[100];
+ for(iter = 0; iter < 10; iter++){
+ for(y = 0; y < 256; y++)
+ for(x = 0; x < 256; x++){
+ int res = calcVCfx(y, x, 30, iter);
+// int res = calcIIIfx(y, x, 30, iter);
+ if(0 && res == y){
+ img->pixels[y*img->stride + x*img->bpp + 0] = 255;
+ img->pixels[y*img->stride + x*img->bpp + 1] = 0;
+ img->pixels[y*img->stride + x*img->bpp + 2] = 0;
+ }else{
+ img->pixels[y*img->stride + x*img->bpp + 0] = res;
+ img->pixels[y*img->stride + x*img->bpp + 1] = res;
+ img->pixels[y*img->stride + x*img->bpp + 2] = res;
+ }
+ img->pixels[y*img->stride + x*img->bpp + 3] = 255;
+ }
+ sprintf(filename, "vcfx_%02d.bmp", iter);
+// sprintf(filename, "iiifx_%02d.bmp", iter);
+ rw::writeBMP(img, filename);
+ }
+ exit(0);
+}
+
+bool
+InitRW(void)
+{
+// rw::platform = rw::PLATFORM_D3D8;
+ if(!sk::InitRW())
+ return false;
+
+ rw::d3d::isP8supported = false;
+
+// postfxtest();
+
+ initFont();
+
+ rw::RGBA foreground = { 255, 255, 0, 255 };
+ rw::RGBA background = { 0, 0, 0, 0 };
+ rw::Charset::open();
+ testfont = rw::Charset::create(&foreground, &background);
+ assert(testfont);
+ foreground.blue = 255.0f;
+ testfont->setColors(&foreground, &background);
+
+ tex = rw::Texture::read("files/maze", nil);
+ tex2 = rw::Texture::read("files/checkers", nil);
+
+ const char *filename = "files/teapot.dff";
+ if(sk::args.argc > 1)
+ filename = sk::args.argv[1];
+ rw::StreamFile in;
+ if(in.open(filename, "rb") == NULL){
+ printf("couldn't open file\n");
+ return false;
+ }
+ rw::findChunk(&in, rw::ID_CLUMP, NULL, NULL);
+ Scene.clump = rw::Clump::streamRead(&in);
+ assert(Scene.clump);
+ in.close();
+
+ // TEST - Set texture to the all materials of the clump
+// FORLIST(lnk, Scene.clump->atomics){
+// rw::Atomic *a = rw::Atomic::fromClump(lnk);
+// for(int i = 0; i < a->geometry->matList.numMaterials; i++)
+// a->geometry->matList.materials[i]->setTexture(tex);
+// }
+
+ Scene.clump->getFrame()->translate(&zero, rw::COMBINEREPLACE);
+
+ dumpUserData(Scene.clump);
+ setupClump(Scene.clump);
+
+ Scene.world = rw::World::create();
+
+ rw::Light *ambient = rw::Light::create(rw::Light::AMBIENT);
+ ambient->setColor(0.3f, 0.3f, 0.3f);
+ Scene.world->addLight(ambient);
+
+ rw::V3d xaxis = { 1.0f, 0.0f, 0.0f };
+ rw::Light *direct = rw::Light::create(rw::Light::DIRECTIONAL);
+ direct->setColor(0.8f, 0.8f, 0.8f);
+ direct->setFrame(rw::Frame::create());
+ direct->getFrame()->rotate(&xaxis, 180.0f, rw::COMBINEREPLACE);
+ Scene.world->addLight(direct);
+
+ camera = new Camera;
+ Scene.camera = sk::CameraCreate(sk::globals.width, sk::globals.height, 1);
+ camera->m_rwcam = Scene.camera;
+ camera->m_aspectRatio = 640.0f/448.0f;
+// camera->m_near = 0.5f;
+ camera->m_near = 1.5f;
+ camera->m_far = 450.0f;
+// camera->m_far = 15.0f;
+ camera->m_target.set(0.0f, 0.0f, 0.0f);
+ camera->m_position.set(0.0f, -10.0f, 0.0f);
+// camera->setPosition(Vec3(0.0f, 5.0f, 0.0f));
+// camera->setPosition(Vec3(0.0f, -70.0f, 0.0f));
+// camera->setPosition(Vec3(0.0f, -1.0f, 3.0f));
+ camera->update();
+
+ Scene.world->addCamera(camera->m_rwcam);
+
+ initsplines();
+
+ return true;
+}
+
+void
+im2dtest(void)
+{
+ using namespace rw::RWDEVICE;
+ int i;
+ static struct
+ {
+ float x, y;
+ rw::uint8 r, g, b, a;
+ float u, v;
+ } vs[4] = {
+ { 0.0f, 0.0f, 255, 0, 0, 128, 0.0f, 0.0f },
+ { 640.0f, 0.0f, 0, 255, 0, 128, 1.0f, 0.0f },
+ { 0.0f, 448.0f, 0, 0, 255, 128, 0.0f, 1.0f },
+ { 640.0f, 448.0f, 0, 255, 255, 128, 1.0f, 1.0f },
+/*
+ { 0.0f, 0.0f, 255, 0, 0, 128, 0.0f, 1.0f },
+ { 640.0f, 0.0f, 0, 255, 0, 128, 0.0f, 0.0f },
+ { 0.0f, 448.0f, 0, 0, 255, 128, 1.0f, 1.0f },
+ { 640.0f, 448.0f, 0, 255, 255, 128, 1.0f, 0.0f },
+*/
+ };
+ Im2DVertex verts[4];
+ static short indices[] = {
+ 0, 1, 2, 3
+ };
+
+ float recipZ = 1.0f/Scene.camera->nearPlane;
+ for(i = 0; i < 4; i++){
+ verts[i].setScreenX(vs[i].x);
+ verts[i].setScreenY(vs[i].y);
+ verts[i].setScreenZ(rw::im2d::GetNearZ());
+ verts[i].setCameraZ(Scene.camera->nearPlane);
+ verts[i].setRecipCameraZ(recipZ);
+ verts[i].setColor(vs[i].r, vs[i].g, vs[i].b, vs[i].a);
+ if(dosoftras)
+ verts[i].setColor(255, 255, 255, 255);
+ verts[i].setU(vs[i].u + 0.5f/640.0f, recipZ);
+ verts[i].setV(vs[i].v + 0.5f/448.0f, recipZ);
+ }
+
+ rw::SetRenderStatePtr(rw::TEXTURERASTER, tex->raster);
+ if(dosoftras)
+ rw::SetRenderStatePtr(rw::TEXTURERASTER, testras);
+ rw::SetRenderState(rw::TEXTUREADDRESS, rw::Texture::WRAP);
+ rw::SetRenderState(rw::TEXTUREFILTER, rw::Texture::NEAREST);
+ rw::SetRenderState(rw::VERTEXALPHA, 1);
+ rw::im2d::RenderIndexedPrimitive(rw::PRIMTYPETRISTRIP,
+ &verts, 4, &indices, 4);
+}
+
+void
+im2dtest2(void)
+{
+ using namespace rw::RWDEVICE;
+ int i;
+ rw::Camera *cam = Scene.camera;
+ float n = cam->nearPlane;
+ float f = cam->farPlane;
+ float mid = (n+f)/4.0f;
+ struct
+ {
+ float x, y, z;
+ rw::uint8 r, g, b, a;
+ float u, v;
+ } vs[4] = {
+ { 0.5f, 0.5f, n, 255, 255, 255, 255, 0.0f, 0.0f },
+ { 0.5f, 0.5f, mid, 255, 255, 255, 255, 1.0f, 0.0f },
+ { 0.5f, -0.5f, n, 255, 255, 255, 255, 0.0f, 1.0f },
+ { 0.5f, -0.5f, mid, 255, 255, 255, 255, 1.0f, 1.0f },
+ };
+ Im2DVertex verts[4];
+ static short indices[] = {
+ 0, 1, 2, 3
+ };
+
+ for(i = 0; i < 4; i++){
+ float recipZ = 1.0f/vs[i].z;
+ verts[i].setScreenX((vs[i].x*recipZ + 0.5f) * 640.0f);
+ verts[i].setScreenY((vs[i].y*recipZ + 0.5f) * 448.0f);
+ verts[i].setScreenZ(recipZ * cam->zScale + cam->zShift);
+// verts[i].setCameraZ(vs[i].z);
+ verts[i].setRecipCameraZ(recipZ);
+ verts[i].setColor(vs[i].r, vs[i].g, vs[i].b, vs[i].a);
+ if(dosoftras)
+ verts[i].setColor(255, 255, 255, 255);
+ verts[i].setU(vs[i].u + 0.5f/640.0f, recipZ);
+ verts[i].setV(vs[i].v + 0.5f/448.0f, recipZ);
+ }
+
+ rw::SetRenderStatePtr(rw::TEXTURERASTER, tex->raster);
+ rw::SetRenderState(rw::TEXTUREADDRESS, rw::Texture::WRAP);
+ rw::SetRenderState(rw::TEXTUREFILTER, rw::Texture::NEAREST);
+ rw::SetRenderState(rw::VERTEXALPHA, 1);
+ rw::im2d::RenderIndexedPrimitive(rw::PRIMTYPETRISTRIP,
+ &verts, 4, &indices, 4);
+}
+
+void
+im3dtest(void)
+{
+ using namespace rw::RWDEVICE;
+ int i;
+ static struct
+ {
+ float x, y, z;
+ rw::uint8 r, g, b, a;
+ float u, v;
+ } vs[8] = {
+ { -1.0f, -1.0f, -1.0f, 255, 0, 0, 128, 0.0f, 0.0f },
+ { -1.0f, 1.0f, -1.0f, 0, 255, 0, 128, 0.0f, 1.0f },
+ { 1.0f, -1.0f, -1.0f, 0, 0, 255, 128, 1.0f, 0.0f },
+ { 1.0f, 1.0f, -1.0f, 255, 0, 255, 128, 1.0f, 1.0f },
+
+ { -1.0f, -1.0f, 1.0f, 255, 0, 0, 128, 0.0f, 0.0f },
+ { -1.0f, 1.0f, 1.0f, 0, 255, 0, 128, 0.0f, 1.0f },
+ { 1.0f, -1.0f, 1.0f, 0, 0, 255, 128, 1.0f, 0.0f },
+ { 1.0f, 1.0f, 1.0f, 255, 0, 255, 128, 1.0f, 1.0f },
+ };
+ Im3DVertex verts[8];
+ static short indices[2*6] = {
+ 0, 1, 2, 2, 1, 3,
+ 4, 5, 6, 6, 5, 7
+ };
+
+ for(i = 0; i < 8; i++){
+ verts[i].setX(vs[i].x);
+ verts[i].setY(vs[i].y);
+ verts[i].setZ(vs[i].z);
+ verts[i].setColor(vs[i].r, vs[i].g, vs[i].b, vs[i].a);
+ verts[i].setU(vs[i].u);
+ verts[i].setV(vs[i].v);
+ }
+
+ rw::SetRenderStatePtr(rw::TEXTURERASTER, tex->raster);
+// rw::SetRenderStatePtr(rw::TEXTURERASTER, testfont->raster);
+// rw::SetRenderStatePtr(rw::TEXTURERASTER, frontbuffer->raster);
+ rw::SetRenderState(rw::TEXTUREADDRESS, rw::Texture::WRAP);
+ rw::SetRenderState(rw::TEXTUREFILTER, rw::Texture::NEAREST);
+
+/*
+ genIm3DTransform(verts, 8, nil);
+ genIm3DRenderIndexed(rw::PRIMTYPETRILIST, indices, 12);
+ genIm3DEnd();
+*/
+ rw::im3d::Transform(verts, 8, nil, rw::im3d::EVERYTHING);
+ rw::im3d::RenderIndexedPrimitive(rw::PRIMTYPETRILIST, indices, 12);
+ rw::im3d::End();
+}
+
+void
+getFrontBuffer(void)
+{
+ rw::Raster *fb = Scene.camera->frameBuffer;
+
+ if(frontbuffer == nil || fb->width > frontbuffer->raster->width || fb->height > frontbuffer->raster->height){
+ int w, h;
+ for(w = 1; w < fb->width; w <<= 1);
+ for(h = 1; h < fb->height; h <<= 1);
+ rw::Raster *ras = rw::Raster::create(w, h, fb->depth, rw::Raster::CAMERATEXTURE);
+ if(frontbuffer){
+ frontbuffer->raster->destroy();
+ frontbuffer->raster = ras;
+ }else
+ frontbuffer = rw::Texture::create(ras);
+ printf("created FB with %d %d %d\n", ras->width, ras->height, ras->depth);
+ }
+
+ rw::Raster::pushContext(frontbuffer->raster);
+ fb->renderFast(0, 0);
+ rw::Raster::popContext();
+}
+
+void
+Draw(float timeDelta)
+{
+ getFrontBuffer();
+
+ rw::SetRenderState(rw::FOGCOLOR, 0xFF0000FF);
+// rw::SetRenderState(rw::FOGENABLE, 1);
+ camera->m_rwcam->fogPlane = camera->m_rwcam->nearPlane;
+
+ static rw::RGBA clearcol = { 161, 161, 161, 0xFF };
+ camera->m_rwcam->clear(&clearcol, rw::Camera::CLEARIMAGE|rw::Camera::CLEARZ);
+ camera->update();
+ camera->m_rwcam->beginUpdate();
+
+extern void beginSoftras(void);
+ beginSoftras();
+
+// gen::tlTest(Scene.clump);
+void drawtest(void);
+// drawtest();
+
+extern void endSoftras(void);
+ if(dosoftras){
+ endSoftras();
+ }
+ //im2dtest();
+ //im2dtest2();
+
+// Scene.clump->render();
+// im3dtest();
+// printScreen("Hello, World!", 10, 10);
+
+// testfont->print("foo ABC", 200, 200, true);
+
+ rendersplines();
+
+ camera->m_rwcam->endUpdate();
+
+ camera->m_rwcam->showRaster(0);
+}
+
+
+void
+KeyUp(int key)
+{
+}
+
+void
+KeyDown(int key)
+{
+ switch(key){
+ case 'W':
+ camera->orbit(0.0f, 0.1f);
+ break;
+ case 'S':
+ camera->orbit(0.0f, -0.1f);
+ break;
+ case 'A':
+ camera->orbit(-0.1f, 0.0f);
+ break;
+ case 'D':
+ camera->orbit(0.1f, 0.0f);
+ break;
+ case sk::KEY_UP:
+ camera->turn(0.0f, 0.1f);
+ break;
+ case sk::KEY_DOWN:
+ camera->turn(0.0f, -0.1f);
+ break;
+ case sk::KEY_LEFT:
+ camera->turn(0.1f, 0.0f);
+ break;
+ case sk::KEY_RIGHT:
+ camera->turn(-0.1f, 0.0f);
+ break;
+ case 'R':
+ camera->zoom(0.1f);
+ break;
+ case 'F':
+ camera->zoom(-0.1f);
+ break;
+ case 'V':
+ dosoftras = !dosoftras;
+ break;
+ case sk::KEY_ESC:
+ sk::globals.quit = 1;
+ break;
+ }
+}
+
+void
+MouseMove(int x, int y)
+{
+}
+
+void
+MouseButton(int buttons)
+{
+}
+
+sk::EventStatus
+AppEventHandler(sk::Event e, void *param)
+{
+ using namespace sk;
+ Rect *r;
+ MouseState *ms;
+
+ switch(e){
+ case INITIALIZE:
+ Init();
+ return EVENTPROCESSED;
+ case RWINITIALIZE:
+ return ::InitRW() ? EVENTPROCESSED : EVENTERROR;
+ case PLUGINATTACH:
+ return attachPlugins() ? EVENTPROCESSED : EVENTERROR;
+ case KEYDOWN:
+ KeyDown(*(int*)param);
+ return EVENTPROCESSED;
+ case KEYUP:
+ KeyUp(*(int*)param);
+ return EVENTPROCESSED;
+ case MOUSEBTN:
+ ms = (MouseState*)param;
+ MouseButton(ms->buttons);
+ return EVENTPROCESSED;
+ case MOUSEMOVE:
+ ms = (MouseState*)param;
+ MouseMove(ms->posx, ms->posy);
+ return EVENTPROCESSED;
+ case RESIZE:
+ r = (Rect*)param;
+ // TODO: register when we're minimized
+ if(r->w == 0) r->w = 1;
+ if(r->h == 0) r->h = 1;
+
+ sk::globals.width = r->w;
+ sk::globals.height = r->h;
+ if(camera)
+ camera->m_aspectRatio = (float)r->w/r->h;
+ if(Scene.camera)
+ sk::CameraSize(Scene.camera, r);
+ break;
+ case IDLE:
+ Draw(*(float*)param);
+ return EVENTPROCESSED;
+ }
+ return sk::EVENTNOTPROCESSED;
+}
diff --git a/tools/playground/ras_test.cpp b/tools/playground/ras_test.cpp
new file mode 100644
index 0000000..185fdec
--- /dev/null
+++ b/tools/playground/ras_test.cpp
@@ -0,0 +1,902 @@
+#include <rw.h>
+#include <skeleton.h>
+
+namespace rs {
+
+typedef int8_t i8;
+typedef uint8_t u8;
+typedef int16_t i16;
+typedef uint16_t u16;
+typedef int32_t i32;
+typedef uint32_t u32;
+typedef int64_t i64;
+typedef uint64_t u64;
+
+typedef struct Canvas Canvas;
+struct Canvas
+{
+ u8 *fb;
+ u32 *zbuf;
+ int w, h;
+};
+extern Canvas *canvas;
+
+typedef struct Texture Texture;
+struct Texture
+{
+ u8 *pixels;
+ int w, h;
+ int wrap;
+};
+
+typedef struct Point3 Point3;
+struct Point3
+{
+ int x, y, z;
+};
+
+typedef struct Color Color;
+struct Color
+{
+ u8 r, g, b, a;
+};
+
+typedef struct Vertex Vertex;
+struct Vertex
+{
+ i32 x, y, z;
+ float q; // 1/z
+ u8 r, g, b, a;
+ u8 f; // fog
+ float s, t;
+};
+
+Canvas *makecanvas(int w, int h);
+Texture *maketexture(int w, int h);
+void putpixel(Canvas *canvas, Point3 p, Color c);
+void clearcanvas(Canvas *canvas);
+void drawTriangle(Canvas *canvas, Vertex p1, Vertex p2, Vertex p3);
+
+// not good
+void drawRect(Canvas *canvas, Point3 p1, Point3 p2, Color c);
+void drawLine(Canvas *canvas, Point3 p1, Point3 p2, Color c);
+
+//#define trace(...) printf(__VA_ARGS__)
+#define trace(...)
+
+
+int clamp(int x);
+
+
+/*
+ * Render States
+ */
+enum TextureWrap {
+ WRAP_REPEAT,
+ WRAP_CLAMP,
+ WRAP_BORDER,
+};
+
+enum TextureFunction {
+ TFUNC_MODULATE,
+ TFUNC_DECAL,
+ TFUNC_HIGHLIGHT,
+ TFUNC_HIGHLIGHT2,
+};
+
+enum AlphaTestFunc {
+ ALPHATEST_NEVER,
+ ALPHATEST_ALWAYS,
+ ALPHATEST_LESS,
+ ALPHATEST_LEQUAL,
+ ALPHATEST_EQUAL,
+ ALPHATEST_GEQUAL,
+ ALPHATEST_GREATER,
+ ALPHATEST_NOTEQUAL,
+};
+
+enum AlphaTestFail {
+ ALPHAFAIL_KEEP,
+ ALPHAFAIL_FB_ONLY,
+ ALPHAFAIL_ZB_ONLY,
+};
+
+enum DepthTestFunc {
+ DEPTHTEST_NEVER,
+ DEPTHTEST_ALWAYS,
+ DEPTHTEST_GEQUAL,
+ DEPTHTEST_GREATER,
+};
+
+// The blend equation is
+// out = ((A - B) * C >> 7) + D
+// A, B and D select the color, C the alpha value
+enum AlphaBlendOp {
+ ALPHABLEND_SRC,
+ ALPHABLEND_DST,
+ ALPHABLEND_ZERO,
+ ALPHABLEND_FIX = ALPHABLEND_ZERO,
+};
+
+extern int srScissorX0, srScissorX1;
+extern int srScissorY0, srScissorY1;
+extern int srDepthTestEnable;
+extern int srDepthTestFunction;
+extern int srWriteZ;
+extern int srAlphaTestEnable;
+extern int srAlphaTestFunction;
+extern int srAlphaTestReference;
+extern int srAlphaTestFail;
+extern int srAlphaBlendEnable;
+extern int srAlphaBlendA;
+extern int srAlphaBlendB;
+extern int srAlphaBlendC;
+extern int srAlphaBlendD;
+extern int srAlphaBlendFix;
+extern int srTexEnable;
+extern Texture *srTexture;
+extern int srWrapU;
+extern int srWrapV;
+extern Color srBorder;
+extern int srTexUseAlpha;
+extern int srTexFunc;
+extern int srFogEnable;
+extern Color srFogCol;
+
+
+
+// end header
+
+
+
+
+#define CEIL(p) (((p)+15) >> 4)
+
+// render states
+int srScissorX0, srScissorX1;
+int srScissorY0, srScissorY1;
+int srDepthTestEnable = 1;
+int srDepthTestFunction = DEPTHTEST_GEQUAL;
+int srWriteZ = 1;
+int srAlphaTestEnable = 1;
+int srAlphaTestFunction = ALPHATEST_ALWAYS;
+int srAlphaTestReference;
+int srAlphaTestFail = ALPHAFAIL_FB_ONLY;
+int srAlphaBlendEnable = 1;
+int srAlphaBlendA = ALPHABLEND_SRC;
+int srAlphaBlendB = ALPHABLEND_DST;
+int srAlphaBlendC = ALPHABLEND_SRC;
+int srAlphaBlendD = ALPHABLEND_DST;
+int srAlphaBlendFix = 0x80;
+int srTexEnable = 0;
+Texture *srTexture;
+int srWrapU = WRAP_REPEAT;
+int srWrapV = WRAP_REPEAT;
+Color srBorder = { 255, 0, 0, 255 };
+int srTexUseAlpha = 1;
+int srTexFunc = TFUNC_MODULATE;
+int srFogEnable = 0;
+Color srFogCol = { 0, 0, 0, 0 };
+
+int clamp(int x) { if(x < 0) return 0; if(x > 255) return 255; return x; }
+
+Canvas*
+makecanvas(int w, int h)
+{
+ Canvas *canv;
+ canv = (Canvas*)malloc(sizeof(*canv) + w*h*(4+4));
+ canv->w = w;
+ canv->h = h;
+ canv->fb = ((u8*)canv + sizeof(*canv));
+ canv->zbuf = (u32*)(canv->fb + w*h*4);
+ return canv;
+}
+
+Texture*
+maketexture(int w, int h)
+{
+ Texture *t;
+ t = (Texture*)malloc(sizeof(*t) + w*h*4);
+ t->w = w;
+ t->h = h;
+ t->pixels = (u8*)t + sizeof(*t);
+ t->wrap = 0x11; // wrap u and v
+ return t;
+}
+
+void
+clearcanvas(Canvas *canvas)
+{
+ memset(canvas->fb, 0, canvas->w*canvas->h*4);
+ memset(canvas->zbuf, 0, canvas->w*canvas->h*4);
+}
+
+void
+writefb(Canvas *canvas, int x, int y, Color c)
+{
+ u8 *px = &canvas->fb[(y*canvas->w + x)*4];
+ u32 *z = &canvas->zbuf[y*canvas->w + x];
+
+ px[3] = c.r;
+ px[2] = c.g;
+ px[1] = c.b;
+ px[0] = c.a;
+}
+
+void
+putpixel(Canvas *canvas, Point3 p, Color c)
+{
+ // scissor test
+ if(p.x < srScissorX0 || p.x > srScissorX1 ||
+ p.y < srScissorY0 || p.y > srScissorY1)
+ return;
+
+ u8 *px = &canvas->fb[(p.y*canvas->w + p.x)*4];
+ u32 *z = &canvas->zbuf[p.y*canvas->w + p.x];
+
+ int fbwrite = 1;
+ int zbwrite = srWriteZ;
+
+ // alpha test
+ if(srAlphaTestEnable){
+ int fail;
+ switch(srAlphaTestFunction){
+ case ALPHATEST_NEVER:
+ fail = 1;
+ break;
+ case ALPHATEST_ALWAYS:
+ fail = 0;
+ break;
+ case ALPHATEST_LESS:
+ fail = c.a >= srAlphaTestReference;
+ break;
+ case ALPHATEST_LEQUAL:
+ fail = c.a > srAlphaTestReference;
+ break;
+ case ALPHATEST_EQUAL:
+ fail = c.a != srAlphaTestReference;
+ break;
+ case ALPHATEST_GEQUAL:
+ fail = c.a < srAlphaTestReference;
+ break;
+ case ALPHATEST_GREATER:
+ fail = c.a <= srAlphaTestReference;
+ break;
+ case ALPHATEST_NOTEQUAL:
+ fail = c.a == srAlphaTestReference;
+ break;
+ }
+ if(fail){
+ switch(srAlphaTestFail){
+ case ALPHAFAIL_KEEP:
+ return;
+ case ALPHAFAIL_FB_ONLY:
+ zbwrite = 0;
+ break;
+ case ALPHAFAIL_ZB_ONLY:
+ fbwrite = 0;
+ }
+ }
+ }
+
+ // ztest
+ if(srDepthTestEnable){
+ switch(srDepthTestFunction){
+ case DEPTHTEST_NEVER:
+ return;
+ case DEPTHTEST_ALWAYS:
+ break;
+ case DEPTHTEST_GEQUAL:
+ if((u32)p.z < *z)
+ return;
+ break;
+ case DEPTHTEST_GREATER:
+ if((u32)p.z <= *z)
+ return;
+ break;
+ }
+ }
+
+ Color d = { px[3], px[2], px[1], px[0] };
+
+ // blend
+ if(srAlphaBlendEnable){
+ int ar, ag, ab;
+ int br, bg, bb;
+ int dr, dg, db;
+ int ca;
+ switch(srAlphaBlendA){
+ case ALPHABLEND_SRC:
+ ar = c.r;
+ ag = c.g;
+ ab = c.b;
+ break;
+ case ALPHABLEND_DST:
+ ar = d.r;
+ ag = d.g;
+ ab = d.b;
+ break;
+ case ALPHABLEND_ZERO:
+ ar = 0;
+ ag = 0;
+ ab = 0;
+ break;
+ default: assert(0);
+ }
+ switch(srAlphaBlendB){
+ case ALPHABLEND_SRC:
+ br = c.r;
+ bg = c.g;
+ bb = c.b;
+ break;
+ case ALPHABLEND_DST:
+ br = d.r;
+ bg = d.g;
+ bb = d.b;
+ break;
+ case ALPHABLEND_ZERO:
+ br = 0;
+ bg = 0;
+ bb = 0;
+ break;
+ default: assert(0);
+ }
+ switch(srAlphaBlendC){
+ case ALPHABLEND_SRC:
+ ca = c.a;
+ break;
+ case ALPHABLEND_DST:
+ ca = d.a;
+ break;
+ case ALPHABLEND_FIX:
+ ca = srAlphaBlendFix;
+ break;
+ default: assert(0);
+ }
+ switch(srAlphaBlendD){
+ case ALPHABLEND_SRC:
+ dr = c.r;
+ dg = c.g;
+ db = c.b;
+ break;
+ case ALPHABLEND_DST:
+ dr = d.r;
+ dg = d.g;
+ db = d.b;
+ break;
+ case ALPHABLEND_ZERO:
+ dr = 0;
+ dg = 0;
+ db = 0;
+ break;
+ default: assert(0);
+ }
+
+ int r, g, b;
+ r = ((ar - br) * ca >> 7) + dr;
+ g = ((ag - bg) * ca >> 7) + dg;
+ b = ((ab - bb) * ca >> 7) + db;
+
+ c.r = clamp(r);
+ c.g = clamp(g);
+ c.b = clamp(b);
+ }
+
+ if(fbwrite)
+ writefb(canvas, p.x, p.y, c);
+ if(zbwrite)
+ *z = p.z;
+}
+
+Color
+sampletex_nearest(int u, int v)
+{
+ Texture *tex = srTexture;
+
+ const int usize = tex->w;
+ const int vsize = tex->h;
+
+ int iu = u >> 4;
+ int iv = v >> 4;
+
+ switch(srWrapU){
+ case WRAP_REPEAT:
+ iu %= usize;
+ break;
+ case WRAP_CLAMP:
+ if(iu < 0) iu = 0;
+ if(iu >= usize) iu = usize-1;
+ break;
+ case WRAP_BORDER:
+ if(iu < 0 || iu >= usize)
+ return srBorder;
+ }
+
+ switch(srWrapV){
+ case WRAP_REPEAT:
+ iv %= vsize;
+ break;
+ case WRAP_CLAMP:
+ if(iv < 0) iv = 0;
+ if(iv >= vsize) iv = vsize-1;
+ break;
+ case WRAP_BORDER:
+ if(iv < 0 || iv >= vsize)
+ return srBorder;
+ }
+
+ u8 *cp = &tex->pixels[(iv*tex->w + iu)*4];
+ Color c = { cp[0], cp[1], cp[2], cp[3] };
+ return c;
+}
+
+// t is texture, f is fragment
+Color
+texfunc(Color t, Color f)
+{
+ int r, g, b, a;
+ switch(srTexFunc){
+ case TFUNC_MODULATE:
+ r = t.r * f.r >> 7;
+ g = t.g * f.g >> 7;
+ b = t.b * f.b >> 7;
+ a = srTexUseAlpha ?
+ t.a * f.a >> 7 :
+ f.a;
+ break;
+ case TFUNC_DECAL:
+ r = t.r;
+ g = t.g;
+ b = t.b;
+ a = srTexUseAlpha ? t.a : f.a;
+ break;
+ case TFUNC_HIGHLIGHT:
+ r = (t.r * f.r >> 7) + f.a;
+ g = (t.g * f.g >> 7) + f.a;
+ b = (t.b * f.b >> 7) + f.a;
+ a = srTexUseAlpha ?
+ t.a + f.a :
+ f.a;
+ break;
+ case TFUNC_HIGHLIGHT2:
+ r = (t.r * f.r >> 7) + f.a;
+ g = (t.g * f.g >> 7) + f.a;
+ b = (t.b * f.b >> 7) + f.a;
+ a = srTexUseAlpha ? t.a : f.a;
+ break;
+ }
+ Color v;
+ v.r = clamp(r);
+ v.g = clamp(g);
+ v.b = clamp(b);
+ v.a = clamp(a);
+ return v;
+}
+
+Point3 mkpnt(int x, int y, int z) { Point3 p = { x, y, z}; return p; }
+
+void
+drawRect(Canvas *canvas, Point3 p1, Point3 p2, Color c)
+{
+ int x, y;
+ for(y = p1.y; y <= p2.y; y++)
+ for(x = p1.x; x <= p2.x; x++)
+ putpixel(canvas, mkpnt(x, y, 0), c);
+}
+
+void
+drawLine(Canvas *canvas, Point3 p1, Point3 p2, Color c)
+{
+ int dx, dy;
+ int incx, incy;
+ int e;
+ int x, y;
+
+ dx = abs(p2.x-p1.x);
+ incx = p2.x > p1.x ? 1 : -1;
+ dy = abs(p2.y-p1.y);
+ incy = p2.y > p1.y ? 1 : -1;
+ e = 0;
+ if(dx == 0){
+ for(y = p1.y; y != p2.y; y += incy)
+ putpixel(canvas, mkpnt(p1.x, y, 0), c);
+ }else if(dx > dy){
+ y = p1.y;
+ for(x = p1.x; x != p2.x; x += incx){
+ putpixel(canvas, mkpnt(x, y, 0), c);
+ e += dy;
+ if(2*e >= dx){
+ e -= dx;
+ y += incy;
+ }
+ }
+ }else{
+ x = p1.x;
+ for(y = p1.y; y != p2.y; y += incy){
+ putpixel(canvas, mkpnt(x, y, 0), c);
+ e += dx;
+ if(2*e >= dy){
+ e -= dy;
+ x += incx;
+ }
+ }
+ }
+}
+
+/*
+ attibutes we want to interpolate:
+ R G B A
+ U V / S T Q
+ X Y Z F
+*/
+
+struct TriAttribs
+{
+ i64 z;
+ i32 r, g, b, a;
+ i32 f;
+ float s, t;
+ float q;
+};
+
+static void
+add1(struct TriAttribs *a, struct TriAttribs *b)
+{
+ a->z += b->z;
+ a->r += b->r;
+ a->g += b->g;
+ a->b += b->b;
+ a->a += b->a;
+ a->f += b->f;
+ a->s += b->s;
+ a->t += b->t;
+ a->q += b->q;
+}
+
+static void
+sub1(struct TriAttribs *a, struct TriAttribs *b)
+{
+ a->z -= b->z;
+ a->r -= b->r;
+ a->g -= b->g;
+ a->b -= b->b;
+ a->a -= b->a;
+ a->f -= b->f;
+ a->s -= b->s;
+ a->t -= b->t;
+ a->q -= b->q;
+}
+
+static void
+guard(struct TriAttribs *a)
+{
+ if(a->z < 0) a->z = 0;
+ else if(a->z > 0x3FFFFFFFC000LL) a->z = 0x3FFFFFFFC000LL;
+ if(a->r < 0) a->r = 0;
+ else if(a->r > 0xFF000) a->r = 0xFF000;
+ if(a->g < 0) a->g = 0;
+ else if(a->g > 0xFF000) a->g = 0xFF000;
+ if(a->b < 0) a->b = 0;
+ else if(a->b > 0xFF000) a->b = 0xFF000;
+ if(a->a < 0) a->a = 0;
+ else if(a->a > 0xFF000) a->a = 0xFF000;
+ if(a->f < 0) a->f = 0;
+ else if(a->f > 0xFF000) a->f = 0xFF000;
+}
+
+struct RasTri
+{
+ int x, y;
+ int ymid, yend;
+ int right;
+ int e[2], dx[3], dy[3];
+ struct TriAttribs gx, gy, v, s;
+};
+
+static int
+triangleSetup(struct RasTri *tri, Vertex v1, Vertex v2, Vertex v3)
+{
+ int dx1, dx2, dx3;
+ int dy1, dy2, dy3;
+
+ dy1 = v3.y - v1.y; // long edge
+ if(dy1 == 0) return 1;
+ dx1 = v3.x - v1.x;
+ dx2 = v2.x - v1.x; // first small edge
+ dy2 = v2.y - v1.y;
+ dx3 = v3.x - v2.x; // second small edge
+ dy3 = v3.y - v2.y;
+
+ // this is twice the triangle area
+ const int area = dx2*dy1 - dx1*dy2;
+ if(area == 0) return 1;
+ // figure out if 0 or 1 is the right edge
+ tri->right = area < 0;
+
+ /* The gradients are to step whole pixels,
+ * so they are pre-multiplied by 16. */
+
+ float denom = 16.0f/area;
+ // gradients x
+#define GX(p) ((v2.p - v1.p)*dy1 - (v3.p - v1.p)*dy2)
+ tri->gx.z = GX(z)*denom * 16384;
+ tri->gx.r = GX(r)*denom * 4096;
+ tri->gx.g = GX(g)*denom * 4096;
+ tri->gx.b = GX(b)*denom * 4096;
+ tri->gx.a = GX(a)*denom * 4096;
+ tri->gx.f = GX(f)*denom * 4096;
+ tri->gx.s = GX(s)*denom;
+ tri->gx.t = GX(t)*denom;
+ tri->gx.q = GX(q)*denom;
+
+ // gradients y
+ denom = -denom;
+#define GY(p) ((v2.p - v1.p)*dx1 - (v3.p - v1.p)*dx2)
+ tri->gy.z = GY(z)*denom * 16384;
+ tri->gy.r = GY(r)*denom * 4096;
+ tri->gy.g = GY(g)*denom * 4096;
+ tri->gy.b = GY(b)*denom * 4096;
+ tri->gy.a = GY(a)*denom * 4096;
+ tri->gy.f = GY(f)*denom * 4096;
+ tri->gy.s = GY(s)*denom;
+ tri->gy.t = GY(t)*denom;
+ tri->gy.q = GY(q)*denom;
+
+ tri->ymid = CEIL(v2.y);
+ tri->yend = CEIL(v3.y);
+
+ tri->y = CEIL(v1.y);
+ tri->x = CEIL(v1.x);
+
+ tri->dy[0] = dy2<<4; // upper edge
+ tri->dy[1] = dy1<<4; // lower edge
+ tri->dy[2] = dy3<<4; // long edge
+ tri->dx[0] = dx2<<4;
+ tri->dx[1] = dx1<<4;
+ tri->dx[2] = dx3<<4;
+
+ // prestep to land on pixel center
+
+ int stepx = v1.x - (tri->x<<4);
+ int stepy = v1.y - (tri->y<<4);
+ tri->e[0] = (-stepy*tri->dx[0] + stepx*tri->dy[0]) >> 4;
+ tri->e[1] = (-stepy*tri->dx[1] + stepx*tri->dy[1]) >> 4;
+
+ // attributes along interpolated edge
+ // why is this cast needed? (mingw)
+ tri->v.z = (i64)v1.z*16384 - (stepy*tri->gy.z + stepx*tri->gx.z)/16;
+ tri->v.r = v1.r*4096 - (stepy*tri->gy.r + stepx*tri->gx.r)/16;
+ tri->v.g = v1.g*4096 - (stepy*tri->gy.g + stepx*tri->gx.g)/16;
+ tri->v.b = v1.b*4096 - (stepy*tri->gy.b + stepx*tri->gx.b)/16;
+ tri->v.a = v1.a*4096 - (stepy*tri->gy.a + stepx*tri->gx.a)/16;
+ tri->v.f = v1.f*4096 - (stepy*tri->gy.f + stepx*tri->gx.f)/16;
+ tri->v.s = v1.s - (stepy*tri->gy.s + stepx*tri->gx.s)/16.0f;
+ tri->v.t = v1.t - (stepy*tri->gy.t + stepx*tri->gx.t)/16.0f;
+ tri->v.q = v1.q - (stepy*tri->gy.q + stepx*tri->gx.q)/16.0f;
+
+ return 0;
+}
+
+void
+drawTriangle(Canvas *canvas, Vertex v1, Vertex v2, Vertex v3)
+{
+ Color c;
+ struct RasTri tri;
+ int stepx, stepy;
+
+ // Sort such that we have from top to bottom v1,v2,v3
+ if(v2.y < v1.y){ Vertex tmp = v1; v1 = v2; v2 = tmp; }
+ if(v3.y < v1.y){ Vertex tmp = v1; v1 = v3; v3 = tmp; }
+ if(v3.y < v2.y){ Vertex tmp = v2; v2 = v3; v3 = tmp; }
+
+ if(triangleSetup(&tri, v1, v2, v3))
+ return;
+
+ // Current scanline start and end
+ int xn[2] = { tri.x, tri.x };
+ int a = !tri.right; // left edge
+ int b = tri.right; // right edge
+
+ // If upper triangle has no height, only do the lower part
+ if(tri.dy[0] == 0)
+ goto secondtri;
+ while(tri.y < tri.yend){
+ /* TODO: is this the righ way to step the edges? */
+
+ /* Step x and interpolated value down left edge */
+ while(tri.e[a] <= -tri.dy[a]){
+ xn[a]--;
+ tri.e[a] += tri.dy[a];
+ sub1(&tri.v, &tri.gx);
+ }
+ while(tri.e[a] > 0){
+ xn[a]++;
+ tri.e[a] -= tri.dy[a];
+ add1(&tri.v, &tri.gx);
+ }
+
+ /* Step x down right edge */
+ while(tri.e[b] <= -tri.dy[b]){
+ xn[b]--;
+ tri.e[b] += tri.dy[b];
+ }
+ while(tri.e[b] > 0){
+ xn[b]++;
+ tri.e[b] -= tri.dy[b];
+ }
+
+ // When we reach the mid vertex, change state and jump to start of loop again
+ // TODO: this is a bit ugly in here...can we fix it?
+ if(tri.y == tri.ymid){
+ secondtri:
+ tri.dx[0] = tri.dx[2];
+ tri.dy[0] = tri.dy[2];
+ // Either the while prevents this or we returned early because dy1 == 0
+ assert(tri.dy[0] != 0);
+ stepx = v2.x - (xn[0]<<4);
+ stepy = v2.y - (tri.y<<4);
+ tri.e[0] = (-stepy*tri.dx[0] + stepx*tri.dy[0]) >> 4;
+
+ tri.ymid = -1; // so we don't do this again
+ continue;
+ }
+
+ /* Rasterize one line */
+ tri.s = tri.v;
+ for(tri.x = xn[a]; tri.x < xn[b]; tri.x++){
+ guard(&tri.s);
+ c.r = tri.s.r >> 12;
+ c.g = tri.s.g >> 12;
+ c.b = tri.s.b >> 12;
+ c.a = tri.s.a >> 12;
+ if(srTexEnable && srTexture){
+ float w = 1.0f/tri.s.q;
+ float s = tri.s.s * w;
+ float t = tri.s.t * w;
+ int u = s * srTexture->w * 16;
+ int v = t * srTexture->h * 16;
+ Color texc = sampletex_nearest(u, v);
+ c = texfunc(texc, c);
+ }
+ if(srFogEnable){
+ const int f = tri.s.f >> 12;
+ c.r = (f*c.r >> 8) + ((255 - f)*srFogCol.r >> 8);
+ c.g = (f*c.g >> 8) + ((255 - f)*srFogCol.g >> 8);
+ c.b = (f*c.b >> 8) + ((255 - f)*srFogCol.b >> 8);
+ }
+ putpixel(canvas, mkpnt(tri.x, tri.y, tri.s.z>>14), c);
+ add1(&tri.s, &tri.gx);
+ }
+
+ /* Step in y */
+ tri.y++;
+ tri.e[a] += tri.dx[a];
+ tri.e[b] += tri.dx[b];
+ add1(&tri.v, &tri.gy);
+ }
+}
+
+Canvas *canvas;
+
+}
+
+using namespace rw;
+
+void
+rastest_renderTriangles(RWDEVICE::Im2DVertex *scrverts, int32 numVerts, uint16 *indices, int32 numTris)
+{
+ int i;
+ RGBA col;
+ rs::Vertex v[3];
+ RWDEVICE::Im2DVertex *iv;
+
+ rs::srDepthTestEnable = 1;
+ rs::srAlphaTestEnable = 0;
+ rs::srTexEnable = 0;
+ rs::srAlphaBlendEnable = 0;
+
+ while(numTris--){
+ for(i = 0; i < 3; i++){
+ iv = &scrverts[indices[i]];
+ v[i].x = iv->getScreenX() * 16.0f;
+ v[i].y = iv->getScreenY() * 16.0f;
+ v[i].z = 16777216*(1.0f-iv->getScreenZ());
+ v[i].q = iv->getRecipCameraZ();
+ col = iv->getColor();
+ v[i].r = col.red;
+ v[i].g = col.green;
+ v[i].b = col.blue;
+ v[i].a = col.alpha;
+ v[i].f = 0;
+ v[i].s = iv->u*iv->getRecipCameraZ();
+ v[i].t = iv->v*iv->getRecipCameraZ();
+ }
+ drawTriangle(rs::canvas, v[0], v[1], v[2]);
+
+ indices += 3;
+ }
+}
+
+extern rw::Raster *testras;
+
+void
+beginSoftras(void)
+{
+ Camera *cam = (Camera*)engine->currentCamera;
+
+ if(rs::canvas == nil ||
+ cam->frameBuffer->width != rs::canvas->w ||
+ cam->frameBuffer->height != rs::canvas->h){
+ rs::canvas = rs::makecanvas(cam->frameBuffer->width, cam->frameBuffer->height);
+ testras = rw::Raster::create(rs::canvas->w, rs::canvas->h, 32, rw::Raster::C8888);
+ }
+
+ clearcanvas(rs::canvas);
+ rs::srScissorX0 = 0;
+ rs::srScissorX1 = rs::canvas->w-1;
+ rs::srScissorY0 = 0;
+ rs::srScissorY1 = rs::canvas->h-1;
+}
+
+void
+endSoftras(void)
+{
+ int i;
+ uint8 *dst = testras->lock(0, Raster::LOCKWRITE|Raster::LOCKNOFETCH);
+ if(dst == nil)
+ return;
+ uint8 *src = rs::canvas->fb;
+ for(i = 0; i < rs::canvas->w*rs::canvas->h; i++){
+ dst[0] = src[1];
+ dst[1] = src[2];
+ dst[2] = src[3];
+ dst[3] = src[0];
+ dst += 4;
+ src += 4;
+ }
+ // abgr in canvas
+ // bgra in raster
+ testras->unlock(0);
+}
+
+
+/*
+typedef struct PixVert PixVert;
+struct PixVert
+{
+ float x, y, z, q;
+ int r, g, b, a;
+ float u, v;
+};
+#include "test.inc"
+
+void
+drawtest(void)
+{
+ int i, j;
+ rs::Vertex v[3];
+
+ rs::srDepthTestEnable = 1;
+ rs::srAlphaTestEnable = 0;
+ rs::srTexEnable = 0;
+ rs::srAlphaBlendEnable = 0;
+
+ for(i = 0; i < nelem(verts); i += 3){
+ for(j = 0; j < 3; j++){
+ v[j].x = verts[i+j].x * 16.0f;
+ v[j].y = verts[i+j].y * 16.0f;
+ v[j].z = 16777216*(1.0f - verts[i+j].z);
+ v[j].q = verts[i+j].q;
+ v[j].r = verts[i+j].r;
+ v[j].g = verts[i+j].g;
+ v[j].b = verts[i+j].b;
+ v[j].a = verts[i+j].a;
+ v[j].f = 0;
+ v[j].s = verts[i+j].u*v[j].q;
+ v[j].t = verts[i+j].v*v[j].q;
+ }
+ drawTriangle(rs::canvas, v[0], v[1], v[2]);
+ }
+//exit(0);
+}
+*/
diff --git a/tools/playground/splines.cpp b/tools/playground/splines.cpp
new file mode 100644
index 0000000..00a3f79
--- /dev/null
+++ b/tools/playground/splines.cpp
@@ -0,0 +1,750 @@
+#include <rw.h>
+#include <skeleton.h>
+
+#include <vector>
+
+using namespace rw;
+using namespace RWDEVICE;
+
+static Im3DVertex im3dVerts[1024];
+static int numImVerts;
+static rw::PrimitiveType imPrim;
+static Im3DVertex imVert;
+
+void
+BeginIm3D(rw::PrimitiveType prim)
+{
+ numImVerts = 0;
+ imPrim = prim;
+}
+
+void
+EndIm3D(void)
+{
+ rw::im3d::Transform(im3dVerts, numImVerts, nil, rw::im3d::EVERYTHING);
+ rw::im3d::RenderPrimitive(imPrim);
+ rw::im3d::End();
+}
+
+void
+AddVertex(const rw::V3d &vert)
+{
+ if(numImVerts >= 1024){
+ EndIm3D();
+ switch(imPrim){
+ case PRIMTYPEPOLYLINE:
+ im3dVerts[0] = im3dVerts[numImVerts-1];
+ numImVerts = 1;
+ break;
+ case PRIMTYPETRISTRIP:
+ // TODO: winding?
+ im3dVerts[0] = im3dVerts[numImVerts-2];
+ im3dVerts[1] = im3dVerts[numImVerts-1];
+ numImVerts = 2;
+ break;
+ case PRIMTYPETRIFAN:
+ im3dVerts[1] = im3dVerts[numImVerts-1];
+ numImVerts = 2;
+ break;
+ default:
+ numImVerts = 0;
+ }
+ }
+
+ imVert.setX(vert.x);
+ imVert.setY(vert.y);
+ imVert.setZ(vert.z);
+ im3dVerts[numImVerts++] = imVert;
+}
+
+float epsilon = 0.000001;
+
+class BezierSurf
+{
+public:
+ // hardcoded 4x4 for now
+ V3d verts[16];
+
+ void mirrorX(void);
+ void mirrorY(void);
+
+ V3d eval(float u, float v);
+ void drawHull(void);
+ void drawShaded(void);
+};
+
+class RBCurve
+{
+public:
+ int degree;
+ std::vector<V3d> verts;
+ std::vector<float> weights; // for rational
+ std::vector<float> knots;
+
+ V3d eval(float u);
+ void drawHull(void);
+ void drawSpline(void);
+};
+
+class RBSurf
+{
+public:
+ int degreeU, degreeV;
+ int numU, numV;
+ std::vector<V3d> verts;
+ std::vector<float> weights;
+ std::vector<float> knotsU, knotsV;
+
+ void update(void);
+ V3d eval(float u, float v);
+ void drawHull(void);
+ void drawIsoparms(void);
+ void drawShaded(void);
+};
+
+float div0(float a, float b) { return b == 0.0f ? a : a/b; }
+
+// naive algorithm
+float
+evalBasis(int i, int d, float u, float knots[])
+{
+ if(d == 0){
+ if(knots[i] <= u && u < knots[i+1])
+ return 1.0f;
+ return 0.0f;
+ }
+
+ float b0 = evalBasis(i, d-1, u, knots);
+ float b1 = evalBasis(i+1, d-1, u, knots);
+ return b0*div0(u-knots[i], knots[i+d] - knots[i]) + b1*div0(knots[i+d+1]-u, knots[i+d+1] - knots[i+1]);
+}
+
+float
+evalBasisFast(int i, int d, float u, float knots[])
+{
+ int r, j;
+ float tmp[10];
+
+ // degree 0 values
+ for(j = 0; j < d+1; j++)
+ tmp[j] = knots[i+j] <= u && u < knots[i+j+1] ? 1.0f : 0.0f;
+
+ // build up from degree zero
+ for(r = d, d = 1; r > 0; r--, d++){
+ for(j = 0; j < r; j++){
+ float t1 = div0(u-knots[i+j], knots[i+j + d] - knots[i+j]);
+ float t2 = div0(knots[i+j + d+1]-u, knots[i+j + d+1] - knots[i+j + 1]);
+ tmp[j] = tmp[j]*t1 + tmp[j+1]*t2;
+ }
+ }
+ return tmp[0];
+}
+
+void
+BezierSurf::mirrorX(void)
+{
+ int i;
+ for(i = 0; i < 16; i++)
+ verts[i].x = -verts[i].x;
+}
+
+void
+BezierSurf::mirrorY(void)
+{
+ int i;
+ for(i = 0; i < 16; i++)
+ verts[i].y = -verts[i].y;
+}
+
+V3d
+BezierSurf::eval(float u, float v)
+{
+ int i, j;
+ V3d out = { 0.0f, 0.0f, 0.0f };
+ float us[4], vs[4];
+ float iu = 1.0f-u;
+ float iv = 1.0f-v;
+ us[0] = iu*iu*iu;
+ us[1] = 3.0f*u*iu*iu;
+ us[2] = 3.0f*u*u*iu;
+ us[3] = u*u*u;
+ vs[0] = iv*iv*iv;
+ vs[1] = 3.0f*v*iv*iv;
+ vs[2] = 3.0f*v*v*iv;
+ vs[3] = v*v*v;
+ for(i = 0; i < 4; i++)
+ for(j = 0; j < 4; j++)
+ out = add(out, scale(verts[j+i*4],us[j]*vs[i]));
+ return out;
+}
+
+void
+BezierSurf::drawHull(void)
+{
+ rw::SetRenderStatePtr(rw::TEXTURERASTER, nil);
+ rw::SetRenderState(rw::FOGENABLE, 0);
+
+ imVert.setU(0.0f);
+ imVert.setV(0.0f);
+ imVert.setColor(138, 72, 51, 255);
+// imVert.setColor(228, 172, 121, 255);
+
+ int iu, iv;
+ for(iv = 0; iv < 4; iv++){
+ BeginIm3D(rw::PRIMTYPEPOLYLINE);
+ for(iu = 0; iu < 4; iu++)
+ AddVertex(verts[iu + iv*4]);
+ EndIm3D();
+ }
+
+ for(iu = 0; iu < 4; iu++){
+ BeginIm3D(rw::PRIMTYPEPOLYLINE);
+ for(iv = 0; iv < 4; iv++)
+ AddVertex(verts[iu + iv*4]);
+ EndIm3D();
+ }
+}
+
+void
+BezierSurf::drawShaded(void)
+{
+ V3d vert;
+ int iu, iv;
+ float u, v;
+
+ rw::SetRenderStatePtr(rw::TEXTURERASTER, nil);
+ rw::SetRenderState(rw::FOGENABLE, 0);
+
+ imVert.setU(0.0f);
+ imVert.setV(0.0f);
+ imVert.setColor(0, 128, 240, 255);
+
+ float endu = 1.0f - epsilon;
+ float endv = 1.0f - epsilon;
+ float uinc = (endu-0.0f)/40.0f;
+ float vinc = (endv-0.0f)/40.0f;
+
+ float vnext;
+ for(v = 0.0f;; v = vnext){
+ if(v > endv)
+ v = endv;
+ vnext = v + vinc;
+
+ BeginIm3D(rw::PRIMTYPETRISTRIP);
+ for(u = 0.0f;; u += uinc){
+ if(u > endu)
+ u = endu;
+
+ AddVertex(eval(u, v));
+ AddVertex(eval(u, vnext));
+
+ if(u >= endu)
+ break;
+ }
+ EndIm3D();
+ if(vnext >= endv)
+ break;
+ }
+}
+
+V3d
+RBCurve::eval(float u)
+{
+ int i;
+ V3d vert = { 0.0f, 0.0f, 0.0f };
+ float w = 0.0f;
+
+ // Find knots we're interested in
+ for(i = 0; i < knots.size(); i++)
+ if(knots[i] <= u && u < knots[i+1])
+ break;
+ int startI = i-degree;
+ int endI = i+1;
+
+ for(i = startI; i < endI; i++){
+ float r = evalBasisFast(i, degree, u, &knots[0]);
+ w += weights[i]*r;
+ vert = add(vert, scale(verts[i], weights[i]*r));
+ }
+ return scale(vert, div0(1.0f,w));
+}
+
+void
+RBCurve::drawHull(void)
+{
+ int i;
+
+ rw::SetRenderStatePtr(rw::TEXTURERASTER, nil);
+ rw::SetRenderState(rw::FOGENABLE, 0);
+
+ BeginIm3D(rw::PRIMTYPEPOLYLINE);
+ imVert.setU(0.0f);
+ imVert.setV(0.0f);
+ imVert.setColor(138, 72, 51, 255);
+// imVert.setColor(228, 172, 121, 255);
+ for(i = 0; i < verts.size(); i++)
+ AddVertex(verts[i]);
+ EndIm3D();
+}
+
+void
+RBCurve::drawSpline(void)
+{
+ int i;
+ float u, endu;
+ V3d vert;
+
+ rw::SetRenderStatePtr(rw::TEXTURERASTER, nil);
+ rw::SetRenderState(rw::FOGENABLE, 0);
+ BeginIm3D(rw::PRIMTYPEPOLYLINE);
+ imVert.setU(0.0f);
+ imVert.setV(0.0f);
+ imVert.setColor(0, 4, 96, 255);
+
+ u = knots[0];
+ endu = knots[knots.size()-1] - epsilon;
+ float uinc = (endu-knots[0])/40.0f;
+ for(;; u += uinc){
+ if(u > endu)
+ u = endu;
+ AddVertex(eval(u));
+ if(u >= endu)
+ break;
+ }
+
+ EndIm3D();
+}
+
+void
+RBSurf::update(void)
+{
+ numU = knotsU.size() - degreeU - 1;
+ numV = knotsV.size() - degreeV - 1;
+}
+
+V3d
+RBSurf::eval(float u, float v)
+{
+ int i, j;
+ V3d vert = { 0.0f, 0.0f, 0.0f };
+ float w = 0.0f;
+
+ float basisU[10], basisV[10];
+
+ // Find knots we're interested in
+ int k;
+ for(k = 0; k < knotsU.size(); k++)
+ if(knotsU[k] <= u && u < knotsU[k+1])
+ break;
+ int startI = k-degreeU;
+ int endI = k+1;
+ for(k = 0; k < knotsV.size(); k++)
+ if(knotsV[k] <= v && v < knotsV[k+1])
+ break;
+ int startJ = k-degreeV;
+ int endJ = k+1;
+
+ for(i = startI; i < endI; i++) basisU[i-startI] = evalBasisFast(i, degreeU, u, &knotsU[0]);
+ for(j = startJ; j < endJ; j++) basisV[j-startJ] = evalBasisFast(j, degreeV, v, &knotsV[0]);
+
+ for(j = startJ; j < endJ; j++)
+ for(i = startI; i < endI; i++){
+ float r = basisV[j-startJ]*basisU[i-startI];
+ w += weights[j*numU + i]*r;
+ vert = add(vert, scale(verts[j*numU + i], weights[j*numU + i]*r));
+ }
+ return scale(vert, div0(1.0f,w));
+}
+
+void
+RBSurf::drawHull(void)
+{
+ rw::SetRenderStatePtr(rw::TEXTURERASTER, nil);
+ rw::SetRenderState(rw::FOGENABLE, 0);
+
+ imVert.setU(0.0f);
+ imVert.setV(0.0f);
+ imVert.setColor(138, 72, 51, 255);
+// imVert.setColor(228, 172, 121, 255);
+
+ int iu, iv;
+ for(iv = 0; iv < numV; iv++){
+ BeginIm3D(rw::PRIMTYPEPOLYLINE);
+ for(iu = 0; iu < numU; iu++)
+ AddVertex(verts[iu + iv*numU]);
+ EndIm3D();
+ }
+
+ for(iu = 0; iu < numU; iu++){
+ BeginIm3D(rw::PRIMTYPEPOLYLINE);
+ for(iv = 0; iv < numV; iv++)
+ AddVertex(verts[iu + iv*numU]);
+ EndIm3D();
+ }
+}
+
+void
+RBSurf::drawIsoparms(void)
+{
+ V3d vert;
+ int iu, iv;
+ float u, v;
+
+ rw::SetRenderStatePtr(rw::TEXTURERASTER, nil);
+ rw::SetRenderState(rw::FOGENABLE, 0);
+
+ imVert.setU(0.0f);
+ imVert.setV(0.0f);
+ imVert.setColor(0, 4, 96, 255);
+
+ float endu = knotsU[knotsU.size()-1] - epsilon;
+ float endv = knotsU[knotsV.size()-1] - epsilon;
+ float uinc = (endu-knotsU[0])/40.0f;
+ float vinc = (endv-knotsV[0])/40.0f;
+
+ v = -100000.0f;
+ for(iv = 0; iv < knotsV.size(); iv++){
+ if(knotsV[iv] <= v) continue;
+ v = knotsV[iv];
+ if(v > endv) v = endv;
+
+ BeginIm3D(rw::PRIMTYPEPOLYLINE);
+ for(u = knotsU[0];; u += uinc){
+ if(u > endu)
+ u = endu;
+ AddVertex(eval(u, v));
+ if(u >= endu)
+ break;
+ }
+ EndIm3D();
+ }
+
+ u = -100000.0f;
+ for(iu = 0; iu < knotsU.size(); iu++){
+ if(knotsU[iu] <= u) continue;
+ u = knotsU[iu];
+ if(u > endu) u = endu;
+
+ BeginIm3D(rw::PRIMTYPEPOLYLINE);
+ for(v = knotsV[0];; v += vinc){
+ if(v > endv)
+ v = endv;
+ AddVertex(eval(u, v));
+ if(v >= endv)
+ break;
+ }
+ EndIm3D();
+ }
+}
+
+void
+RBSurf::drawShaded(void)
+{
+ V3d vert;
+ int iu, iv;
+ float u, v;
+
+ rw::SetRenderStatePtr(rw::TEXTURERASTER, nil);
+ rw::SetRenderState(rw::FOGENABLE, 0);
+
+ imVert.setU(0.0f);
+ imVert.setV(0.0f);
+ imVert.setColor(0, 128, 240, 255);
+
+ float endu = knotsU[knotsU.size()-1] - epsilon;
+ float endv = knotsU[knotsV.size()-1] - epsilon;
+ float uinc = (endu-knotsU[0])/40.0f;
+ float vinc = (endv-knotsV[0])/40.0f;
+
+ float vnext;
+ for(v = knotsV[0];; v = vnext){
+ if(v > endv)
+ v = endv;
+ vnext = v + vinc;
+
+ BeginIm3D(rw::PRIMTYPETRISTRIP);
+ for(u = knotsU[0];; u += uinc){
+ if(u > endu)
+ u = endu;
+
+ AddVertex(eval(u, v));
+ AddVertex(eval(u, vnext));
+
+ if(u >= endu)
+ break;
+ }
+ EndIm3D();
+ if(vnext >= endv)
+ break;
+ }
+}
+
+
+RBCurve testspline1, testspline2;
+RBSurf testsurf;
+
+V3d teapotVerts[] = {
+ { 0.2000, 0.0000, 2.70000 }, { 0.2000, -0.1120, 2.70000 },
+ { 0.1120, -0.2000, 2.70000 }, { 0.0000, -0.2000, 2.70000 },
+ { 1.3375, 0.0000, 2.53125 }, { 1.3375, -0.7490, 2.53125 },
+ { 0.7490, -1.3375, 2.53125 }, { 0.0000, -1.3375, 2.53125 },
+ { 1.4375, 0.0000, 2.53125 }, { 1.4375, -0.8050, 2.53125 },
+ { 0.8050, -1.4375, 2.53125 }, { 0.0000, -1.4375, 2.53125 },
+ { 1.5000, 0.0000, 2.40000 }, { 1.5000, -0.8400, 2.40000 },
+ { 0.8400, -1.5000, 2.40000 }, { 0.0000, -1.5000, 2.40000 },
+ { 1.7500, 0.0000, 1.87500 }, { 1.7500, -0.9800, 1.87500 },
+ { 0.9800, -1.7500, 1.87500 }, { 0.0000, -1.7500, 1.87500 },
+ { 2.0000, 0.0000, 1.35000 }, { 2.0000, -1.1200, 1.35000 },
+ { 1.1200, -2.0000, 1.35000 }, { 0.0000, -2.0000, 1.35000 },
+ { 2.0000, 0.0000, 0.90000 }, { 2.0000, -1.1200, 0.90000 },
+ { 1.1200, -2.0000, 0.90000 }, { 0.0000, -2.0000, 0.90000 },
+ { -2.0000, 0.0000, 0.90000 }, { 2.0000, 0.0000, 0.45000 },
+ { 2.0000, -1.1200, 0.45000 }, { 1.1200, -2.0000, 0.45000 },
+ { 0.0000, -2.0000, 0.45000 }, { 1.5000, 0.0000, 0.22500 },
+ { 1.5000, -0.8400, 0.22500 }, { 0.8400, -1.5000, 0.22500 },
+ { 0.0000, -1.5000, 0.22500 }, { 1.5000, 0.0000, 0.15000 },
+ { 1.5000, -0.8400, 0.15000 }, { 0.8400, -1.5000, 0.15000 },
+ { 0.0000, -1.5000, 0.15000 }, { -1.6000, 0.0000, 2.02500 },
+ { -1.6000, -0.3000, 2.02500 }, { -1.5000, -0.3000, 2.25000 },
+ { -1.5000, 0.0000, 2.25000 }, { -2.3000, 0.0000, 2.02500 },
+ { -2.3000, -0.3000, 2.02500 }, { -2.5000, -0.3000, 2.25000 },
+ { -2.5000, 0.0000, 2.25000 }, { -2.7000, 0.0000, 2.02500 },
+ { -2.7000, -0.3000, 2.02500 }, { -3.0000, -0.3000, 2.25000 },
+ { -3.0000, 0.0000, 2.25000 }, { -2.7000, 0.0000, 1.80000 },
+ { -2.7000, -0.3000, 1.80000 }, { -3.0000, -0.3000, 1.80000 },
+ { -3.0000, 0.0000, 1.80000 }, { -2.7000, 0.0000, 1.57500 },
+ { -2.7000, -0.3000, 1.57500 }, { -3.0000, -0.3000, 1.35000 },
+ { -3.0000, 0.0000, 1.35000 }, { -2.5000, 0.0000, 1.12500 },
+ { -2.5000, -0.3000, 1.12500 }, { -2.6500, -0.3000, 0.93750 },
+ { -2.6500, 0.0000, 0.93750 }, { -2.0000, -0.3000, 0.90000 },
+ { -1.9000, -0.3000, 0.60000 }, { -1.9000, 0.0000, 0.60000 },
+ { 1.7000, 0.0000, 1.42500 }, { 1.7000, -0.6600, 1.42500 },
+ { 1.7000, -0.6600, 0.60000 }, { 1.7000, 0.0000, 0.60000 },
+ { 2.6000, 0.0000, 1.42500 }, { 2.6000, -0.6600, 1.42500 },
+ { 3.1000, -0.6600, 0.82500 }, { 3.1000, 0.0000, 0.82500 },
+ { 2.3000, 0.0000, 2.10000 }, { 2.3000, -0.2500, 2.10000 },
+ { 2.4000, -0.2500, 2.02500 }, { 2.4000, 0.0000, 2.02500 },
+ { 2.7000, 0.0000, 2.40000 }, { 2.7000, -0.2500, 2.40000 },
+ { 3.3000, -0.2500, 2.40000 }, { 3.3000, 0.0000, 2.40000 },
+ { 2.8000, 0.0000, 2.47500 }, { 2.8000, -0.2500, 2.47500 },
+ { 3.5250, -0.2500, 2.49375 }, { 3.5250, 0.0000, 2.49375 },
+ { 2.9000, 0.0000, 2.47500 }, { 2.9000, -0.1500, 2.47500 },
+ { 3.4500, -0.1500, 2.51250 }, { 3.4500, 0.0000, 2.51250 },
+ { 2.8000, 0.0000, 2.40000 }, { 2.8000, -0.1500, 2.40000 },
+ { 3.2000, -0.1500, 2.40000 }, { 3.2000, 0.0000, 2.40000 },
+ { 0.0000, 0.0000, 3.15000 }, { 0.8000, 0.0000, 3.15000 },
+ { 0.8000, -0.4500, 3.15000 }, { 0.4500, -0.8000, 3.15000 },
+ { 0.0000, -0.8000, 3.15000 }, { 0.0000, 0.0000, 2.85000 },
+ { 1.4000, 0.0000, 2.40000 }, { 1.4000, -0.7840, 2.40000 },
+ { 0.7840, -1.4000, 2.40000 }, { 0.0000, -1.4000, 2.40000 },
+ { 0.4000, 0.0000, 2.55000 }, { 0.4000, -0.2240, 2.55000 },
+ { 0.2240, -0.4000, 2.55000 }, { 0.0000, -0.4000, 2.55000 },
+ { 1.3000, 0.0000, 2.55000 }, { 1.3000, -0.7280, 2.55000 },
+ { 0.7280, -1.3000, 2.55000 }, { 0.0000, -1.3000, 2.55000 },
+ { 1.3000, 0.0000, 2.40000 }, { 1.3000, -0.7280, 2.40000 },
+ { 0.7280, -1.3000, 2.40000 }, { 0.0000, -1.3000, 2.40000 }
+};
+int teapotPatches[10][16] = {
+ { 118, 118, 118, 118, 124, 122, 119, 121,
+ 123, 126, 125, 120, 40, 39, 38, 37 },
+ { 102, 103, 104, 105, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15 },
+ { 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27 },
+ { 24, 25, 26, 27, 29, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, 40 },
+ { 96, 96, 96, 96, 97, 98, 99, 100,
+ 101, 101, 101, 101, 0, 1, 2, 3 },
+ { 0, 1, 2, 3, 106, 107, 108, 109,
+ 110, 111, 112, 113, 114, 115, 116, 117 },
+ { 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56 },
+ { 53, 54, 55, 56, 57, 58, 59, 60,
+ 61, 62, 63, 64, 28, 65, 66, 67 },
+ { 68, 69, 70, 71, 72, 73, 74, 75,
+ 76, 77, 78, 79, 80, 81, 82, 83 },
+ { 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95 },
+};
+
+BezierSurf teapotSurfs[32];
+
+void
+initsplines(void)
+{
+ V3d vert;
+
+ testspline1.degree = 3;
+ testspline1.verts.clear();
+ testspline1.weights.clear();
+ vert.set(-30.63383, 22.65459, 0);
+ vert = scale(vert, 1.0f/20.0f);
+ testspline1.verts.push_back(vert);
+ testspline1.weights.push_back(1.0f);
+ vert.set(13.50783, 33.01786, 15.06403);
+ vert = scale(vert, 1.0f/20.0f);
+ testspline1.verts.push_back(vert);
+ testspline1.weights.push_back(1.0f);
+ vert.set(34.252, -10.36327, 15.06403);
+ vert = scale(vert, 1.0f/20.0f);
+ testspline1.verts.push_back(vert);
+ testspline1.weights.push_back(1.0f);
+ vert.set(-7.959972, -1.205032, 0);
+ vert = scale(vert, 1.0f/20.0f);
+ testspline1.verts.push_back(vert);
+ testspline1.weights.push_back(1.0f);
+ vert.set(6.995127, -41.32158, -18.19684);
+ vert = scale(vert, 1.0f/20.0f);
+ testspline1.verts.push_back(vert);
+ testspline1.weights.push_back(1.0f);
+
+ testspline1.knots.clear();
+ testspline1.knots.push_back(0);
+ testspline1.knots.push_back(0);
+ testspline1.knots.push_back(0);
+ testspline1.knots.push_back(0);
+ testspline1.knots.push_back(1);
+ testspline1.knots.push_back(2);
+ testspline1.knots.push_back(2);
+ testspline1.knots.push_back(2);
+ testspline1.knots.push_back(2);
+
+
+ testspline2.degree = 2;
+ testspline2.verts.clear();
+#define V(x, y, z) \
+ vert.set(x, y, z); \
+ testspline2.verts.push_back(scale(vert, 1.0f/20.0f)); \
+ testspline2.weights.push_back(1.0f);
+ V(-61.9913, 9.158239, 0);
+ V(-32.32231, 27.23371, 0);
+ V(25.80961, -4.820126, 0);
+#undef V
+ testspline2.knots.clear();
+ testspline2.knots.push_back(0);
+ testspline2.knots.push_back(0);
+ testspline2.knots.push_back(0);
+ testspline2.knots.push_back(1);
+ testspline2.knots.push_back(1);
+ testspline2.knots.push_back(1);
+
+/*
+ testspline2 = testspline1;
+// testspline2.knots.clear();
+// testspline2.knots.push_back(0);
+// testspline2.knots.push_back(0);
+// testspline2.knots.push_back(0);
+// testspline2.knots.push_back(0);
+// testspline2.knots.push_back(3);
+// testspline2.knots.push_back(4);
+// testspline2.knots.push_back(4);
+// testspline2.knots.push_back(4);
+// testspline2.knots.push_back(4);
+ testspline1.weights[2] = 5.0f;
+*/
+
+#define V(x, y, z) \
+ vert.set(x, y, z); \
+ testsurf.verts.push_back(scale(vert, 1.0f/20.0f)); \
+ testsurf.weights.push_back(1.0f);
+
+ testsurf.degreeU = 3;
+ testsurf.degreeV = 3;
+ testsurf.verts.clear();
+ testsurf.weights.clear();
+ V(-69.22764, -0, 12.77366);
+ V(-48.72468, 0, 29.16251);
+ V(-24.84476, 0, 39.52605);
+ V(22.43265, -0, 45.79238);
+ V(36.4229, 0, 28.9215);
+ V(61.02645, 0, 6.74835);
+ V(86.35364, -0, 20.96809);
+ V(-69.22764, 9.286676, 12.77366);
+ V(-48.72468, 9.286676, 29.16251);
+ V(-24.84476, 9.286676, 39.52605);
+ V(22.43265, 9.286676, 45.79238);
+ V(36.4229, 9.286676, 28.9215);
+ V(61.02645, 9.286676, 6.74835);
+ V(86.35364, 9.286676, 20.96809);
+ V(-68.13416, 23.51821, 6.114491);
+ V(-48.19925, 23.51821, 22.27994);
+ V(-26.91943, 23.51821, 33.20763);
+ V(18.69658, 23.51821, 39.0488);
+ V(27.88844, 23.51821, 30.80604);
+ V(57.49908, 23.51821, -0.6488895);
+ V(86.35364, 23.51821, 14.21974);
+ V(-67.00163, 52.46369, -0.7825046);
+ V(-47.65504, 52.46369, 15.15157);
+ V(-29.06818, 52.46369, 26.66356);
+ V(14.82709, 52.46369, 32.06438);
+ V(19.04919, 52.46369, 32.75788);
+ V(53.84574, 52.46369, -8.310311);
+ V(86.35364, 52.46369, 7.230374);
+ V(-67.86079, 70.07219, 4.449699);
+ V(-48.06789, 70.07219, 20.5593);
+ V(-27.43809, 70.07219, 31.62803);
+ V(17.76257, 70.07219, 37.36291);
+ V(25.75483, 70.07219, 31.27717);
+ V(56.61724, 70.07219, -2.498198);
+ V(86.35364, 70.07219, 12.53265);
+#undef V
+ testsurf.knotsU.clear();
+ testsurf.knotsU.push_back(0);
+ testsurf.knotsU.push_back(0);
+ testsurf.knotsU.push_back(0);
+ testsurf.knotsU.push_back(0);
+ testsurf.knotsU.push_back(0.25);
+ testsurf.knotsU.push_back(0.5);
+ testsurf.knotsU.push_back(0.75);
+ testsurf.knotsU.push_back(1);
+ testsurf.knotsU.push_back(1);
+ testsurf.knotsU.push_back(1);
+ testsurf.knotsU.push_back(1);
+ testsurf.knotsV.clear();
+ testsurf.knotsV.push_back(0);
+ testsurf.knotsV.push_back(0);
+ testsurf.knotsV.push_back(0);
+ testsurf.knotsV.push_back(0);
+ testsurf.knotsV.push_back(0.5);
+ testsurf.knotsV.push_back(1);
+ testsurf.knotsV.push_back(1);
+ testsurf.knotsV.push_back(1);
+ testsurf.knotsV.push_back(1);
+
+ testsurf.update();
+
+ int i, j;
+ for(i = 0; i < 10; i++)
+ for(j = 0; j < 16; j++)
+ teapotSurfs[i].verts[j] = teapotVerts[teapotPatches[i][j]];
+ for(i = 0; i < 10; i++){
+ teapotSurfs[i+10] = teapotSurfs[i];
+ teapotSurfs[i+10].mirrorY();
+ }
+ for(i = 0; i < 6; i++){
+ teapotSurfs[i+20] = teapotSurfs[i];
+ teapotSurfs[i+20].mirrorX();
+ teapotSurfs[i+20+6] = teapotSurfs[i+10];
+ teapotSurfs[i+20+6].mirrorX();
+ }
+}
+
+void
+rendersplines(void)
+{
+// testspline1.drawHull();
+// testspline1.drawSpline();
+
+// testspline2.drawHull();
+// testspline2.drawSpline();
+
+// testsurf.drawHull();
+// testsurf.drawShaded();
+// testsurf.drawIsoparms();
+
+ int i;
+ for(i = 0; i < 32; i++){
+ teapotSurfs[i].drawHull();
+ teapotSurfs[i].drawShaded();
+ }
+}
diff --git a/tools/playground/tl_tests.cpp b/tools/playground/tl_tests.cpp
new file mode 100644
index 0000000..7cfc59a
--- /dev/null
+++ b/tools/playground/tl_tests.cpp
@@ -0,0 +1,766 @@
+#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 int32
+clipPoly(CamSpace3DVertex *in, int32 nin, CamSpace3DVertex *out, Plane *plane)
+{
+ int32 j;
+ int32 nout;
+ int32 x1, x2;
+ float32 d1, d2, t;
+
+ nout = 0;
+ for(j = 0; j < nin; j++){
+ x1 = j;
+ x2 = (j+1) % nin;
+
+ d1 = dot(plane->normal, in[x1].camVertex) + plane->distance;
+ d2 = dot(plane->normal, in[x2].camVertex) + plane->distance;
+ if(d1*d2 < 0.0f){
+ t = d1/(d1 - d2);
+ interpVertex(&out[nout++], &in[x1], &in[x2], t);
+ }
+ if(d2 >= 0.0f)
+ out[nout++] = in[x2];
+ }
+ return nout;
+}
+
+static void
+clipTriangles2(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;
+ int32 nout;
+ Camera *cam = (Camera*)engine->currentCamera;
+
+ Plane planes[6] = {
+ { { 0.0f, 0.0f, 1.0f }, -cam->nearPlane }, // z = near
+ { { 0.0f, 0.0f, -1.0f }, cam->farPlane }, // z = far
+
+ { { -1.0f, 0.0f, 1.0f }, 0.0f }, // x = w
+// { { 1.0f, 0.0f, 1.0f }, 0.0f }, // x = -w
+ { { 1.0f, 0.0f, 0.0f }, 0.0f }, // x = 0
+
+ { { 0.0f, -1.0f, 1.0f }, 0.0f }, // y = w
+// { { 0.0f, 1.0f, 1.0f }, 0.0f } // y = -1
+ { { 0.0f, 1.0f, 0.0f }, 0.0f } // y = 0
+ };
+
+ 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];
+ nout = 0;
+
+ // clip here
+ if(nout = clipPoly(in, 3, out, &planes[0]), nout == 0) continue;
+ if(nout = clipPoly(out, nout, in, &planes[1]), nout == 0) continue;
+ if(nout = clipPoly(in, nout, out, &planes[2]), nout == 0) continue;
+ if(nout = clipPoly(out, nout, in, &planes[3]), nout == 0) continue;
+ if(nout = clipPoly(in, nout, out, &planes[4]), nout == 0) continue;
+ if(nout = clipPoly(out, nout, in, &planes[5]), nout == 0) continue;
+ out = in;
+
+ // 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);
+ clipTriangles2(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;
+}