diff options
Diffstat (limited to 'tools/playground')
| -rw-r--r-- | tools/playground/CMakeLists.txt | 22 | ||||
| -rw-r--r-- | tools/playground/camera.cpp | 134 | ||||
| -rw-r--r-- | tools/playground/camera.h | 26 | ||||
| -rw-r--r-- | tools/playground/files/Bm437_IBM_BIOS.FON | bin | 0 -> 4064 bytes | |||
| -rw-r--r-- | tools/playground/files/Bm437_IBM_BIOS.tga | bin | 0 -> 65554 bytes | |||
| -rw-r--r-- | tools/playground/files/Bm437_IBM_VGA8.FON | bin | 0 -> 6112 bytes | |||
| -rw-r--r-- | tools/playground/files/Bm437_IBM_VGA8.tga | bin | 0 -> 131090 bytes | |||
| -rw-r--r-- | tools/playground/files/foobar.tga | bin | 0 -> 65554 bytes | |||
| -rw-r--r-- | tools/playground/files/maze.tga | bin | 0 -> 49196 bytes | |||
| -rw-r--r-- | tools/playground/files/teapot.dff | bin | 0 -> 42796 bytes | |||
| -rw-r--r-- | tools/playground/font.cpp | 181 | ||||
| -rw-r--r-- | tools/playground/main.cpp | 641 | ||||
| -rw-r--r-- | tools/playground/ras_test.cpp | 902 | ||||
| -rw-r--r-- | tools/playground/splines.cpp | 750 | ||||
| -rw-r--r-- | tools/playground/tl_tests.cpp | 766 |
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 Binary files differnew file mode 100644 index 0000000..be2443f --- /dev/null +++ b/tools/playground/files/Bm437_IBM_BIOS.FON diff --git a/tools/playground/files/Bm437_IBM_BIOS.tga b/tools/playground/files/Bm437_IBM_BIOS.tga Binary files differnew file mode 100644 index 0000000..50034b4 --- /dev/null +++ b/tools/playground/files/Bm437_IBM_BIOS.tga diff --git a/tools/playground/files/Bm437_IBM_VGA8.FON b/tools/playground/files/Bm437_IBM_VGA8.FON Binary files differnew file mode 100644 index 0000000..6ffa09a --- /dev/null +++ b/tools/playground/files/Bm437_IBM_VGA8.FON diff --git a/tools/playground/files/Bm437_IBM_VGA8.tga b/tools/playground/files/Bm437_IBM_VGA8.tga Binary files differnew file mode 100644 index 0000000..521fb40 --- /dev/null +++ b/tools/playground/files/Bm437_IBM_VGA8.tga diff --git a/tools/playground/files/foobar.tga b/tools/playground/files/foobar.tga Binary files differnew file mode 100644 index 0000000..895bae0 --- /dev/null +++ b/tools/playground/files/foobar.tga diff --git a/tools/playground/files/maze.tga b/tools/playground/files/maze.tga Binary files differnew file mode 100644 index 0000000..4714c42 --- /dev/null +++ b/tools/playground/files/maze.tga diff --git a/tools/playground/files/teapot.dff b/tools/playground/files/teapot.dff Binary files differnew file mode 100644 index 0000000..7a65176 --- /dev/null +++ b/tools/playground/files/teapot.dff 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; +} |
