summaryrefslogtreecommitdiff
path: root/tools
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
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')
-rw-r--r--tools/CMakeLists.txt20
-rw-r--r--tools/camera/CMakeLists.txt19
-rw-r--r--tools/camera/camexamp.cpp395
-rw-r--r--tools/camera/camexamp.h62
-rw-r--r--tools/camera/files/clump.dffbin0 -> 57700 bytes
-rw-r--r--tools/camera/files/clump/shinarm.pngbin0 -> 84781 bytes
-rw-r--r--tools/camera/files/clump/shinbody.pngbin0 -> 101261 bytes
-rw-r--r--tools/camera/files/clump/shinface.pngbin0 -> 81338 bytes
-rw-r--r--tools/camera/files/clump/shinleg.pngbin0 -> 89939 bytes
-rw-r--r--tools/camera/main.cpp508
-rw-r--r--tools/camera/viewer.cpp51
-rw-r--r--tools/camera/viewer.h6
-rw-r--r--tools/dumprwtree/CMakeLists.txt20
-rw-r--r--tools/dumprwtree/dumprwtree.cpp145
-rw-r--r--tools/hopalong/main.cpp469
-rw-r--r--tools/hopalong/particle.inc467
-rw-r--r--tools/hopalong/particle.pngbin0 -> 5564 bytes
-rw-r--r--tools/hopalong/vfs.cpp133
-rw-r--r--tools/hopalong/vfs.h15
-rw-r--r--tools/im2d/CMakeLists.txt23
-rw-r--r--tools/im2d/files/whiteash.pngbin0 -> 4313 bytes
-rw-r--r--tools/im2d/im2d.cpp134
-rw-r--r--tools/im2d/im2d.h62
-rw-r--r--tools/im2d/linelist.cpp159
-rw-r--r--tools/im2d/main.cpp254
-rw-r--r--tools/im2d/polyline.cpp132
-rw-r--r--tools/im2d/trifan.cpp119
-rw-r--r--tools/im2d/trilist.cpp166
-rw-r--r--tools/im2d/tristrip.cpp130
-rw-r--r--tools/im3d/CMakeLists.txt23
-rw-r--r--tools/im3d/files/whiteash.pngbin0 -> 4313 bytes
-rw-r--r--tools/im3d/im3d.cpp138
-rw-r--r--tools/im3d/im3d.h62
-rw-r--r--tools/im3d/linelist.cpp176
-rw-r--r--tools/im3d/main.cpp268
-rw-r--r--tools/im3d/polyline.cpp157
-rw-r--r--tools/im3d/trifan.cpp152
-rw-r--r--tools/im3d/trilist.cpp202
-rw-r--r--tools/im3d/tristrip.cpp169
-rw-r--r--tools/imguitest/CMakeLists.txt17
-rw-r--r--tools/imguitest/main.cpp175
-rw-r--r--tools/lights/CMakeLists.txt18
-rw-r--r--tools/lights/checker.dffbin0 -> 14348 bytes
-rw-r--r--tools/lights/lights.cpp463
-rw-r--r--tools/lights/lights.h37
-rw-r--r--tools/lights/main.cpp396
-rw-r--r--tools/lights/main.h7
-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
-rw-r--r--tools/ps2test/CMakeLists.txt26
-rw-r--r--tools/ps2test/gs.h437
-rwxr-xr-xtools/ps2test/main.cpp788
-rw-r--r--tools/ps2test/mem.h95
-rw-r--r--tools/ps2test/ps2.h23
-rw-r--r--tools/ps2test/vu/defaultpipe.dsm93
-rw-r--r--tools/ps2test/vu/light.vu94
-rw-r--r--tools/ps2test/vu/setup_persp.vu39
-rw-r--r--tools/ps2test/vu/skinpipe.dsm94
-rw-r--r--tools/ska2anm/CMakeLists.txt20
-rw-r--r--tools/ska2anm/ska2anm.cpp83
-rw-r--r--tools/subrast/CMakeLists.txt19
-rw-r--r--tools/subrast/files/clump.dffbin0 -> 76384 bytes
-rw-r--r--tools/subrast/files/textures/whiteash.pngbin0 -> 4313 bytes
-rw-r--r--tools/subrast/main.cpp354
-rw-r--r--tools/subrast/subrast.cpp141
-rw-r--r--tools/subrast/subrast.h10
79 files changed, 11687 insertions, 0 deletions
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
new file mode 100644
index 0000000..be7ea66
--- /dev/null
+++ b/tools/CMakeLists.txt
@@ -0,0 +1,20 @@
+if(LIBRW_TOOLS AND NOT LIBRW_PLATFORM_PS2)
+ add_subdirectory(dumprwtree)
+ add_subdirectory(ska2anm)
+endif()
+
+if(LIBRW_EXAMPLES)
+ if(TARGET librw::skeleton)
+ add_subdirectory(imguitest)
+ add_subdirectory(playground)
+ add_subdirectory(lights)
+ add_subdirectory(subrast)
+ add_subdirectory(camera)
+ add_subdirectory(im2d)
+ add_subdirectory(im3d)
+ endif()
+
+ if(LIBRW_PLATFORM_PS2)
+ add_subdirectory(ps2test)
+ endif()
+endif()
diff --git a/tools/camera/CMakeLists.txt b/tools/camera/CMakeLists.txt
new file mode 100644
index 0000000..6f0dd5f
--- /dev/null
+++ b/tools/camera/CMakeLists.txt
@@ -0,0 +1,19 @@
+add_executable(camera WIN32
+ main.cpp
+ camexamp.cpp
+ viewer.cpp
+)
+
+target_link_libraries(camera
+ PRIVATE
+ librw::skeleton
+ librw::librw
+)
+
+add_custom_command(
+ TARGET camera POST_BUILD
+ COMMAND "${CMAKE_COMMAND}" -E make_directory "$<TARGET_FILE_DIR:camera>/files"
+ COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/files" "$<TARGET_FILE_DIR:camera>/files"
+)
+
+librw_platform_target(camera)
diff --git a/tools/camera/camexamp.cpp b/tools/camera/camexamp.cpp
new file mode 100644
index 0000000..5f072c6
--- /dev/null
+++ b/tools/camera/camexamp.cpp
@@ -0,0 +1,395 @@
+#include <rw.h>
+#include <skeleton.h>
+
+#include "viewer.h"
+#include "camexamp.h"
+
+#define TEXSIZE 256
+
+rw::Camera *MainCamera;
+rw::Camera *SubCamera;
+
+rw::Raster *SubCameraRaster;
+rw::Raster *SubCameraZRaster;
+rw::Raster *SubCameraMainCameraSubRaster;
+rw::Raster *SubCameraMainCameraSubZRaster;
+
+TextureCamera CameraTexture;
+
+rw::int32 CameraSelected = 0;
+rw::int32 ProjectionIndex = 0;
+bool SubCameraMiniView = true;
+
+CameraData SubCameraData;
+
+void
+CameraQueryData(CameraData *data, CameraDataType type, rw::Camera *camera)
+{
+ data->camera = camera;
+ if(type & FARCLIPPLANE) data->farClipPlane = camera->farPlane;
+ if(type & NEARCLIPPLANE) data->nearClipPlane = camera->nearPlane;
+ if(type & PROJECTION) data->projection = camera->projection;
+ if(type & OFFSET) data->offset = camera->viewOffset;
+ if(type & VIEWWINDOW) data->viewWindow = camera->viewWindow;
+ if(type & MATRIX) data->matrix = &camera->getFrame()->matrix;
+}
+
+void
+CameraSetData(CameraData *data, CameraDataType type)
+{
+ if(type & FARCLIPPLANE) data->camera->setFarPlane(data->farClipPlane);
+ if(type & NEARCLIPPLANE) data->camera->setNearPlane(data->nearClipPlane);
+ if(type & PROJECTION) data->camera->setProjection(data->projection);
+ if(type & OFFSET) data->camera->setViewOffset(&data->offset);
+ if(type & VIEWWINDOW) data->camera->setViewWindow(&data->viewWindow);
+}
+
+void
+ProjectionCallback(void)
+{
+ if(ProjectionIndex == 0)
+ SubCameraData.projection = rw::Camera::PERSPECTIVE;
+ else
+ SubCameraData.projection = rw::Camera::PARALLEL;
+ CameraSetData(&SubCameraData, PROJECTION);
+}
+
+void
+ClipPlaneCallback(void)
+{
+ CameraSetData(&SubCameraData, (CameraDataType)(NEARCLIPPLANE | FARCLIPPLANE));
+}
+
+void
+ChangeViewOffset(float deltaX, float deltaY)
+{
+ SubCameraData.offset.x += deltaX;
+ SubCameraData.offset.y += deltaY;
+ if(SubCameraData.offset.x > 5.0f)
+ SubCameraData.offset.x = 5.0f;
+ if(SubCameraData.offset.x < -5.0f)
+ SubCameraData.offset.x = -5.0f;
+ if(SubCameraData.offset.y > 5.0f)
+ SubCameraData.offset.y = 5.0f;
+ if(SubCameraData.offset.y < -5.0f)
+ SubCameraData.offset.y = -5.0f;
+ CameraSetData(&SubCameraData, OFFSET);
+}
+
+void
+ChangeViewWindow(float deltaX, float deltaY)
+{
+ SubCameraData.viewWindow.x += deltaX;
+ SubCameraData.viewWindow.y += deltaY;
+ if(SubCameraData.viewWindow.x > 5.0f)
+ SubCameraData.viewWindow.x = 5.0f;
+ if(SubCameraData.viewWindow.x < 0.01f)
+ SubCameraData.viewWindow.x = 0.01f;
+ if(SubCameraData.viewWindow.y > 5.0f)
+ SubCameraData.viewWindow.y = 5.0f;
+ if(SubCameraData.viewWindow.y < 0.01f)
+ SubCameraData.viewWindow.y = 0.01f;
+ CameraSetData(&SubCameraData, VIEWWINDOW);
+}
+
+void
+CamerasCreate(rw::World *world)
+{
+ rw::V3d offset = { 3.0f, 0.0f, 8.0f };
+ float rotate = -90.0f;
+
+ SubCamera = ViewerCreate(world);
+ ViewerMove(SubCamera, &offset);
+ ViewerRotate(SubCamera, rotate, 0.0f);
+
+ MainCamera = ViewerCreate(world);
+
+ CameraQueryData(&SubCameraData, ALL, SubCamera);
+
+ SubCameraData.nearClipPlane = 0.3f;
+ CameraSetData(&SubCameraData, NEARCLIPPLANE);
+
+ SubCameraData.farClipPlane = 5.0f;
+ CameraSetData(&SubCameraData, FARCLIPPLANE);
+
+ CameraTexture.camera = SubCamera;
+ CameraTextureInit(&CameraTexture);
+
+ SubCameraData.cameraTexture = &CameraTexture;
+
+
+ SubCameraMainCameraSubRaster = rw::Raster::create(0, 0, 0, rw::Raster::CAMERA);
+ SubCameraMainCameraSubZRaster = rw::Raster::create(0, 0, 0, rw::Raster::ZBUFFER);
+}
+
+void
+CamerasDestroy(rw::World *world)
+{
+ SubCameraMiniViewSelect(false);
+
+ if(MainCamera){
+ ViewerDestroy(MainCamera, world);
+ MainCamera = nil;
+ }
+
+ if(SubCamera){
+ ViewerDestroy(SubCamera, world);
+ SubCamera = nil;
+ }
+
+ CameraTextureTerm(&CameraTexture);
+
+ if(SubCameraMainCameraSubRaster){
+ SubCameraMainCameraSubRaster->destroy();
+ SubCameraMainCameraSubRaster = nil;
+ }
+
+ if(SubCameraMainCameraSubZRaster){
+ SubCameraMainCameraSubZRaster->destroy();
+ SubCameraMainCameraSubZRaster = nil;
+ }
+}
+
+void
+UpdateSubRaster(rw::Camera *camera, rw::Rect *rect)
+{
+ rw::Rect subRect;
+
+ subRect.x = rect->w * 0.75f;
+ subRect.y = 0;
+
+ subRect.w = rect->w * 0.25f;
+ subRect.h = rect->h * 0.25f;
+
+ SubCameraMainCameraSubRaster->subRaster(camera->frameBuffer, &subRect);
+ SubCameraMainCameraSubZRaster->subRaster(camera->zBuffer, &subRect);
+}
+
+void
+CameraSizeUpdate(rw::Rect *rect, float viewWindow, float aspectRatio)
+{
+ static bool RasterInit;
+
+ if(MainCamera == nil)
+ return;
+
+ sk::CameraSize(MainCamera, rect, viewWindow, aspectRatio);
+
+ UpdateSubRaster(MainCamera, rect);
+
+ if(RasterInit)
+ SubCameraMiniViewSelect(false);
+
+ sk::CameraSize(SubCamera, rect, viewWindow, aspectRatio);
+
+ SubCameraRaster = SubCamera->frameBuffer;
+ SubCameraZRaster = SubCamera->zBuffer;
+
+ RasterInit = true;
+ SubCameraMiniViewSelect(CameraSelected == 0);
+
+ CameraQueryData(&SubCameraData, VIEWWINDOW, SubCamera);
+}
+
+void
+RenderSubCamera(rw::RGBA *backgroundColor, rw::int32 clearMode, rw::World *world)
+{
+ SubCamera->clear(backgroundColor, clearMode);
+ SubCamera->beginUpdate();
+ world->render();
+ SubCamera->endUpdate();
+}
+
+void
+RenderTextureCamera(rw::RGBA *foregroundColor, rw::int32 clearMode, rw::World *world)
+{
+ rw::Raster *saveRaster, *saveZRaster;
+
+ saveRaster = CameraTexture.camera->frameBuffer;
+ saveZRaster = CameraTexture.camera->zBuffer;
+ CameraTexture.camera->frameBuffer = CameraTexture.raster;
+ CameraTexture.camera->zBuffer = CameraTexture.zRaster;
+
+ CameraTexture.camera->clear(foregroundColor, clearMode);
+ CameraTexture.camera->beginUpdate();
+ world->render();
+ CameraTexture.camera->endUpdate();
+
+
+ CameraTexture.camera->frameBuffer = saveRaster;
+ CameraTexture.camera->zBuffer = saveZRaster;
+}
+
+void
+SubCameraMiniViewSelect(bool select)
+{
+ if(select){
+ SubCamera->frameBuffer = SubCameraMainCameraSubRaster;
+ SubCamera->zBuffer = SubCameraMainCameraSubZRaster;
+ }else{
+ SubCamera->frameBuffer = SubCameraRaster;
+ SubCamera->zBuffer = SubCameraZRaster;
+ }
+}
+
+
+
+void
+CameraTextureInit(TextureCamera *ct)
+{
+ ct->raster = rw::Raster::create(TEXSIZE, TEXSIZE, 0, rw::Raster::CAMERATEXTURE);
+ assert(ct->raster);
+ ct->zRaster = rw::Raster::create(TEXSIZE, TEXSIZE, 0, rw::Raster::ZBUFFER);
+ assert(ct->zRaster);
+
+ ct->texture = rw::Texture::create(ct->raster);
+ ct->texture->setFilter(rw::Texture::FilterMode::LINEAR);
+}
+
+void
+CameraTextureTerm(TextureCamera *ct)
+{
+ if(ct->raster){
+ ct->raster->destroy();
+ ct->raster = nil;
+ }
+
+ if(ct->zRaster){
+ ct->zRaster->destroy();
+ ct->zRaster = nil;
+ }
+
+ if(ct->texture){
+ ct->texture->raster = nil;
+ ct->texture->destroy();
+ ct->texture = nil;
+ }
+}
+
+
+
+void
+DrawCameraFrustum(CameraData *c)
+{
+ rw::RGBA yellow = { 255, 255, 0, 64 };
+ rw::RGBA red = { 255, 0, 0, 255 };
+ rw::RWDEVICE::Im3DVertex frustum[13];
+ // lines
+ rw::uint16 indicesL[] = {
+ 1, 2, 2, 3, 3, 4, 4, 1,
+ 5, 6, 6, 7, 7, 8, 8, 5,
+ 9, 10, 10, 11, 11, 12, 12, 9,
+ 5, 9, 6, 10, 7, 11, 8, 12,
+ 0, 0
+ };
+ // triangles
+ rw::uint16 indicesT[] = {
+ 5, 6, 10,
+ 10, 9, 5,
+ 6, 7, 11,
+ 11, 10, 6,
+ 7, 8, 12,
+ 12, 11, 7,
+ 8, 5, 9,
+ 9, 12, 8,
+
+ 7, 6, 5,
+ 5, 8, 7,
+ 9, 10, 11,
+ 11, 12, 9
+ };
+ float signs[4][2] = {
+ { 1, 1 },
+ { -1, 1 },
+ { -1, -1 },
+ { 1, -1 }
+ };
+
+ float depth[3];
+ depth[0] = 1.0f; // view window
+ depth[1] = c->nearClipPlane;
+ depth[2] = c->farClipPlane;
+
+ int k = 0;
+ frustum[k].setX(c->offset.x);
+ frustum[k].setY(c->offset.y);
+ frustum[k].setZ(0.0f);
+ k++;
+
+ for(int i = 0; i < 3; i++) // depths
+ for(int j = 0; j < 4; j++){ // planes
+ if(c->projection == rw::Camera::PERSPECTIVE){
+ frustum[k].setX(-c->offset.x + depth[i]*(signs[j][0]*c->viewWindow.x + c->offset.x));
+ frustum[k].setY(c->offset.y + depth[i]*(signs[j][1]*c->viewWindow.y - c->offset.y));
+ frustum[k].setZ(depth[i]);
+ }else{
+ frustum[k].setX(-c->offset.x + signs[j][0]*c->viewWindow.x + depth[i]*c->offset.x);
+ frustum[k].setY(c->offset.y + signs[j][1]*c->viewWindow.y - depth[i]*c->offset.y);
+ frustum[k].setZ(depth[i]);
+ }
+ k++;
+ }
+
+ for(int i = 0; i < 5; i++)
+ frustum[i].setColor(red.red, red.green, red.blue, red.alpha);
+ for(int i = 5; i < 13; i++)
+ frustum[i].setColor(yellow.red, yellow.green, yellow.blue, 255);
+
+ rw::SetRenderStatePtr(rw::TEXTURERASTER, nil);
+
+ rw::im3d::Transform(frustum, 13, c->camera->getFrame()->getLTM(), rw::im3d::ALLOPAQUE);
+ rw::im3d::RenderIndexedPrimitive(rw::PRIMTYPELINELIST, indicesL, 34);
+ rw::im3d::End();
+
+ for(int i = 5; i < 13; i++)
+ frustum[i].setColor(yellow.red, yellow.green, yellow.blue, yellow.alpha);
+
+ rw::SetRenderState(rw::VERTEXALPHA, 1);
+
+ rw::im3d::Transform(frustum, 13, c->camera->getFrame()->getLTM(), rw::im3d::ALLOPAQUE);
+ rw::im3d::RenderIndexedPrimitive(rw::PRIMTYPETRILIST, indicesT, 36);
+ rw::im3d::End();
+}
+
+void
+DrawCameraViewplaneTexture(CameraData *c)
+{
+ rw::RGBA white = { 255, 255, 255, 255 };
+ rw::RWDEVICE::Im3DVertex frustum[4];
+ rw::uint16 indicesV[] = {
+ 2, 1, 0,
+ 0, 3, 2,
+ 0, 1, 2,
+ 2, 3, 0
+ };
+ float uvValues[4][2] = {
+ { 0.0f, 0.0f },
+ { 1.0f, 0.0f },
+ { 1.0f, 1.0f },
+ { 0.0f, 1.0f }
+ };
+ float signs[4][2] = {
+ { 1, 1 },
+ { -1, 1 },
+ { -1, -1 },
+ { 1, -1 }
+ };
+
+ for(int j = 0; j < 4; j++){
+ frustum[j].setX(signs[j][0]*c->viewWindow.x);
+ frustum[j].setY(signs[j][1]*c->viewWindow.y);
+ frustum[j].setZ(1.0f);
+ }
+ for(int i = 0; i < 4; i++){
+ frustum[i].setColor(white.red, white.green, white.blue, white.alpha);
+ frustum[i].setU(uvValues[i][0]);
+ frustum[i].setV(uvValues[i][1]);
+ }
+
+ rw::SetRenderState(rw::VERTEXALPHA, 1);
+ rw::SetRenderStatePtr(rw::TEXTURERASTER, c->cameraTexture->texture->raster);
+
+ rw::im3d::Transform(frustum, 4, c->camera->getFrame()->getLTM(), rw::im3d::VERTEXUV);
+ rw::im3d::RenderIndexedPrimitive(rw::PRIMTYPETRILIST, indicesV, 12);
+ rw::im3d::End();
+}
diff --git a/tools/camera/camexamp.h b/tools/camera/camexamp.h
new file mode 100644
index 0000000..7df301f
--- /dev/null
+++ b/tools/camera/camexamp.h
@@ -0,0 +1,62 @@
+struct TextureCamera
+{
+ rw::Raster *raster;
+ rw::Raster *zRaster;
+ rw::Camera *camera;
+ rw::Texture *texture;
+};
+
+struct CameraData
+{
+ float farClipPlane;
+ float nearClipPlane;
+ rw::uint32 projection;
+ rw::V2d offset;
+ rw::V2d viewWindow;
+ rw::Camera *camera;
+ TextureCamera *cameraTexture;
+ rw::Matrix *matrix;
+};
+
+enum CameraDataType
+{
+ NONE = 0x00,
+ FARCLIPPLANE = 0x01,
+ NEARCLIPPLANE = 0x02,
+ PROJECTION = 0x04,
+ OFFSET = 0x08,
+ VIEWWINDOW = 0x10,
+ MATRIX = 0x20,
+ ALL = 0xFF
+};
+
+extern rw::Camera *MainCamera;
+extern rw::Camera *SubCamera;
+
+extern rw::int32 CameraSelected;
+extern rw::int32 ProjectionIndex;
+
+extern CameraData SubCameraData;
+
+void CameraQueryData(CameraData *data, CameraDataType type, rw::Camera *camera);
+void CameraSetData(CameraData *data, CameraDataType type);
+
+void ChangeViewOffset(float deltaX, float deltaY);
+void ChangeViewWindow(float deltaX, float deltaY);
+void ProjectionCallback(void);
+void ClipPlaneCallback(void);
+
+void CamerasCreate(rw::World *world);
+void CamerasDestroy(rw::World *world);
+void CameraSizeUpdate(rw::Rect *rect, float viewWindow, float aspectRatio);
+void RenderSubCamera(rw::RGBA *foregroundColor, rw::int32 clearMode, rw::World *world);
+void RenderTextureCamera(rw::RGBA *foregroundColor, rw::int32 clearMode, rw::World *world);
+void SubCameraMiniViewSelect(bool select);
+
+void CameraTextureInit(TextureCamera *ct);
+void CameraTextureTerm(TextureCamera *ct);
+void DrawCameraFrustum(CameraData *c);
+void DrawCameraViewplaneTexture(CameraData *c);
+
+void ViewerRotate(rw::Camera *camera, float deltaX, float deltaY);
+void ViewerTranslate(rw::Camera *camera, float deltaX, float deltaY);
diff --git a/tools/camera/files/clump.dff b/tools/camera/files/clump.dff
new file mode 100644
index 0000000..960354f
--- /dev/null
+++ b/tools/camera/files/clump.dff
Binary files differ
diff --git a/tools/camera/files/clump/shinarm.png b/tools/camera/files/clump/shinarm.png
new file mode 100644
index 0000000..1dbc402
--- /dev/null
+++ b/tools/camera/files/clump/shinarm.png
Binary files differ
diff --git a/tools/camera/files/clump/shinbody.png b/tools/camera/files/clump/shinbody.png
new file mode 100644
index 0000000..645b198
--- /dev/null
+++ b/tools/camera/files/clump/shinbody.png
Binary files differ
diff --git a/tools/camera/files/clump/shinface.png b/tools/camera/files/clump/shinface.png
new file mode 100644
index 0000000..014ca32
--- /dev/null
+++ b/tools/camera/files/clump/shinface.png
Binary files differ
diff --git a/tools/camera/files/clump/shinleg.png b/tools/camera/files/clump/shinleg.png
new file mode 100644
index 0000000..e56c1c3
--- /dev/null
+++ b/tools/camera/files/clump/shinleg.png
Binary files differ
diff --git a/tools/camera/main.cpp b/tools/camera/main.cpp
new file mode 100644
index 0000000..b50b40e
--- /dev/null
+++ b/tools/camera/main.cpp
@@ -0,0 +1,508 @@
+#include <rw.h>
+#include <skeleton.h>
+#include <assert.h>
+
+#include "viewer.h"
+#include "camexamp.h"
+
+rw::V3d zero = { 0.0f, 0.0f, 0.0f };
+rw::EngineOpenParams engineOpenParams;
+float FOV = 70.0f;
+
+rw::RGBA ForegroundColor = { 200, 200, 200, 255 };
+rw::RGBA BackgroundColor = { 64, 64, 64, 0 };
+rw::RGBA BackgroundColorSub = { 74, 74, 74, 0 };
+
+rw::World *World;
+rw::Charset *Charset;
+
+rw::V3d Xaxis = { 1.0f, 0.0, 0.0f };
+rw::V3d Yaxis = { 0.0f, 1.0, 0.0f };
+rw::V3d Zaxis = { 0.0f, 0.0, 1.0f };
+
+float TimeDelta;
+
+rw::Clump *Clump;
+
+rw::World*
+CreateWorld(void)
+{
+ rw::BBox bb;
+
+ bb.inf.x = bb.inf.y = bb.inf.z = -100.0f;
+ bb.sup.x = bb.sup.y = bb.sup.z = 100.0f;
+
+ return rw::World::create(&bb);
+}
+
+void
+LightsCreate(rw::World *world)
+{
+ rw::Light *light = rw::Light::create(rw::Light::AMBIENT);
+ assert(light);
+ World->addLight(light);
+
+ light = rw::Light::create(rw::Light::DIRECTIONAL);
+ assert(light);
+ rw::Frame *frame = rw::Frame::create();
+ assert(frame);
+ frame->rotate(&Xaxis, 30.0f, rw::COMBINEREPLACE);
+ frame->rotate(&Yaxis, 30.0f, rw::COMBINEPOSTCONCAT);
+ light->setFrame(frame);
+ World->addLight(light);
+}
+
+rw::Clump*
+ClumpCreate(rw::World *world)
+{
+ rw::Clump *clump;
+ rw::StreamFile in;
+
+ rw::Image::setSearchPath("files/clump/");
+ const char *filename = "files/clump.dff";
+ if(in.open(filename, "rb") == NULL){
+ printf("couldn't open file\n");
+ return nil;
+ }
+ if(!rw::findChunk(&in, rw::ID_CLUMP, NULL, NULL))
+ return nil;
+ clump = rw::Clump::streamRead(&in);
+ in.close();
+ if(clump == nil)
+ return nil;
+
+ rw::Frame *frame = clump->getFrame();
+ rw::V3d pos = { 0.0f, 0.0f, 8.0f };
+ frame->translate(&pos, rw::COMBINEREPLACE);
+ World->addClump(clump);
+ return clump;
+}
+
+void
+ClumpRotate(rw::Clump *clump, rw::Camera *camera, float xAngle, float yAngle)
+{
+ rw::Matrix *cameraMatrix = &camera->getFrame()->matrix;
+ rw::Frame *clumpFrame = clump->getFrame();
+ rw::V3d pos = clumpFrame->matrix.pos;
+
+ pos = rw::scale(pos, -1.0f);
+ clumpFrame->translate(&pos, rw::COMBINEPOSTCONCAT);
+
+ clumpFrame->rotate(&cameraMatrix->up, xAngle, rw::COMBINEPOSTCONCAT);
+ clumpFrame->rotate(&cameraMatrix->right, yAngle, rw::COMBINEPOSTCONCAT);
+
+ pos = rw::scale(pos, -1.0f);
+ clumpFrame->translate(&pos, rw::COMBINEPOSTCONCAT);
+}
+
+void
+ClumpTranslate(rw::Clump *clump, rw::Camera *camera, float xDelta, float yDelta)
+{
+ rw::Matrix *cameraMatrix = &camera->getFrame()->matrix;
+ rw::Frame *clumpFrame = clump->getFrame();
+
+ rw::V3d deltaX = rw::scale(cameraMatrix->right, xDelta);
+ rw::V3d deltaZ = rw::scale(cameraMatrix->at, yDelta);
+ rw::V3d delta = rw::add(deltaX, deltaZ);
+
+ clumpFrame->translate(&delta, rw::COMBINEPOSTCONCAT);
+}
+
+void
+ClumpSetPosition(rw::Clump *clump, rw::V3d *position)
+{
+ clump->getFrame()->translate(position, rw::COMBINEREPLACE);
+}
+
+void
+Initialize(void)
+{
+ sk::globals.windowtitle = "Camera example";
+ sk::globals.width = 1280;
+ sk::globals.height = 800;
+ sk::globals.quit = 0;
+}
+
+bool
+Initialize3D(void)
+{
+ if(!sk::InitRW())
+ return false;
+
+ Charset = rw::Charset::create(&ForegroundColor, &BackgroundColor);
+
+ World = CreateWorld();
+
+ CamerasCreate(World);
+ LightsCreate(World);
+
+ Clump = ClumpCreate(World);
+
+ rw::SetRenderState(rw::CULLMODE, rw::CULLBACK);
+ rw::SetRenderState(rw::ZTESTENABLE, 1);
+ rw::SetRenderState(rw::ZWRITEENABLE, 1);
+
+ ImGui_ImplRW_Init();
+ ImGui::StyleColorsClassic();
+
+ rw::Rect r;
+ r.x = 0;
+ r.y = 0;
+ r.w = sk::globals.width;
+ r.h = sk::globals.height;
+ CameraSizeUpdate(&r, 0.5f, 4.0f/3.0f);
+
+ return true;
+}
+
+void
+DestroyLight(rw::Light *light, rw::World *world)
+{
+ world->removeLight(light);
+ rw::Frame *frame = light->getFrame();
+ if(frame){
+ light->setFrame(nil);
+ frame->destroy();
+ }
+ light->destroy();
+}
+
+void
+Terminate3D(void)
+{
+ if(Clump){
+ World->removeClump(Clump);
+ Clump->destroy();
+ Clump = nil;
+ }
+
+ FORLIST(lnk, World->globalLights){
+ rw::Light *light = rw::Light::fromWorld(lnk);
+ DestroyLight(light, World);
+ }
+ FORLIST(lnk, World->localLights){
+ rw::Light *light = rw::Light::fromWorld(lnk);
+ DestroyLight(light, World);
+ }
+
+ CamerasDestroy(World);
+
+ if(World){
+ World->destroy();
+ World = nil;
+ }
+
+ if(Charset){
+ Charset->destroy();
+ Charset = nil;
+ }
+
+ sk::TerminateRW();
+}
+
+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
+DisplayOnScreenInfo(void)
+{
+ char str[256];
+ sprintf(str, "View window (%.2f, %.2f)", SubCameraData.viewWindow.x, SubCameraData.viewWindow.y);
+ Charset->print(str, 100, 100, 0);
+ sprintf(str, "View offset (%.2f, %.2f)", SubCameraData.offset.x, SubCameraData.offset.y);
+ Charset->print(str, 100, 120, 0);
+}
+
+void
+ResetCameraAndClump(void)
+{
+ SubCameraData.nearClipPlane = 0.3f;
+ SubCameraData.farClipPlane = 5.0f;
+ SubCameraData.projection = rw::Camera::PERSPECTIVE;
+ SubCameraData.offset.x = 0.0f;
+ SubCameraData.offset.y = 0.0f;
+ SubCameraData.viewWindow.x = 0.5f;
+ SubCameraData.viewWindow.y = 0.38f;
+ CameraSetData(&SubCameraData, ALL);
+ ProjectionIndex = 0;
+
+ rw::V3d position = { 3.0f, 0.0f, 8.0f };
+ rw::V3d point = { 0.0f, 0.0f, 8.0f };
+ ViewerSetPosition(SubCameraData.camera, &position);
+ ViewerRotate(SubCamera, -90.0f, 0.0f);
+
+ ClumpSetPosition(Clump, &point);
+}
+
+void
+Gui(void)
+{
+ static bool showCameraWindow = true;
+ ImGui::Begin("Camera", &showCameraWindow);
+
+ ImGui::RadioButton("Main camera", &CameraSelected, 0);
+ ImGui::RadioButton("Sub camera", &CameraSelected, 1);
+
+ if(ImGui::RadioButton("Perspective", &ProjectionIndex, 0))
+ ProjectionCallback();
+ if(ImGui::RadioButton("Parallel", &ProjectionIndex, 1))
+ ProjectionCallback();
+
+ if(ImGui::SliderFloat("Near clip-plane", &SubCameraData.nearClipPlane, 0.1f, SubCameraData.farClipPlane-0.1f))
+ ClipPlaneCallback();
+ if(ImGui::SliderFloat("Far clip-plane", &SubCameraData.farClipPlane, SubCameraData.nearClipPlane+0.1f, 20.0f))
+ ClipPlaneCallback();
+
+ if(ImGui::Button("Reset"))
+ ResetCameraAndClump();
+
+ ImGui::End();
+}
+
+void
+MainCameraRender(rw::Camera *camera)
+{
+ RenderTextureCamera(&BackgroundColorSub, rw::Camera::CLEARIMAGE|rw::Camera::CLEARZ, World);
+
+ camera->clear(&BackgroundColor, rw::Camera::CLEARIMAGE|rw::Camera::CLEARZ);
+
+ camera->beginUpdate();
+
+ ImGui_ImplRW_NewFrame(TimeDelta);
+
+ World->render();
+
+ DrawCameraViewplaneTexture(&SubCameraData);
+ DrawCameraFrustum(&SubCameraData);
+
+ DisplayOnScreenInfo();
+
+ Gui();
+ ImGui::EndFrame();
+ ImGui::Render();
+
+ ImGui_ImplRW_RenderDrawLists(ImGui::GetDrawData());
+
+ camera->endUpdate();
+
+
+ RenderSubCamera(&BackgroundColorSub, rw::Camera::CLEARIMAGE|rw::Camera::CLEARZ, World);
+}
+
+void
+SubCameraRender(rw::Camera *camera)
+{
+ camera->clear(&BackgroundColor, rw::Camera::CLEARIMAGE|rw::Camera::CLEARZ);
+
+ camera->beginUpdate();
+
+ ImGui_ImplRW_NewFrame(TimeDelta);
+
+ World->render();
+
+ DisplayOnScreenInfo();
+
+ Gui();
+ ImGui::EndFrame();
+ ImGui::Render();
+
+ ImGui_ImplRW_RenderDrawLists(ImGui::GetDrawData());
+
+ camera->endUpdate();
+}
+
+void
+Render(void)
+{
+ rw::Camera *camera;
+
+ SubCameraMiniViewSelect(CameraSelected == 0);
+
+ switch(CameraSelected){
+ default:
+ case 0:
+ camera = MainCamera;
+ MainCameraRender(camera);
+ break;
+ case 1:
+ camera = SubCamera;
+ SubCameraRender(camera);
+ break;
+ }
+ camera->showRaster(0);
+}
+
+void
+Idle(float timeDelta)
+{
+ TimeDelta = timeDelta;
+ Render();
+}
+
+int MouseX, MouseY;
+int MouseDeltaX, MouseDeltaY;
+int MouseButtons;
+
+bool RotateClump;
+bool TranslateClump;
+bool RotateCamera;
+bool TranslateCamera;
+bool ViewXWindow;
+bool ViewYWindow;
+bool ViewXOffset;
+bool ViewYOffset;
+
+bool Ctrl, Alt, Shift;
+
+void
+KeyUp(int key)
+{
+ switch(key){
+ case sk::KEY_LCTRL:
+ case sk::KEY_RCTRL:
+ Ctrl = false;
+ break;
+ case sk::KEY_LALT:
+ case sk::KEY_RALT:
+ Alt = false;
+ break;
+ case sk::KEY_LSHIFT:
+ case sk::KEY_RSHIFT:
+ Shift = false;
+ break;
+ }
+}
+
+void
+KeyDown(int key)
+{
+ switch(key){
+ case sk::KEY_LCTRL:
+ case sk::KEY_RCTRL:
+ Ctrl = true;
+ break;
+ case sk::KEY_LALT:
+ case sk::KEY_RALT:
+ Alt = true;
+ break;
+ case sk::KEY_LSHIFT:
+ case sk::KEY_RSHIFT:
+ Shift = true;
+ break;
+ case sk::KEY_ESC:
+ sk::globals.quit = 1;
+ break;
+ }
+}
+
+void
+MouseBtn(sk::MouseState *mouse)
+{
+ MouseButtons = mouse->buttons;
+ RotateClump = !Ctrl && !Alt && !Shift && !!(MouseButtons&1);
+ TranslateClump = !Ctrl && !Alt && !Shift && !!(MouseButtons&4);
+ RotateCamera = Ctrl && !!(MouseButtons&1);
+ TranslateCamera = Ctrl && !!(MouseButtons&4);
+ ViewXWindow = Shift && !!(MouseButtons&1);
+ ViewYWindow = Shift && !!(MouseButtons&4);
+ ViewXOffset = Alt && !!(MouseButtons&1);
+ ViewYOffset = Alt && !!(MouseButtons&4);
+}
+
+void
+MouseMove(sk::MouseState *mouse)
+{
+ MouseDeltaX = mouse->posx - MouseX;
+ MouseDeltaY = mouse->posy - MouseY;
+ MouseX = mouse->posx;
+ MouseY = mouse->posy;
+
+ if(RotateClump)
+ ClumpRotate(Clump, MainCamera, MouseDeltaX, -MouseDeltaY);
+ if(TranslateClump)
+ ClumpTranslate(Clump, MainCamera, -MouseDeltaX*0.01f, -MouseDeltaY*0.1f);
+ if(RotateCamera)
+ ViewerRotate(SubCamera, -MouseDeltaX*0.1f, MouseDeltaY*0.1f);
+ if(TranslateCamera)
+ ViewerTranslate(SubCamera, -MouseDeltaX*0.01f, -MouseDeltaY*0.01f);
+ if(ViewXWindow)
+ ChangeViewWindow(-MouseDeltaY*0.01f, 0.0f);
+ if(ViewYWindow)
+ ChangeViewWindow(0.0f, -MouseDeltaY*0.01f);
+ if(ViewXOffset)
+ ChangeViewOffset(-MouseDeltaY*0.01f, 0.0f);
+ if(ViewYOffset)
+ ChangeViewOffset(0.0f, -MouseDeltaY*0.01f);
+}
+
+sk::EventStatus
+AppEventHandler(sk::Event e, void *param)
+{
+ using namespace sk;
+ Rect *r;
+ MouseState *ms;
+
+ ImGuiEventHandler(e, param);
+ ImGuiIO &io = ImGui::GetIO();
+
+ switch(e){
+ case INITIALIZE:
+ Initialize();
+ return EVENTPROCESSED;
+ case RWINITIALIZE:
+ return Initialize3D() ? EVENTPROCESSED : EVENTERROR;
+ case RWTERMINATE:
+ Terminate3D();
+ return EVENTPROCESSED;
+ case PLUGINATTACH:
+ return attachPlugins() ? EVENTPROCESSED : EVENTERROR;
+ case KEYDOWN:
+ KeyDown(*(int*)param);
+ return EVENTPROCESSED;
+ case KEYUP:
+ KeyUp(*(int*)param);
+ return EVENTPROCESSED;
+ case MOUSEBTN:
+ if(!io.WantCaptureMouse){
+ ms = (MouseState*)param;
+ MouseBtn(ms);
+ }else
+ MouseButtons = 0;
+ return EVENTPROCESSED;
+ case MOUSEMOVE:
+ MouseMove((MouseState*)param);
+ 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;
+
+ CameraSizeUpdate(r, 0.5f, 4.0f/3.0f);
+ break;
+ case IDLE:
+ Idle(*(float*)param);
+ return EVENTPROCESSED;
+ }
+ return sk::EVENTNOTPROCESSED;
+}
diff --git a/tools/camera/viewer.cpp b/tools/camera/viewer.cpp
new file mode 100644
index 0000000..b9732d3
--- /dev/null
+++ b/tools/camera/viewer.cpp
@@ -0,0 +1,51 @@
+#include <rw.h>
+#include <skeleton.h>
+
+rw::Camera*
+ViewerCreate(rw::World *world)
+{
+ rw::Camera *camera = sk::CameraCreate(sk::globals.width, sk::globals.height, 1);
+ assert(camera);
+ camera->setNearPlane(0.1f);
+ camera->setFarPlane(500.0f);
+ world->addCamera(camera);
+ return camera;
+}
+
+void
+ViewerDestroy(rw::Camera *camera, rw::World *world)
+{
+ if(camera && world){
+ world->removeCamera(camera);
+ sk::CameraDestroy(camera);
+ }
+}
+
+void
+ViewerMove(rw::Camera *camera, rw::V3d *offset)
+{
+ sk::CameraMove(camera, offset);
+}
+
+void
+ViewerRotate(rw::Camera *camera, float deltaX, float deltaY)
+{
+ sk::CameraTilt(camera, nil, deltaY);
+ sk::CameraPan(camera, nil, deltaX);
+}
+
+void
+ViewerTranslate(rw::Camera *camera, float deltaX, float deltaY)
+{
+ rw::V3d offset;
+ offset.x = deltaX;
+ offset.y = deltaY;
+ offset.z = 0.0f;
+ sk::CameraMove(camera, &offset);
+}
+
+void
+ViewerSetPosition(rw::Camera *camera, rw::V3d *position)
+{
+ camera->getFrame()->translate(position, rw::COMBINEREPLACE);
+}
diff --git a/tools/camera/viewer.h b/tools/camera/viewer.h
new file mode 100644
index 0000000..acd5d79
--- /dev/null
+++ b/tools/camera/viewer.h
@@ -0,0 +1,6 @@
+rw::Camera *ViewerCreate(rw::World *world);
+void ViewerDestroy(rw::Camera *camera, rw::World *world);
+void ViewerMove(rw::Camera *camera, rw::V3d *offset);
+void ViewerRotate(rw::Camera *camera, float deltaX, float deltaY);
+void ViewerTranslate(rw::Camera *camera, float deltaX, float deltaY);
+void ViewerSetPosition(rw::Camera *camera, rw::V3d *position);
diff --git a/tools/dumprwtree/CMakeLists.txt b/tools/dumprwtree/CMakeLists.txt
new file mode 100644
index 0000000..092b3dd
--- /dev/null
+++ b/tools/dumprwtree/CMakeLists.txt
@@ -0,0 +1,20 @@
+add_executable(dumprwtree
+ dumprwtree.cpp
+)
+
+target_link_libraries(dumprwtree
+ PRIVATE
+ librw::librw
+)
+
+if(LIBRW_GL3_GFXLIB MATCHES "SDL[23]")
+ target_compile_definitions(dumprwtree PRIVATE SDL_MAIN_HANDLED)
+endif()
+
+if(LIBRW_INSTALL)
+ install(TARGETS dumprwtree
+ RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
+ )
+endif()
+
+librw_platform_target(dumprwtree INSTALL)
diff --git a/tools/dumprwtree/dumprwtree.cpp b/tools/dumprwtree/dumprwtree.cpp
new file mode 100644
index 0000000..ca86d3c
--- /dev/null
+++ b/tools/dumprwtree/dumprwtree.cpp
@@ -0,0 +1,145 @@
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <cassert>
+
+#include <rw.h>
+
+using namespace std;
+using namespace rw;
+
+const char *chunks[] = { "None", "Struct", "String", "Extension", "Unknown",
+ "Camera", "Texture", "Material", "Material List", "Atomic Section",
+ "Plane Section", "World", "Spline", "Matrix", "Frame List",
+ "Geometry", "Clump", "Unknown", "Light", "Unicode String", "Atomic",
+ "Texture Native", "Texture Dictionary", "Animation Database",
+ "Image", "Skin Animation", "Geometry List", "Anim Animation",
+ "Team", "Crowd", "Delta Morph Animation", "Right To Render",
+ "MultiTexture Effect Native", "MultiTexture Effect Dictionary",
+ "Team Dictionary", "Platform Independet Texture Dictionary",
+ "Table of Contents", "Particle Standard Global Data", "AltPipe",
+ "Platform Independet Peds", "Patch Mesh", "Chunk Group Start",
+ "Chunk Group End", "UV Animation Dictionary", "Coll Tree"
+};
+
+/* From 0x0101 through 0x0135 */
+const char *toolkitchunks0[] = { "Metrics PLG", "Spline PLG", "Stereo PLG",
+ "VRML PLG", "Morph PLG", "PVS PLG", "Memory Leak PLG", "Animation PLG",
+ "Gloss PLG", "Logo PLG", "Memory Info PLG", "Random PLG",
+ "PNG Image PLG", "Bone PLG", "VRML Anim PLG", "Sky Mipmap Val",
+ "MRM PLG", "LOD Atomic PLG", "ME PLG", "Lightmap PLG",
+ "Refine PLG", "Skin PLG", "Label PLG", "Particles PLG", "GeomTX PLG",
+ "Synth Core PLG", "STQPP PLG",
+ "Part PP PLG", "Collision PLG", "HAnim PLG", "User Data PLG",
+ "Material Effects PLG", "Particle System PLG", "Delta Morph PLG",
+ "Patch PLG", "Team PLG", "Crowd PP PLG", "Mip Split PLG",
+ "Anisotrophy PLG", "Not used", "GCN Material PLG", "Geometric PVS PLG",
+ "XBOX Material PLG", "Multi Texture PLG", "Chain PLG", "Toon PLG",
+ "PTank PLG", "Particle Standard PLG", "PDS PLG", "PrtAdv PLG",
+ "Normal Map PLG", "ADC PLG", "UV Animation PLG"
+};
+
+/* From 0x0180 through 0x01c1 */
+const char *toolkitchunks1[] = {
+ "Character Set PLG", "NOHS World PLG", "Import Util PLG",
+ "Slerp PLG", "Optim PLG", "TL World PLG", "Database PLG",
+ "Raytrace PLG", "Ray PLG", "Library PLG",
+ "Not used", "Not used", "Not used", "Not used", "Not used", "Not used",
+ "2D PLG", "Tile Render PLG", "JPEG Image PLG", "TGA Image PLG",
+ "GIF Image PLG", "Quat PLG", "Spline PVS PLG", "Mipmap PLG",
+ "MipmapK PLG", "2D Font", "Intersection PLG", "TIFF Image PLG",
+ "Pick PLG", "BMP Image PLG", "RAS Image PLG", "Skin FX PLG",
+ "VCAT PLG", "2D Path", "2D Brush", "2D Object", "2D Shape", "2D Scene",
+ "2D Pick Region", "2D Object String", "2D Animation PLG",
+ "2D Animation",
+ "Not used", "Not used", "Not used", "Not used", "Not used", "Not used",
+ "2D Keyframe", "2D Maestro", "Barycentric",
+ "Platform Independent Texture Dictionary TK", "TOC TK", "TPL TK",
+ "AltPipe TK", "Animation TK", "Skin Split Tookit", "Compressed Key TK",
+ "Geometry Conditioning PLG", "Wing PLG", "Generic Pipeline TK",
+ "Lightmap Conversion TK", "Filesystem PLG", "Dictionary TK",
+ "UV Animation Linear", "UV Animation Parameter"
+};
+
+const char *RSchunks[] = { "Unused 1", "Unused 2", "Extra Normals",
+ "Pipeline Set", "Unused 5", "Unused 6", "Specular Material",
+ "Unused 8", "2dfx", "Extra Colors", "Collision Model",
+ "Unused 12", "Environment Material", "Breakable", "Node Name",
+ "Unused 16"
+};
+
+const char*
+getChunkName(uint32 id)
+{
+ switch(id){
+ case 0x50E:
+ return "Bin Mesh PLG";
+ case 0x510:
+ return "Native Data PLG";
+ case 0x511:
+ return "Vertex Format PLG";
+ case 0xF21E:
+ return "ZModeler Lock";
+ }
+
+ if(id <= 45)
+ return chunks[id];
+ else if(id <= 0x0253F2FF && id >= 0x0253F2F0)
+ return RSchunks[id-0x0253F2F0];
+ else if(id <= 0x0135 && id >= 0x0101)
+ return toolkitchunks0[id-0x0101];
+ else if(id <= 0x01C0 && id >= 0x0181)
+ return toolkitchunks1[id-0x0181];
+ else
+ return "Unknown";
+}
+
+void
+readchunk(StreamFile *s, ChunkHeaderInfo *h, int level)
+{
+ for(int i = 0; i < level; i++)
+ printf(" ");
+ const char *name = getChunkName(h->type);
+ printf("%s (%x bytes @ 0x%x/0x%x) - [0x%x]\n",
+ name, h->length, s->tell()-12, s->tell(), h->type);
+
+ uint32 end = s->tell() + h->length;
+ while(s->tell() < end){
+ ChunkHeaderInfo nh;
+ readChunkHeaderInfo(s, &nh);
+ if(nh.version == h->version && nh.build == h->build){
+ readchunk(s, &nh, level+1);
+ if(h->type == 0x510)
+ s->seek(end, 0);
+ }else{
+ s->seek(h->length-12);
+ break;
+ }
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ if(argc < 2){
+ fprintf(stderr, "usage: %s rwStreamFile\n", argv[0]);
+ return 0;
+ }
+ StreamFile s;
+ s.open(argv[1], "rb");
+
+ ChunkHeaderInfo header, last;
+ while(readChunkHeaderInfo(&s, &header)){
+ if(header.type == 0)
+ break;
+ last = header;
+ if(argc == 2)
+ readchunk(&s, &header, 0);
+ }
+
+ printf("%x %x %x\n", last.version, last.build,
+ libraryIDPack(last.version, last.build));
+
+ s.close();
+ return 0;
+}
diff --git a/tools/hopalong/main.cpp b/tools/hopalong/main.cpp
new file mode 100644
index 0000000..7399ff1
--- /dev/null
+++ b/tools/hopalong/main.cpp
@@ -0,0 +1,469 @@
+#include <rw.h>
+#include <skeleton.h>
+#include <assert.h>
+
+rw::V3d zero = { 0.0f, 0.0f, 0.0f };
+struct SceneGlobals {
+ rw::World *world;
+ rw::Camera *camera;
+} Scene;
+rw::EngineOpenParams engineOpenParams;
+
+rw::Texture *ParticleTex;
+rw::Raster *ParticleRaster;
+
+static rw::RWDEVICE::Im2DVertex im2dVerts[1024];
+static int numImVerts;
+static rw::uint16 imIndices[1024*2];
+static int numImIndices;
+static rw::PrimitiveType imPrim;
+
+void
+BeginIm2D(rw::PrimitiveType prim)
+{
+ numImVerts = 0;
+ numImIndices = 0;
+ imPrim = prim;
+}
+
+void
+EndIm2D(void)
+{
+ rw::im2d::RenderIndexedPrimitive(imPrim, &im2dVerts, numImVerts, &imIndices, numImIndices);
+}
+
+void
+RenderParticle(float x, float y, float sz, rw::RGBA col)
+{
+ using namespace rw;
+ using namespace RWDEVICE;
+
+ if(numImVerts+4 > nelem(im2dVerts) ||
+ numImIndices+6 > nelem(imIndices)){
+ EndIm2D();
+ numImVerts = 0;
+ numImIndices = 0;
+ }
+
+ float recipZ = 1.0f/Scene.camera->nearPlane;
+ rw::RWDEVICE::Im2DVertex *verts = &im2dVerts[numImVerts];
+
+ x += sk::globals.width/2;
+ y += sk::globals.height/2;
+
+ verts[0].setScreenX(x-sz);
+ verts[0].setScreenY(y-sz);
+ verts[0].setScreenZ(rw::im2d::GetNearZ());
+ verts[0].setCameraZ(Scene.camera->nearPlane);
+ verts[0].setRecipCameraZ(recipZ);
+ verts[0].setColor(col.red, col.green, col.blue, col.alpha);
+ verts[0].setU(0.0f, recipZ);
+ verts[0].setV(0.0f, recipZ);
+
+ verts[1].setScreenX(x+sz);
+ verts[1].setScreenY(y-sz);
+ verts[1].setScreenZ(rw::im2d::GetNearZ());
+ verts[1].setCameraZ(Scene.camera->nearPlane);
+ verts[1].setRecipCameraZ(recipZ);
+ verts[1].setColor(col.red, col.green, col.blue, col.alpha);
+ verts[1].setU(1.0f, recipZ);
+ verts[1].setV(0.0f, recipZ);
+
+ verts[2].setScreenX(x+sz);
+ verts[2].setScreenY(y+sz);
+ verts[2].setScreenZ(rw::im2d::GetNearZ());
+ verts[2].setCameraZ(Scene.camera->nearPlane);
+ verts[2].setRecipCameraZ(recipZ);
+ verts[2].setColor(col.red, col.green, col.blue, col.alpha);
+ verts[2].setU(1.0f, recipZ);
+ verts[2].setV(1.0f, recipZ);
+
+ verts[3].setScreenX(x-sz);
+ verts[3].setScreenY(y+sz);
+ verts[3].setScreenZ(rw::im2d::GetNearZ());
+ verts[3].setCameraZ(Scene.camera->nearPlane);
+ verts[3].setRecipCameraZ(recipZ);
+ verts[3].setColor(col.red, col.green, col.blue, col.alpha);
+ verts[3].setU(0.0f, recipZ);
+ verts[3].setV(1.0f, recipZ);
+
+ imIndices[numImIndices+0] = numImVerts+0;
+ imIndices[numImIndices+1] = numImVerts+1;
+ imIndices[numImIndices+2] = numImVerts+2;
+ imIndices[numImIndices+3] = numImVerts+0;
+ imIndices[numImIndices+4] = numImVerts+2;
+ imIndices[numImIndices+5] = numImVerts+3;
+
+ numImVerts += 4;
+ numImIndices += 6;
+}
+
+float sgn(float f) { return f < 0.0f ? -1.0f : f > 0.0f ? 1.0f : 0.0f; }
+
+struct Orbit
+{
+ ImVec4 color;
+ float StartX;
+ float StartY;
+ float PointSz;
+ int numIterations;
+ bool used;
+};
+
+Orbit orbits[100];
+int curOrbit = 0;
+
+float A = 0.0f;
+float B = 0.0f;
+float C = 0.0f;
+float D = 0.0f;
+float E = 0.0f;
+float Exp = 0.25f;
+float Scale = 50.0f;
+int type;
+bool textured = true;
+bool animateX;
+bool animateY;
+bool animateA;
+bool animateB;
+bool animateC;
+
+void
+ResetOrbit(Orbit *o)
+{
+ o->color = ImVec4(1.0f, 0.0f, 0.0f, 1.00f);
+ o->StartX = 1.0f;
+ o->StartY = 0.0f;
+ o->PointSz = 3.0f;
+ o->numIterations = 1000;
+ o->used = false;
+}
+
+void
+RenderOrbit(Orbit *o)
+{
+ rw::RGBA red = { 0xFF, 0, 0, 0xFF };
+ rw::RGBA color = rw::makeRGBA(o->color.x*255, o->color.y*255, o->color.z*255, o->color.w*255);
+
+ float x1, y1;
+ float x0 = o->StartX;
+ float y0 = o->StartY;
+ int n = o->numIterations;
+
+ if(textured){
+ rw::SetRenderStatePtr(rw::TEXTURERASTER, ParticleRaster);
+ rw::SetRenderState(rw::TEXTUREFILTER, rw::Texture::LINEAR);
+ }else
+ rw::SetRenderStatePtr(rw::TEXTURERASTER, nil);
+ BeginIm2D(rw::PRIMTYPETRILIST);
+ while(n--){
+ switch(type){
+ case 0:
+ default:
+ x1 = y0 - sgn(x0)*(D + sqrtf(fabs(B*x0 - C)));
+ break;
+ case 1:
+ x1 = y0 - sgn(x0)*(D + powf(fabs(B*x0 - C), Exp));
+ break;
+ case 2:
+ x1 = y0 - sgn(x0)*(D + logf(2.0f + sqrtf(fabs(B*x0 - C))));
+ break;
+ }
+ y1 = A - x0;
+ x1 += E;
+ RenderParticle(x1*Scale, y1*Scale, o->PointSz, color);
+ x0 = x1;
+ y0 = y1;
+ }
+ EndIm2D();
+}
+
+void
+RenderHopalong(void)
+{
+ int i;
+ for(i = 0; i < nelem(orbits); i++)
+ if(orbits[i].used)
+ RenderOrbit(&orbits[i]);
+}
+
+void
+Init(void)
+{
+ sk::globals.windowtitle = "Hopalong map";
+ sk::globals.width = 1280;
+ sk::globals.height = 800;
+ 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;
+}
+
+#include "particle.inc"
+
+#include "vfs.h"
+VFS_file files[] = {
+ { "particle.png", particle_png, particle_png_len }
+};
+VFS vfs = {
+ files, nelem(files)
+};
+
+bool
+InitRW(void)
+{
+ if(!sk::InitRW())
+ return false;
+ installVFS(&vfs);
+
+ Scene.world = rw::World::create();
+
+ ParticleTex = rw::Texture::read("particle", nil);
+ if(ParticleTex)
+ ParticleRaster = ParticleTex->raster;
+
+ rw::Light *ambient = rw::Light::create(rw::Light::AMBIENT);
+ ambient->setColor(0.2f, 0.2f, 0.2f);
+ 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);
+
+ Scene.camera = sk::CameraCreate(sk::globals.width, sk::globals.height, 1);
+ Scene.world->addCamera(Scene.camera);
+
+
+ for(int i = 0; i < nelem(orbits); i++)
+ ResetOrbit(&orbits[i]);
+ orbits[0].used = true;
+
+ ImGui_ImplRW_Init();
+ ImGui::StyleColorsClassic();
+
+ return true;
+}
+
+void
+orbitGUI(Orbit *o)
+{
+ ImGui::SliderFloat("x0", &o->StartX, -10.0f, 10.0f);
+ ImGui::SliderFloat("y0", &o->StartY, -10.0f, 10.0f);
+ ImGui::SliderFloat("Point Size", &o->PointSz, 0.5f, 10.0f);
+ ImGui::SliderInt("NumPoints", &o->numIterations, 1, 10000);
+ ImGui::ColorEdit3("color", (float*)&o->color);
+
+/*
+ ImGui::Checkbox("Textured", &textured);
+ ImGui::Checkbox("AnimateX", &animateX);
+ ImGui::Checkbox("AnimateY", &animateY);
+ ImGui::Checkbox("AnimateA", &animateA);
+ ImGui::Checkbox("AnimateB", &animateB);
+ ImGui::Checkbox("AnimateC", &animateC);
+*/
+}
+
+void
+hopalongGUI(void)
+{
+ int i;
+ static char tmp[128];
+ if(curOrbit >= 0)
+ orbitGUI(&orbits[curOrbit]);
+ ImGui::SliderFloat("A", &A, -10.0f, 10.0f);
+ ImGui::SliderFloat("B", &B, -10.0f, 10.0f);
+ ImGui::SliderFloat("C", &C, -10.0f, 10.0f);
+ ImGui::SliderFloat("D", &D, -10.0f, 10.0f);
+ ImGui::SliderFloat("E", &E, -10.0f, 10.0f);
+ ImGui::SliderFloat("exp", &Exp, 0.001f, 4.0f);
+ ImGui::RadioButton("sqrt", &type, 0);
+ ImGui::RadioButton("^exp", &type, 1);
+ ImGui::RadioButton("log", &type, 2);
+ ImGui::SliderFloat("Scale", &Scale, 1.0f, 100.0f);
+ for(i = 0; i < nelem(orbits); i++){
+ if(orbits[i].used){
+ sprintf(tmp, "Orbit %d\n", i);
+ if(ImGui::Button(tmp))
+ curOrbit = i;
+ }
+ }
+ if( ImGui::Button("Add Orbit"))
+ for(i = 0; i < nelem(orbits); i++)
+ if(!orbits[i].used){
+ ResetOrbit(&orbits[i]);
+ orbits[i].used = true;
+ curOrbit = i;
+ break;
+ }
+ if(curOrbit >= 0 && ImGui::Button("Remove Orbit")){
+ orbits[curOrbit].used = false;
+ curOrbit = -1;
+ for(i = 0; i < nelem(orbits); i++)
+ if(orbits[i].used){
+ curOrbit = i;
+ break;
+ }
+ }
+}
+
+#ifdef __unix__
+#include <unistd.h>
+#endif
+
+void
+Draw(float timeDelta)
+{
+ static bool show_demo_window = false;
+
+ rw::RGBA clearcol = { 0, 0, 0, 255 };
+ Scene.camera->clear(&clearcol, rw::Camera::CLEARIMAGE|rw::Camera::CLEARZ);
+ Scene.camera->beginUpdate();
+
+ ImGui_ImplRW_NewFrame(timeDelta);
+
+ RenderHopalong();
+
+/*
+ if(animateX) StartX += timeDelta/10000.0f;
+ if(animateY) StartY += timeDelta/10000.0f;
+ if(animateA) A += timeDelta/10000.0f;
+ if(animateB) B += timeDelta/10000.0f;
+ if(animateC) C += timeDelta/10000.0f;
+*/
+
+ hopalongGUI();
+
+ if(show_demo_window){
+ ImGui::SetNextWindowPos(ImVec2(650, 20), ImGuiCond_FirstUseEver);
+ ImGui::ShowDemoWindow(&show_demo_window);
+ }
+
+
+ ImGui::EndFrame();
+ ImGui::Render();
+
+ ImGui_ImplRW_RenderDrawLists(ImGui::GetDrawData());
+
+ Scene.camera->endUpdate();
+ Scene.camera->showRaster(0);
+
+#ifdef __unix__
+ usleep(10000);
+#endif
+}
+
+
+void
+KeyUp(int key)
+{
+}
+
+void
+KeyDown(int key)
+{
+ switch(key){
+ case sk::KEY_ESC:
+ sk::globals.quit = 1;
+ break;
+ }
+}
+
+int MouseX, MouseY;
+int MouseDeltaX, MouseDeltaY;
+int MouseButtons;
+
+void
+MouseBtn(sk::MouseState *mouse)
+{
+ MouseButtons = mouse->buttons;
+}
+
+void
+MouseMove(sk::MouseState *mouse)
+{
+ MouseDeltaX = mouse->posx - MouseX;
+ MouseDeltaY = mouse->posy - MouseY;
+ MouseX = mouse->posx;
+ MouseY = mouse->posy;
+ if(MouseButtons&1){
+ int mx = MouseX - sk::globals.width/2;
+ int my = MouseY - sk::globals.height/2;
+ if(curOrbit >= 0){
+ orbits[curOrbit].StartX = mx/Scale;
+ orbits[curOrbit].StartY = my/Scale;
+ }
+ }
+}
+
+sk::EventStatus
+AppEventHandler(sk::Event e, void *param)
+{
+ using namespace sk;
+ Rect *r;
+ MouseState *ms;
+
+ ImGuiEventHandler(e, param);
+ ImGuiIO &io = ImGui::GetIO();
+
+ 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:
+ if(!io.WantCaptureMouse){
+ ms = (MouseState*)param;
+ MouseBtn(ms);
+ }else
+ MouseButtons = 0;
+ return EVENTPROCESSED;
+ case MOUSEMOVE:
+ MouseMove((MouseState*)param);
+ 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;
+ // TODO: set aspect ratio
+ if(Scene.camera)
+ sk::CameraSize(Scene.camera, r);
+ break;
+ case IDLE:
+ Draw(*(float*)param);
+ return EVENTPROCESSED;
+ }
+ return sk::EVENTNOTPROCESSED;
+}
diff --git a/tools/hopalong/particle.inc b/tools/hopalong/particle.inc
new file mode 100644
index 0000000..5eeabe0
--- /dev/null
+++ b/tools/hopalong/particle.inc
@@ -0,0 +1,467 @@
+unsigned char particle_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
+ 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x08, 0x03, 0x00, 0x00, 0x00, 0x6b, 0xac, 0x58, 0x54, 0x00, 0x00, 0x02,
+ 0xfd, 0x50, 0x4c, 0x54, 0x45, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x52, 0x92, 0xc4, 0x9b, 0x00, 0x00, 0x00, 0xfe, 0x74, 0x52,
+ 0x4e, 0x53, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
+ 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
+ 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22,
+ 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e,
+ 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a,
+ 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
+ 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52,
+ 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e,
+ 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a,
+ 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76,
+ 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82,
+ 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
+ 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a,
+ 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6,
+ 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2,
+ 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe,
+ 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca,
+ 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6,
+ 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2,
+ 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee,
+ 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa,
+ 0xfb, 0xfc, 0xfd, 0xfe, 0x86, 0xe3, 0x51, 0x9e, 0x00, 0x00, 0x11, 0x70,
+ 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0xec, 0x96, 0x81, 0x6a, 0xeb, 0x30,
+ 0x0c, 0x00, 0xad, 0xfe, 0xff, 0x37, 0xd7, 0x0f, 0x26, 0x38, 0xb4, 0x1e,
+ 0x26, 0xb3, 0xeb, 0xd0, 0xb4, 0xaf, 0x8e, 0x23, 0x6b, 0x2b, 0x60, 0xee,
+ 0xa2, 0x58, 0x89, 0xf6, 0x9f, 0x8f, 0xaf, 0x80, 0xaf, 0x80, 0xaf, 0x80,
+ 0x6b, 0x6c, 0xdc, 0x3f, 0x54, 0xc0, 0xc2, 0x76, 0xfd, 0x33, 0x05, 0x84,
+ 0x73, 0xa3, 0x2b, 0xff, 0x10, 0x01, 0x51, 0x56, 0x32, 0xe3, 0x2b, 0xeb,
+ 0x9f, 0x20, 0x20, 0xb4, 0x90, 0x88, 0x9f, 0x44, 0xcb, 0x9b, 0x0a, 0x88,
+ 0x41, 0xd4, 0xae, 0x42, 0x56, 0x7c, 0x27, 0x01, 0xa6, 0x0f, 0x92, 0xe1,
+ 0x9e, 0x62, 0xef, 0x25, 0x21, 0xbe, 0x8f, 0x80, 0x10, 0x7c, 0x91, 0x30,
+ 0x68, 0x83, 0x82, 0xe7, 0x26, 0xbc, 0x83, 0x00, 0x81, 0xdb, 0x81, 0xcf,
+ 0xc4, 0xce, 0xaa, 0x5b, 0x22, 0x2e, 0x2e, 0x00, 0x7c, 0xe8, 0x99, 0xae,
+ 0x82, 0x4c, 0xa0, 0x82, 0xb1, 0x6b, 0xe6, 0x92, 0xeb, 0x95, 0x05, 0x0c,
+ 0xb0, 0x65, 0x20, 0xea, 0xe6, 0x50, 0x75, 0xf1, 0x0f, 0x54, 0x5c, 0x52,
+ 0x80, 0xca, 0xbe, 0x06, 0x4c, 0xa0, 0xa7, 0x6e, 0x0d, 0x15, 0x88, 0xf0,
+ 0xe7, 0x42, 0xc0, 0xd0, 0x25, 0x05, 0x40, 0x57, 0xc1, 0x49, 0x55, 0x07,
+ 0x75, 0xb8, 0xf6, 0xa1, 0xaf, 0xe9, 0x19, 0x65, 0x10, 0xe7, 0xe0, 0xfb,
+ 0x92, 0x02, 0x57, 0x80, 0xf0, 0x7d, 0xed, 0x57, 0x10, 0x3b, 0xf1, 0xa1,
+ 0x84, 0x98, 0x04, 0x7e, 0x1b, 0x78, 0xe0, 0xc7, 0x00, 0xd0, 0x24, 0xb8,
+ 0xd9, 0xa8, 0x20, 0x76, 0xe3, 0x17, 0x5e, 0x56, 0xc2, 0x1f, 0x05, 0x80,
+ 0x4d, 0xa8, 0x22, 0xac, 0xe0, 0xb5, 0x02, 0x54, 0xf9, 0x20, 0x93, 0x57,
+ 0x09, 0x2d, 0x83, 0xba, 0x00, 0xf4, 0xa0, 0xd6, 0xc9, 0x7f, 0xfc, 0x2e,
+ 0xbc, 0x54, 0x80, 0xf0, 0x2b, 0x39, 0x37, 0x0a, 0xb8, 0x28, 0x01, 0x7d,
+ 0x00, 0x70, 0x01, 0xce, 0x8d, 0x87, 0xad, 0x0a, 0x62, 0x2b, 0x3e, 0xe0,
+ 0x0a, 0xb3, 0x02, 0xa0, 0x26, 0x20, 0xc2, 0x0a, 0x5e, 0x28, 0x40, 0xa5,
+ 0x0f, 0x35, 0xab, 0x05, 0xd4, 0x77, 0x00, 0x7e, 0x70, 0x24, 0xe0, 0x61,
+ 0xf5, 0xab, 0xf0, 0x32, 0x01, 0xb0, 0x04, 0x13, 0x60, 0x46, 0xe1, 0x9f,
+ 0x10, 0x50, 0x0c, 0xd4, 0xd1, 0x58, 0xb0, 0x90, 0xf3, 0x35, 0x02, 0x6a,
+ 0xed, 0x8b, 0xbb, 0xf0, 0xaf, 0x0b, 0xc8, 0xa0, 0x81, 0x9c, 0x0d, 0x45,
+ 0x10, 0x67, 0xe1, 0xdf, 0xc0, 0xcf, 0x38, 0x2d, 0x20, 0x6f, 0x14, 0xdc,
+ 0x4f, 0x52, 0x10, 0xcf, 0xf1, 0x43, 0x6f, 0x76, 0xbf, 0x05, 0x2d, 0x0e,
+ 0x05, 0x80, 0xef, 0xfa, 0xc7, 0x82, 0x1d, 0x3c, 0x63, 0x20, 0xb6, 0x3c,
+ 0xfe, 0x4a, 0x6f, 0x03, 0x14, 0x80, 0x3f, 0x04, 0x46, 0x9f, 0x01, 0xd0,
+ 0x89, 0xbf, 0x3a, 0xd8, 0x51, 0x04, 0xf1, 0x0c, 0x3f, 0x15, 0x0e, 0xf3,
+ 0x2d, 0xb3, 0x0c, 0x16, 0xc0, 0x35, 0x12, 0xe0, 0x26, 0x50, 0xf1, 0x33,
+ 0xdc, 0xf9, 0xb3, 0xe1, 0xc0, 0x06, 0xce, 0x14, 0x90, 0x08, 0x70, 0x81,
+ 0x9e, 0xf8, 0x56, 0xd0, 0x42, 0x02, 0x30, 0x50, 0xf9, 0x25, 0xa0, 0x37,
+ 0xe1, 0xa3, 0x80, 0x4a, 0x48, 0x53, 0xa9, 0xef, 0x7c, 0x01, 0x7e, 0xfc,
+ 0x95, 0xbf, 0x18, 0x20, 0xc2, 0x3f, 0x27, 0x00, 0x03, 0xe0, 0x57, 0xfe,
+ 0x6a, 0xe0, 0xc9, 0x22, 0x88, 0x75, 0xfe, 0x68, 0x82, 0xc7, 0x00, 0xbf,
+ 0x34, 0x2c, 0xe4, 0xfc, 0x9b, 0x80, 0x9c, 0x39, 0x1a, 0xa8, 0xf0, 0x4b,
+ 0x42, 0xeb, 0xcb, 0x06, 0x62, 0x85, 0xdf, 0x4f, 0xff, 0x26, 0x03, 0x8c,
+ 0x83, 0x3e, 0x78, 0xd0, 0x05, 0x19, 0xe6, 0xbf, 0xbb, 0x0a, 0x30, 0x70,
+ 0x9a, 0x00, 0x9f, 0xfd, 0x89, 0x2d, 0x03, 0x19, 0x76, 0x08, 0x00, 0x5f,
+ 0xfc, 0x99, 0xba, 0x1f, 0x9c, 0x28, 0x00, 0xfe, 0x14, 0x70, 0x43, 0xc0,
+ 0x6f, 0x03, 0xdc, 0xe2, 0xff, 0x93, 0x80, 0x62, 0x40, 0x1d, 0xb0, 0xf2,
+ 0x23, 0xe0, 0x9e, 0x02, 0x96, 0x0c, 0xc4, 0x12, 0x3f, 0x83, 0x87, 0x2f,
+ 0x03, 0x19, 0xe0, 0x1f, 0x08, 0x68, 0xd1, 0x7a, 0x1b, 0x08, 0xc8, 0x00,
+ 0xbe, 0xf9, 0x29, 0x03, 0x06, 0x06, 0xce, 0x11, 0x30, 0xaa, 0xfe, 0xc2,
+ 0xaf, 0x46, 0x00, 0xff, 0x8a, 0x80, 0x8c, 0x6a, 0x01, 0xc5, 0xc0, 0xe8,
+ 0x3d, 0xd8, 0x2f, 0xc0, 0xc7, 0x1f, 0xf8, 0x19, 0x6d, 0x60, 0xf4, 0x35,
+ 0x8c, 0x83, 0x32, 0xa0, 0x1f, 0x7e, 0x07, 0x9b, 0x3f, 0x03, 0x0a, 0x16,
+ 0x8f, 0xc2, 0x98, 0xe5, 0xe7, 0xed, 0x07, 0x3f, 0xe7, 0x81, 0x01, 0xf5,
+ 0x41, 0x0b, 0x70, 0x17, 0x1c, 0xf2, 0x63, 0xa0, 0x28, 0xe0, 0x24, 0x98,
+ 0x33, 0x10, 0x2b, 0xfc, 0xe0, 0x3f, 0x18, 0x18, 0x36, 0x02, 0x7d, 0x0d,
+ 0x6b, 0x67, 0x7d, 0x06, 0x54, 0x03, 0x6a, 0x01, 0x0f, 0xfc, 0x28, 0x58,
+ 0x30, 0x10, 0x93, 0xfc, 0xd1, 0xc0, 0xff, 0xa1, 0x47, 0x41, 0x06, 0x19,
+ 0x80, 0x7f, 0x5a, 0x00, 0x06, 0xc4, 0x9f, 0xc8, 0xe0, 0xff, 0x04, 0x14,
+ 0xb4, 0x3e, 0x67, 0x20, 0x66, 0xf8, 0x19, 0xbf, 0xe9, 0xf5, 0x1a, 0x60,
+ 0x20, 0xe3, 0xb8, 0x0d, 0x30, 0x46, 0x4d, 0x20, 0xc9, 0xe1, 0x57, 0xf9,
+ 0x3f, 0x38, 0x60, 0x60, 0x60, 0x9f, 0x00, 0xf8, 0xeb, 0xe3, 0x2f, 0x1a,
+ 0x64, 0x40, 0xc7, 0xe0, 0x8c, 0x00, 0x1f, 0x81, 0xe2, 0x2f, 0xe8, 0xb5,
+ 0x08, 0x30, 0xb0, 0x53, 0x80, 0xf8, 0x13, 0xb9, 0x2a, 0x38, 0x68, 0x04,
+ 0x6a, 0x03, 0x6c, 0x0d, 0xbf, 0x9a, 0xc0, 0x41, 0x0b, 0x28, 0xf8, 0xb9,
+ 0xac, 0x19, 0x88, 0x55, 0x7e, 0xe0, 0xd3, 0x86, 0x0d, 0x1c, 0x9f, 0x82,
+ 0x33, 0x67, 0xa0, 0xf8, 0xa9, 0x7e, 0xe2, 0x9a, 0x81, 0x58, 0xe0, 0x47,
+ 0x80, 0x14, 0xfc, 0x63, 0xdd, 0x8e, 0x76, 0xe4, 0xb6, 0x62, 0x18, 0x0c,
+ 0xe3, 0xcc, 0xbc, 0xff, 0x23, 0x4f, 0x50, 0x60, 0x4a, 0x44, 0x10, 0x3e,
+ 0x2f, 0xed, 0x62, 0xeb, 0x38, 0xf6, 0xf6, 0x52, 0x7f, 0x25, 0x92, 0x3a,
+ 0xde, 0x68, 0x04, 0xaa, 0xa0, 0x33, 0xe0, 0x26, 0x60, 0x0c, 0xd4, 0x02,
+ 0x28, 0x3f, 0x54, 0x20, 0xf0, 0x5b, 0x00, 0xa5, 0x7e, 0x67, 0xe0, 0x27,
+ 0x19, 0x54, 0x05, 0xf9, 0x36, 0xa8, 0x06, 0xfe, 0x24, 0x81, 0x4c, 0xc0,
+ 0x6f, 0x08, 0x9c, 0x87, 0xf5, 0x8f, 0xfd, 0x6d, 0x00, 0x78, 0xc1, 0x7d,
+ 0x16, 0x8a, 0x08, 0xf0, 0x65, 0x28, 0x08, 0xee, 0x52, 0x10, 0xfa, 0xbf,
+ 0x01, 0x8c, 0x1d, 0x76, 0x02, 0x00, 0xe8, 0xf5, 0x27, 0xfe, 0x58, 0xfb,
+ 0x42, 0xd0, 0x09, 0x44, 0x05, 0x6d, 0x81, 0xd5, 0x00, 0xd1, 0xc0, 0x5e,
+ 0xff, 0x2a, 0x5f, 0x0a, 0x89, 0x44, 0x85, 0xc0, 0x63, 0x00, 0x73, 0xf8,
+ 0x67, 0xfd, 0xca, 0x00, 0x32, 0x98, 0x87, 0x2a, 0xe8, 0x3a, 0xbc, 0x25,
+ 0x80, 0x14, 0x84, 0x04, 0x22, 0x00, 0x12, 0xc8, 0x51, 0xe1, 0x2f, 0x01,
+ 0xa4, 0xfe, 0xc9, 0xbe, 0xef, 0x2b, 0x04, 0x74, 0x40, 0x15, 0x81, 0x92,
+ 0x03, 0x8a, 0x04, 0xd0, 0x01, 0x57, 0xe5, 0x7f, 0x26, 0x19, 0x87, 0xc0,
+ 0x73, 0x00, 0x5d, 0x00, 0x5f, 0x27, 0xf2, 0xb7, 0x00, 0x20, 0x03, 0xc1,
+ 0x74, 0x5e, 0x8e, 0x40, 0xdd, 0x86, 0xd6, 0x04, 0x28, 0x01, 0xb1, 0x80,
+ 0x10, 0x98, 0x16, 0x10, 0x40, 0xa4, 0x30, 0x04, 0xee, 0x87, 0xe0, 0xdc,
+ 0xd6, 0xbf, 0xe4, 0x7f, 0x01, 0x78, 0x8f, 0x08, 0x60, 0x85, 0x73, 0x3b,
+ 0x03, 0xdd, 0x06, 0x4d, 0x01, 0x73, 0x63, 0x81, 0xf9, 0xe1, 0x33, 0x00,
+ 0xb6, 0x19, 0x74, 0x02, 0x00, 0xe8, 0xf5, 0x9f, 0x94, 0xbd, 0x5e, 0xa9,
+ 0x5d, 0x2b, 0x44, 0x07, 0x4b, 0x12, 0x2a, 0x39, 0x68, 0x14, 0x50, 0x0b,
+ 0xcc, 0x2b, 0x95, 0xe7, 0x45, 0x24, 0xea, 0x04, 0x3a, 0x00, 0xf5, 0x3f,
+ 0x95, 0x33, 0x08, 0xc7, 0x9d, 0x20, 0xb7, 0x33, 0xd0, 0x5d, 0xc0, 0x09,
+ 0x48, 0xfd, 0xee, 0x00, 0x36, 0x7f, 0x9e, 0x7a, 0x41, 0x07, 0xd0, 0x1b,
+ 0x20, 0x00, 0xfe, 0xea, 0xdf, 0x9b, 0xfa, 0xcd, 0x43, 0x1a, 0x21, 0xdb,
+ 0x40, 0x92, 0xc0, 0xa4, 0x00, 0x37, 0x01, 0x4c, 0xd0, 0x04, 0xb4, 0x08,
+ 0xe4, 0x11, 0x25, 0x0c, 0x80, 0xda, 0x02, 0x02, 0xa0, 0xfe, 0xdd, 0xff,
+ 0x6f, 0x01, 0x6c, 0x04, 0xcf, 0x67, 0x00, 0x0d, 0x78, 0x3e, 0x01, 0xbb,
+ 0x7c, 0x01, 0x7c, 0xf6, 0x14, 0x74, 0x02, 0x1d, 0x40, 0xae, 0x94, 0x2f,
+ 0x80, 0x37, 0x2a, 0x50, 0x67, 0xe0, 0xb9, 0x06, 0xdc, 0x4f, 0x40, 0x5e,
+ 0x29, 0x1a, 0x00, 0x41, 0x90, 0xab, 0x03, 0xa8, 0x02, 0xb0, 0xfd, 0x2f,
+ 0x00, 0xa6, 0x7e, 0x47, 0xa0, 0xce, 0x40, 0xee, 0xa6, 0x01, 0xb9, 0xcb,
+ 0x04, 0x38, 0x02, 0x43, 0x20, 0x7f, 0xb7, 0x1b, 0x56, 0x19, 0x38, 0x7d,
+ 0x00, 0xec, 0xff, 0xbc, 0x8a, 0x10, 0xd2, 0x01, 0xaf, 0x16, 0x05, 0x6b,
+ 0x10, 0xfc, 0x43, 0x07, 0xdc, 0x09, 0xe0, 0xc7, 0x29, 0x28, 0x43, 0x50,
+ 0x00, 0x60, 0x80, 0xef, 0xb9, 0x9b, 0x10, 0x0e, 0x81, 0x40, 0x70, 0x04,
+ 0x4a, 0x0e, 0xa0, 0x01, 0x58, 0x84, 0x55, 0x00, 0xda, 0x3f, 0x37, 0x66,
+ 0x78, 0x0f, 0x80, 0x01, 0x58, 0x06, 0xf8, 0xde, 0xb7, 0x00, 0xcc, 0xc3,
+ 0xaa, 0x60, 0x1f, 0x01, 0x35, 0xd0, 0x3d, 0xa8, 0x0b, 0xe0, 0xdc, 0xcb,
+ 0x0c, 0xcb, 0x10, 0x9c, 0x87, 0x03, 0xf0, 0xb6, 0x05, 0x10, 0x42, 0x09,
+ 0x14, 0x15, 0xd4, 0x06, 0x9b, 0x06, 0x52, 0x3f, 0x02, 0x48, 0x03, 0x7c,
+ 0x9e, 0x0d, 0xc1, 0xa9, 0x03, 0xc0, 0xff, 0x7f, 0x01, 0xbc, 0x9f, 0x47,
+ 0x01, 0x17, 0xe2, 0x5c, 0x2e, 0xc3, 0xcf, 0x43, 0xc0, 0x47, 0x00, 0xf4,
+ 0x40, 0x1b, 0x82, 0x53, 0x1b, 0x60, 0x04, 0x70, 0xca, 0x57, 0x08, 0x7a,
+ 0x14, 0x08, 0xc7, 0xf5, 0x81, 0x90, 0x11, 0x40, 0x02, 0xf2, 0x7b, 0x11,
+ 0x2d, 0x04, 0x94, 0xf1, 0xcf, 0x63, 0x7a, 0x80, 0x16, 0xe8, 0x00, 0xd6,
+ 0x0a, 0xbc, 0xcb, 0x2f, 0x00, 0x20, 0x40, 0x07, 0xe0, 0x83, 0xb6, 0x40,
+ 0x6e, 0x3a, 0x60, 0xd5, 0x5f, 0x01, 0xac, 0xc7, 0x1a, 0x82, 0x0a, 0x40,
+ 0x05, 0xdc, 0x02, 0x68, 0x0b, 0x0c, 0x00, 0x09, 0x28, 0x02, 0xaf, 0xcc,
+ 0x14, 0xeb, 0xa0, 0xcb, 0x60, 0x3a, 0x36, 0xad, 0xeb, 0x51, 0xd8, 0xad,
+ 0xfe, 0xe5, 0xb1, 0x85, 0x50, 0x1d, 0x14, 0x80, 0x03, 0xb0, 0xea, 0x97,
+ 0x81, 0x00, 0xba, 0x08, 0xd8, 0x01, 0xe4, 0x80, 0xdc, 0x4d, 0x02, 0x3a,
+ 0x80, 0x3c, 0x16, 0x81, 0x36, 0x04, 0xe7, 0x56, 0x01, 0x53, 0xb6, 0x0c,
+ 0xb0, 0x02, 0x0f, 0xc7, 0xae, 0x93, 0x40, 0x89, 0xc2, 0xd7, 0x29, 0xc0,
+ 0x83, 0x30, 0x0d, 0xc0, 0xea, 0x83, 0x04, 0x1d, 0x04, 0x40, 0x57, 0x40,
+ 0x8a, 0x57, 0x09, 0xf3, 0x1c, 0x02, 0x24, 0x01, 0x82, 0x00, 0x36, 0x48,
+ 0x0c, 0xa0, 0x01, 0x76, 0xfd, 0x18, 0x40, 0x6e, 0x20, 0x14, 0x1d, 0x2c,
+ 0x00, 0xe6, 0x0c, 0x28, 0x45, 0xcb, 0x40, 0x00, 0x35, 0x0e, 0x97, 0x28,
+ 0x58, 0x82, 0x60, 0x89, 0xc1, 0x02, 0xa0, 0xfa, 0xfc, 0xe7, 0x9c, 0x0f,
+ 0x15, 0x00, 0x34, 0x80, 0x02, 0xb0, 0x28, 0xe4, 0x1e, 0x00, 0x10, 0xe0,
+ 0x70, 0x38, 0x5c, 0xab, 0x0b, 0x8c, 0x09, 0x72, 0x1c, 0x4c, 0xfd, 0x03,
+ 0x20, 0x37, 0xb5, 0x23, 0x03, 0x57, 0x2d, 0x70, 0x6a, 0x03, 0x5c, 0xd7,
+ 0xdf, 0xcc, 0xd0, 0x16, 0x30, 0x0c, 0x8f, 0x08, 0x28, 0x01, 0x06, 0x61,
+ 0x1b, 0xa0, 0x18, 0xe0, 0x35, 0x81, 0xd6, 0x02, 0xe7, 0xb6, 0x01, 0xfe,
+ 0xd6, 0x5c, 0xbd, 0xa0, 0xb4, 0x00, 0x33, 0x50, 0x92, 0x20, 0x13, 0x50,
+ 0x1a, 0xa0, 0xea, 0xff, 0xfc, 0x60, 0x0b, 0x08, 0xa0, 0x37, 0xc0, 0xaa,
+ 0x1f, 0x06, 0xbd, 0x05, 0xcc, 0x82, 0x6e, 0x43, 0xee, 0x42, 0xe6, 0xc0,
+ 0xdb, 0x06, 0xc8, 0x03, 0x02, 0xb7, 0x2d, 0x70, 0x7a, 0x03, 0x4c, 0xe9,
+ 0x45, 0x08, 0x24, 0xc0, 0x0c, 0x70, 0x2a, 0xa8, 0x08, 0x7a, 0x22, 0xe8,
+ 0x04, 0x50, 0x7f, 0x19, 0xff, 0x79, 0xd4, 0x16, 0x38, 0x37, 0x0d, 0x00,
+ 0x81, 0xd5, 0x00, 0xbd, 0x05, 0xf0, 0x81, 0x10, 0x28, 0x23, 0x90, 0xfa,
+ 0xf1, 0x80, 0xda, 0x00, 0xbb, 0x05, 0xa8, 0x9f, 0x16, 0xe8, 0x00, 0x76,
+ 0x03, 0xa4, 0x60, 0x06, 0xa1, 0x0c, 0x01, 0x22, 0xa0, 0x06, 0x98, 0x03,
+ 0x9a, 0x0b, 0x0a, 0xa0, 0x0f, 0xc0, 0xbc, 0xf2, 0xde, 0x2d, 0x20, 0x80,
+ 0xde, 0x00, 0x9b, 0x40, 0x71, 0x82, 0x3c, 0xcf, 0xab, 0x02, 0x30, 0x0a,
+ 0x12, 0x04, 0x1b, 0x80, 0x3c, 0x89, 0x00, 0x36, 0xff, 0xbc, 0x7b, 0x0b,
+ 0x1c, 0x1a, 0x00, 0x05, 0x5c, 0xc5, 0x17, 0x19, 0xf8, 0xbe, 0xe3, 0x1d,
+ 0x45, 0x05, 0xab, 0x06, 0x74, 0x0d, 0x4c, 0x21, 0x29, 0x1f, 0x01, 0xb0,
+ 0xf9, 0x73, 0x0f, 0x01, 0x5b, 0xe0, 0xb8, 0x06, 0xbe, 0xce, 0x5f, 0x0b,
+ 0x84, 0x40, 0x49, 0x03, 0xb9, 0x04, 0x90, 0x24, 0x14, 0x05, 0x20, 0x09,
+ 0x91, 0x83, 0xf2, 0x48, 0x0e, 0x02, 0x40, 0xae, 0x92, 0x00, 0xa8, 0xff,
+ 0x5f, 0x00, 0x5f, 0x4d, 0x65, 0x29, 0x04, 0x40, 0x1a, 0x60, 0x00, 0xd8,
+ 0x06, 0x79, 0x15, 0x02, 0x27, 0xeb, 0xc0, 0x02, 0x10, 0x02, 0x02, 0x70,
+ 0x15, 0x58, 0x00, 0xe6, 0x73, 0x40, 0xa9, 0x5f, 0xf9, 0xcb, 0x1d, 0x00,
+ 0xd3, 0x02, 0x02, 0xd0, 0x03, 0xf3, 0x7b, 0x00, 0xa9, 0xbb, 0x0c, 0x02,
+ 0x04, 0x3a, 0x00, 0xa2, 0x60, 0xd6, 0x61, 0x82, 0x60, 0x07, 0x40, 0xfd,
+ 0xa5, 0xf9, 0xf3, 0xca, 0xef, 0x0d, 0xe0, 0x84, 0x01, 0x80, 0x04, 0xbe,
+ 0xce, 0x12, 0x00, 0x21, 0x48, 0x20, 0x0f, 0x36, 0xe2, 0x0a, 0x20, 0x2e,
+ 0xd0, 0x01, 0x78, 0x16, 0x62, 0x08, 0x2c, 0xff, 0xf3, 0x73, 0xe7, 0x43,
+ 0x09, 0x2d, 0x00, 0x80, 0x59, 0x83, 0x57, 0xe9, 0x95, 0x00, 0xc7, 0x02,
+ 0x24, 0x81, 0x0d, 0xe0, 0xc7, 0x24, 0xb8, 0x01, 0x90, 0x02, 0x38, 0x08,
+ 0xe8, 0xf5, 0xaf, 0x57, 0xd6, 0x62, 0x01, 0x28, 0x81, 0x73, 0x10, 0x5e,
+ 0x09, 0x10, 0x06, 0x54, 0xc1, 0xff, 0x0b, 0x80, 0x1a, 0x48, 0x04, 0xe8,
+ 0xf5, 0xcf, 0x21, 0x39, 0x32, 0x78, 0xae, 0x25, 0x70, 0x03, 0x78, 0x42,
+ 0x20, 0x0f, 0x44, 0x00, 0x1b, 0x70, 0x1d, 0x9c, 0x18, 0x80, 0x09, 0x20,
+ 0x01, 0x8c, 0x40, 0xad, 0x7f, 0x01, 0x50, 0x06, 0x01, 0x70, 0x22, 0x81,
+ 0xe7, 0x5b, 0x54, 0x27, 0xc0, 0x5e, 0x18, 0x06, 0x84, 0x41, 0x82, 0x40,
+ 0xd1, 0x00, 0x62, 0x00, 0x31, 0x90, 0x2d, 0x20, 0x8f, 0x52, 0x7f, 0x7a,
+ 0xe1, 0xab, 0x83, 0xa6, 0xc1, 0x43, 0x03, 0xa0, 0x00, 0x0c, 0xc2, 0x78,
+ 0x61, 0xda, 0xa0, 0xce, 0x80, 0x61, 0xb8, 0xb8, 0x80, 0x41, 0xf8, 0x66,
+ 0x02, 0x42, 0x20, 0x4d, 0x60, 0xf3, 0xa3, 0x02, 0x21, 0xbc, 0x01, 0x38,
+ 0x01, 0x01, 0x40, 0x0b, 0x28, 0x83, 0x38, 0x21, 0x3e, 0x00, 0x80, 0x92,
+ 0x03, 0x00, 0x80, 0x07, 0xe0, 0x81, 0x48, 0x20, 0x7f, 0x03, 0xc0, 0x19,
+ 0x08, 0x00, 0x26, 0x20, 0x21, 0x68, 0x00, 0xa8, 0x87, 0x12, 0x18, 0x23,
+ 0x38, 0xaf, 0x6d, 0x03, 0x1c, 0x0a, 0x25, 0x0a, 0x6f, 0x00, 0x89, 0xc2,
+ 0x21, 0xe0, 0x61, 0x48, 0xb2, 0xcc, 0x58, 0x80, 0xf5, 0xab, 0x7c, 0xf3,
+ 0x48, 0x18, 0x72, 0x06, 0xce, 0xb5, 0x07, 0xa4, 0x4e, 0x1e, 0x4d, 0x06,
+ 0x54, 0x41, 0x6d, 0x60, 0x54, 0xd0, 0x5d, 0x60, 0x34, 0x10, 0x13, 0x50,
+ 0x03, 0xef, 0x05, 0x00, 0x00, 0x1f, 0x7d, 0x00, 0x00, 0x07, 0x0f, 0xb4,
+ 0x05, 0x5c, 0x0a, 0x88, 0x02, 0x97, 0x49, 0x20, 0x97, 0xdb, 0x90, 0xbb,
+ 0x50, 0x2e, 0x53, 0x00, 0x21, 0xc0, 0x15, 0xc0, 0x06, 0xc8, 0xd3, 0x19,
+ 0x10, 0xc0, 0x9a, 0x00, 0x00, 0x30, 0x04, 0x8a, 0x00, 0x22, 0xc8, 0x08,
+ 0xf8, 0x61, 0xc0, 0xcf, 0x02, 0x8c, 0x40, 0x6e, 0x1b, 0xa0, 0x0c, 0x00,
+ 0x00, 0x66, 0x06, 0x04, 0x30, 0x12, 0xf0, 0xda, 0x13, 0xb0, 0x9f, 0xb6,
+ 0x80, 0x4e, 0x38, 0x17, 0x61, 0xb8, 0x1c, 0x09, 0x79, 0x20, 0x44, 0x10,
+ 0xde, 0x97, 0x5b, 0xd0, 0xd5, 0xf4, 0xaf, 0xe7, 0xa4, 0xc1, 0x25, 0x02,
+ 0x07, 0x13, 0xa4, 0x01, 0x16, 0x80, 0xda, 0x02, 0x79, 0x74, 0x1b, 0xe8,
+ 0x23, 0xd0, 0x4d, 0x20, 0xc5, 0xf7, 0x06, 0xd8, 0x00, 0x6c, 0x81, 0xcc,
+ 0x80, 0x00, 0x66, 0x0f, 0x4a, 0x89, 0x02, 0xe8, 0x2a, 0xe0, 0x46, 0x88,
+ 0x0d, 0x70, 0x26, 0xb6, 0x5a, 0x20, 0xb7, 0x26, 0xe0, 0x26, 0xd8, 0x14,
+ 0x00, 0x00, 0x79, 0x65, 0x23, 0x12, 0x00, 0x26, 0x48, 0xfd, 0x02, 0xd0,
+ 0x08, 0x52, 0xbf, 0x2a, 0x68, 0x07, 0x78, 0x24, 0x86, 0x04, 0x14, 0x0d,
+ 0xcc, 0x53, 0x0b, 0x00, 0x00, 0x04, 0x34, 0xc2, 0x0d, 0x20, 0x26, 0xf8,
+ 0xad, 0x8a, 0xfa, 0x01, 0x50, 0x64, 0xb0, 0xf8, 0x60, 0x08, 0x20, 0x82,
+ 0xa9, 0xbf, 0xb9, 0x60, 0x91, 0x40, 0x00, 0x40, 0x20, 0x61, 0x31, 0x47,
+ 0x4c, 0x02, 0xd0, 0x04, 0x53, 0x6c, 0xde, 0x1d, 0x40, 0x4f, 0x02, 0xb9,
+ 0x1f, 0x75, 0x00, 0x41, 0xb8, 0xa6, 0x80, 0x0e, 0x20, 0xaf, 0xbc, 0x31,
+ 0xc2, 0x01, 0xa0, 0x04, 0xa4, 0x5e, 0xea, 0x6f, 0x00, 0xf0, 0x01, 0x46,
+ 0xe0, 0x95, 0x20, 0x54, 0x35, 0xe0, 0xfb, 0xf8, 0xc3, 0x08, 0xe0, 0x01,
+ 0x05, 0x00, 0x04, 0xf2, 0x83, 0x22, 0x00, 0x80, 0x0c, 0x2f, 0xe5, 0x33,
+ 0x06, 0xb9, 0xd7, 0x4a, 0x74, 0xbb, 0x0f, 0xe1, 0x83, 0xae, 0xc3, 0x61,
+ 0x70, 0xbf, 0x09, 0xa5, 0x74, 0x32, 0xe0, 0x22, 0x00, 0x82, 0x8b, 0x24,
+ 0x70, 0x4c, 0x01, 0x23, 0x01, 0x9b, 0x43, 0xd1, 0x41, 0xbf, 0x11, 0x1d,
+ 0x5b, 0xc0, 0x24, 0xd4, 0x76, 0x21, 0x77, 0x61, 0xbf, 0x07, 0x15, 0x05,
+ 0xdc, 0x95, 0x8f, 0x08, 0x90, 0x04, 0x8e, 0x29, 0x00, 0x09, 0x6c, 0x53,
+ 0x30, 0x7f, 0x3c, 0x13, 0x98, 0x33, 0x11, 0xb3, 0x30, 0xcb, 0x50, 0xfe,
+ 0xe4, 0xe2, 0x34, 0xc4, 0xb3, 0x80, 0xf9, 0x53, 0xfa, 0x1f, 0x19, 0x24,
+ 0x09, 0x1c, 0x35, 0xf0, 0xbd, 0x47, 0xc0, 0x2e, 0x98, 0xbf, 0x00, 0xc8,
+ 0x63, 0x00, 0xf8, 0x5b, 0x12, 0xaa, 0xe0, 0xd2, 0x40, 0x7f, 0x37, 0x62,
+ 0x01, 0x48, 0x0b, 0x08, 0x00, 0x02, 0xeb, 0x87, 0xfc, 0x98, 0x30, 0x58,
+ 0x00, 0xbc, 0xce, 0x7b, 0x3a, 0x80, 0x2e, 0xa0, 0xfe, 0x66, 0x84, 0xfa,
+ 0xa0, 0x59, 0xb8, 0x26, 0x61, 0x5c, 0xb0, 0x9b, 0xe0, 0xfc, 0xcd, 0x0b,
+ 0x06, 0xf3, 0x1b, 0x33, 0x97, 0x00, 0x30, 0x41, 0x39, 0xe4, 0x6d, 0x1e,
+ 0x26, 0x0d, 0x13, 0x86, 0x89, 0x82, 0x00, 0x20, 0x08, 0x2e, 0x09, 0xb0,
+ 0x01, 0x4c, 0xc1, 0xca, 0x9f, 0x33, 0x80, 0x0a, 0x1e, 0x35, 0x30, 0xd5,
+ 0x59, 0xbc, 0x4e, 0xe0, 0x46, 0xa4, 0x0a, 0x0a, 0xa0, 0x88, 0xa0, 0x00,
+ 0xd4, 0x40, 0xf7, 0x20, 0x1d, 0x40, 0x08, 0x61, 0x84, 0x0a, 0x9e, 0x9d,
+ 0x03, 0xbf, 0x17, 0xa5, 0xd3, 0x04, 0x6d, 0x06, 0x0c, 0xc3, 0xbf, 0x07,
+ 0x40, 0x10, 0xee, 0x13, 0x90, 0x37, 0x18, 0xe2, 0x83, 0x2b, 0x0b, 0x1e,
+ 0x4d, 0x60, 0x6a, 0x2d, 0x3a, 0xd8, 0x8c, 0xd0, 0x7d, 0x28, 0xf7, 0x7f,
+ 0x01, 0x90, 0x9b, 0x18, 0xd8, 0x4d, 0x50, 0x05, 0xdc, 0x20, 0xb4, 0x81,
+ 0xa3, 0x09, 0xd8, 0x01, 0xca, 0xe0, 0xd4, 0xdf, 0x17, 0x42, 0xfe, 0x1d,
+ 0x2d, 0xeb, 0x20, 0xcb, 0xa0, 0xff, 0x5a, 0xb6, 0xaf, 0x82, 0xb9, 0x8d,
+ 0xc0, 0x76, 0x00, 0x36, 0x00, 0x80, 0xd7, 0x41, 0x02, 0x40, 0x61, 0x16,
+ 0x82, 0x80, 0x0b, 0x71, 0x08, 0x3c, 0x74, 0x81, 0xd4, 0xef, 0x32, 0x6c,
+ 0xfd, 0xa4, 0x20, 0xca, 0x46, 0x04, 0xb6, 0x0d, 0x1c, 0x83, 0xb0, 0x55,
+ 0x17, 0x27, 0xbc, 0x55, 0x41, 0x92, 0x60, 0x07, 0x40, 0x12, 0xbc, 0xd3,
+ 0xc0, 0xea, 0x81, 0xb2, 0x20, 0x0c, 0x0b, 0x80, 0xb2, 0x89, 0x45, 0x79,
+ 0x60, 0x84, 0x8c, 0x40, 0xcd, 0xc2, 0x49, 0x42, 0x7c, 0x1a, 0x34, 0x09,
+ 0x2b, 0x01, 0x98, 0xa0, 0x1d, 0x00, 0x07, 0x7c, 0x10, 0x00, 0xaf, 0xc3,
+ 0x2a, 0x08, 0x86, 0x22, 0x02, 0x20, 0xa8, 0x67, 0x42, 0x17, 0x51, 0xb8,
+ 0x9e, 0x07, 0x51, 0xbe, 0x12, 0xa0, 0x07, 0xd8, 0x06, 0xe3, 0x83, 0x1b,
+ 0xc0, 0xc4, 0x80, 0x77, 0x46, 0xa0, 0x0a, 0xa1, 0x1d, 0xc0, 0xd9, 0xf8,
+ 0x71, 0x1f, 0xdc, 0xfb, 0xf0, 0x10, 0x48, 0xfd, 0xd7, 0xdb, 0x70, 0xee,
+ 0xfd, 0x4d, 0xa4, 0x74, 0x40, 0x11, 0xc0, 0x8c, 0xc0, 0x67, 0x07, 0x81,
+ 0x43, 0x0c, 0x98, 0x8a, 0x25, 0xd1, 0x64, 0x50, 0x23, 0x4c, 0xfd, 0xbb,
+ 0x01, 0xfa, 0x2e, 0xe0, 0xa1, 0x78, 0x6e, 0x4d, 0xb0, 0x4a, 0xe0, 0xd4,
+ 0xed, 0x08, 0xec, 0x20, 0x10, 0x00, 0xf9, 0x28, 0xc4, 0x61, 0x80, 0x10,
+ 0xfa, 0x0c, 0xf8, 0x7d, 0x4c, 0x17, 0xa8, 0x39, 0x40, 0x17, 0xf0, 0xab,
+ 0x58, 0x9f, 0x00, 0x8b, 0xe7, 0x48, 0x20, 0x9f, 0x87, 0x16, 0x80, 0xef,
+ 0x65, 0x03, 0x6c, 0x12, 0x25, 0x0a, 0x05, 0x01, 0x1a, 0xf0, 0xfb, 0x0e,
+ 0x50, 0x03, 0x52, 0x7e, 0x8f, 0x41, 0xb9, 0x4c, 0x85, 0x93, 0x84, 0x02,
+ 0xa0, 0xe4, 0xa0, 0xe2, 0x03, 0x35, 0x0c, 0x33, 0x03, 0xee, 0xc3, 0x76,
+ 0x80, 0xdb, 0x30, 0x13, 0xd0, 0x83, 0x70, 0xf1, 0x80, 0x92, 0x84, 0x0a,
+ 0x80, 0xa6, 0x02, 0x10, 0x70, 0x1f, 0xf4, 0xdb, 0xc8, 0xce, 0xc2, 0xb9,
+ 0x67, 0x19, 0x4c, 0xf9, 0xed, 0xab, 0x88, 0xbb, 0x20, 0x31, 0xa0, 0x14,
+ 0xff, 0x08, 0xc0, 0x5b, 0x02, 0x3d, 0x0a, 0xf5, 0x28, 0x68, 0x12, 0xba,
+ 0x0f, 0x42, 0xe6, 0xa0, 0x1e, 0x04, 0x7b, 0x0c, 0xa2, 0xfe, 0x0f, 0x00,
+ 0x48, 0xc2, 0x0d, 0xc1, 0x22, 0x90, 0x1b, 0x17, 0x28, 0x9f, 0x07, 0xef,
+ 0x45, 0xb0, 0x7c, 0x18, 0xd4, 0x05, 0x72, 0xaf, 0xfa, 0x5b, 0xf9, 0x66,
+ 0xe1, 0xe3, 0x79, 0x90, 0xea, 0x27, 0x82, 0x7b, 0x0d, 0xd0, 0x05, 0x0c,
+ 0x42, 0x93, 0x04, 0x09, 0x42, 0xba, 0xc0, 0xbd, 0x06, 0x58, 0xbe, 0x5a,
+ 0xc8, 0x99, 0x90, 0x00, 0x2c, 0xf6, 0x19, 0x80, 0xfd, 0x79, 0x2c, 0x3e,
+ 0x58, 0x34, 0x00, 0x17, 0x68, 0x1a, 0xf0, 0x7d, 0xae, 0xcf, 0x62, 0x0d,
+ 0x40, 0x71, 0x81, 0x0e, 0xe0, 0x7b, 0x75, 0x07, 0xf0, 0x58, 0x0c, 0x1f,
+ 0xcc, 0xf3, 0x08, 0x80, 0x73, 0x71, 0x4e, 0xc5, 0xcb, 0xa1, 0xb8, 0xbb,
+ 0x50, 0x10, 0x70, 0x1c, 0x56, 0x9c, 0x60, 0x7d, 0x22, 0xfe, 0x87, 0xbd,
+ 0x3b, 0xba, 0x81, 0x30, 0x84, 0x61, 0x18, 0xbc, 0xff, 0xd6, 0x4c, 0x60,
+ 0x21, 0xf5, 0x35, 0xdf, 0x0a, 0x48, 0x77, 0x3f, 0xb4, 0x89, 0xfd, 0x3f,
+ 0x80, 0x1e, 0x0d, 0xe5, 0x73, 0x30, 0x06, 0xe3, 0x87, 0x03, 0xa8, 0xa1,
+ 0x78, 0x3c, 0x06, 0x63, 0x10, 0x14, 0x07, 0xe0, 0x00, 0xfc, 0x04, 0xfc,
+ 0x09, 0xfa, 0x0c, 0xba, 0x08, 0xb9, 0x0a, 0x7b, 0x0c, 0x79, 0x0e, 0x1b,
+ 0x88, 0x18, 0x89, 0x19, 0x8a, 0x1a, 0x8b, 0x8f, 0x2e, 0x46, 0xac, 0xc6,
+ 0x2c, 0x47, 0xad, 0xc7, 0x05, 0x24, 0x44, 0x64, 0x84, 0xa4, 0xc4, 0xe4,
+ 0x04, 0x25, 0x45, 0x65, 0x85, 0xa5, 0xc5, 0xe5, 0x15, 0x26, 0x54, 0x66,
+ 0x94, 0xa6, 0xd4, 0xe6, 0x14, 0x27, 0x55, 0x67, 0x95, 0xa7, 0xf7, 0xea,
+ 0xf3, 0x00, 0x0a, 0x10, 0x1a, 0x20, 0x2a, 0x30, 0x3a, 0x40, 0x4a, 0x50,
+ 0x5a, 0x60, 0x6a, 0x70, 0x7a, 0x80, 0x8a, 0x90, 0x9a, 0xa0, 0xaa, 0xb0,
+ 0xba, 0xc0, 0xca, 0xd0, 0xda, 0xe0, 0xea, 0xf0, 0xfa, 0x04, 0x0b, 0x14,
+ 0x1b, 0x24, 0x2b, 0x34, 0x3b, 0x44, 0x4b, 0xd3, 0xaa, 0x2d, 0xb2, 0x35,
+ 0xba, 0x3d, 0xc2, 0x45, 0xca, 0x4d, 0xd2, 0x55, 0xda, 0x5d, 0xe2, 0x65,
+ 0xea, 0x6d, 0xf2, 0x75, 0xfa, 0xfd, 0xd7, 0x9e, 0x1d, 0xe8, 0x26, 0xb2,
+ 0x03, 0x41, 0x14, 0xf5, 0xfc, 0xff, 0x47, 0xb3, 0x92, 0x66, 0x1b, 0x2a,
+ 0x3a, 0x56, 0x7b, 0x14, 0x6b, 0x04, 0x48, 0xd9, 0x85, 0x09, 0x49, 0xde,
+ 0x5b, 0x74, 0x2f, 0x76, 0x55, 0x1b, 0xc6, 0x31, 0x62, 0x13, 0xc4, 0x12,
+ 0x98, 0x17, 0x40, 0x76, 0x20, 0x11, 0x40, 0x09, 0x34, 0x35, 0x68, 0x0d,
+ 0x18, 0x02, 0xd9, 0x84, 0xd3, 0x2a, 0x78, 0x69, 0xc8, 0x0d, 0x70, 0x49,
+ 0x00, 0x39, 0x98, 0x41, 0xb8, 0xcc, 0x3f, 0xf9, 0x1d, 0x84, 0x3d, 0x0b,
+ 0x0d, 0x4f, 0x43, 0x0e, 0xc3, 0x1a, 0x58, 0x27, 0x61, 0x04, 0x60, 0x26,
+ 0xa0, 0xa0, 0x8d, 0x81, 0x5c, 0x03, 0xab, 0xed, 0x4f, 0x04, 0xb8, 0x02,
+ 0x1c, 0x04, 0x97, 0xa3, 0xe0, 0x8b, 0xdf, 0x10, 0x58, 0x04, 0x41, 0xbe,
+ 0xfe, 0xf2, 0x23, 0x60, 0xbd, 0x09, 0x8a, 0xdc, 0xed, 0xdf, 0x8f, 0xc1,
+ 0x7e, 0x28, 0xd2, 0xcd, 0x01, 0x7e, 0x38, 0xd2, 0x8f, 0xc3, 0x04, 0x41,
+ 0x7d, 0x61, 0x03, 0x5c, 0x12, 0xe0, 0x26, 0x38, 0x31, 0x6a, 0x0d, 0xb0,
+ 0xfd, 0xc1, 0x6f, 0x22, 0xd0, 0x16, 0x74, 0x0b, 0xb8, 0x04, 0x8c, 0x41,
+ 0x14, 0x10, 0x04, 0xf5, 0xfa, 0x9f, 0xea, 0x9a, 0x0d, 0x80, 0x00, 0x0c,
+ 0x64, 0x15, 0x24, 0xbc, 0x01, 0xf8, 0xe2, 0xb7, 0x04, 0xcd, 0xc0, 0xf9,
+ 0x28, 0x4c, 0x0a, 0x5a, 0x84, 0x2f, 0x03, 0x06, 0x61, 0x4a, 0xc8, 0x02,
+ 0x90, 0xff, 0xb2, 0x80, 0x91, 0x31, 0xa0, 0x80, 0x7e, 0x08, 0x9a, 0x67,
+ 0x20, 0xfc, 0x61, 0xa0, 0x49, 0xc1, 0x7e, 0x18, 0x52, 0x40, 0x06, 0xc0,
+ 0x58, 0x0b, 0x58, 0xc6, 0xc0, 0x09, 0x57, 0xec, 0x0a, 0xe8, 0x87, 0x00,
+ 0xdf, 0x11, 0xb4, 0x06, 0x7d, 0x57, 0x70, 0x3d, 0x0a, 0x20, 0xa0, 0x2e,
+ 0xa7, 0x24, 0x03, 0xe0, 0xba, 0x00, 0x63, 0xa0, 0x76, 0x41, 0x1f, 0x80,
+ 0xee, 0x00, 0xb7, 0x40, 0x9f, 0x01, 0x6e, 0x01, 0xf7, 0x40, 0x1f, 0x84,
+ 0x59, 0x80, 0x7d, 0x00, 0xac, 0x05, 0x50, 0x86, 0x6e, 0xff, 0x4c, 0x80,
+ 0x7e, 0x07, 0xac, 0x33, 0xe0, 0xfa, 0x1e, 0xc8, 0x14, 0x20, 0x08, 0x28,
+ 0xc0, 0xdf, 0x09, 0xd0, 0x40, 0xcd, 0x84, 0xb9, 0x16, 0x0e, 0x12, 0xe0,
+ 0xbc, 0x84, 0x02, 0x0c, 0x34, 0x73, 0x00, 0xfc, 0x81, 0x7f, 0xf2, 0x93,
+ 0x02, 0x8f, 0x7c, 0xdd, 0x6b, 0xfe, 0x83, 0x7f, 0x25, 0x60, 0x6d, 0x20,
+ 0xdb, 0xd0, 0x3f, 0x47, 0xdd, 0xf3, 0x43, 0xe1, 0xa6, 0x04, 0x3c, 0x0d,
+ 0xf6, 0x35, 0x10, 0x1f, 0x13, 0x17, 0xfe, 0x34, 0x08, 0xb3, 0xff, 0xe4,
+ 0xbf, 0x2e, 0xc0, 0x18, 0x38, 0x0a, 0x0d, 0x01, 0x9e, 0x02, 0x32, 0x04,
+ 0xeb, 0x3a, 0xda, 0x41, 0xd0, 0x51, 0xb0, 0x2c, 0xc4, 0x91, 0x70, 0x7a,
+ 0x22, 0x50, 0x40, 0x69, 0x7a, 0x64, 0x00, 0xec, 0x08, 0x28, 0x03, 0x91,
+ 0x84, 0x73, 0x7c, 0xf9, 0x0b, 0xbf, 0x1b, 0x04, 0x47, 0x3b, 0x0a, 0x96,
+ 0x02, 0x0d, 0xcc, 0x14, 0x64, 0xfe, 0x15, 0xff, 0x5a, 0xc0, 0xd2, 0x80,
+ 0x5d, 0x60, 0xfe, 0xf5, 0xe7, 0xa0, 0x30, 0xb0, 0x6e, 0x81, 0xe4, 0xef,
+ 0x4f, 0x44, 0x26, 0xa1, 0xf9, 0x2f, 0xff, 0x45, 0x01, 0xc4, 0xc0, 0xf8,
+ 0x4f, 0x54, 0xd4, 0x04, 0x40, 0xc7, 0xef, 0x51, 0xe8, 0x18, 0x29, 0xe0,
+ 0xbc, 0x78, 0x1c, 0x6a, 0x0c, 0x10, 0x03, 0xe5, 0xe2, 0xff, 0x7f, 0x3d,
+ 0x2a, 0x00, 0x36, 0x04, 0x10, 0x84, 0x59, 0x06, 0x4e, 0x40, 0x69, 0xa0,
+ 0x89, 0x00, 0x16, 0x40, 0x2c, 0x81, 0x3e, 0x04, 0x92, 0xdf, 0x79, 0x28,
+ 0xe3, 0x9f, 0x00, 0xec, 0xe8, 0xb6, 0x0c, 0x1c, 0xd3, 0x0a, 0x24, 0x02,
+ 0xc2, 0xc0, 0xb2, 0x06, 0x93, 0x9f, 0x10, 0xb0, 0x0a, 0xeb, 0xda, 0xf3,
+ 0x6f, 0x09, 0xd0, 0xc0, 0x21, 0xbe, 0x11, 0x98, 0xf8, 0xfd, 0x1c, 0xd4,
+ 0x4e, 0x42, 0xa5, 0xc0, 0x18, 0x54, 0xc1, 0x43, 0xfe, 0x3d, 0x01, 0x8d,
+ 0x81, 0xcc, 0xbf, 0x9e, 0xdf, 0x93, 0x40, 0x23, 0x80, 0xd3, 0xc0, 0xca,
+ 0x40, 0x26, 0xa1, 0xfc, 0xdb, 0x02, 0x1a, 0x03, 0x87, 0x67, 0x80, 0x86,
+ 0xdf, 0x08, 0x60, 0x0e, 0x30, 0x04, 0x1a, 0x03, 0x9e, 0x09, 0x1e, 0x3d,
+ 0xff, 0xbe, 0x80, 0x34, 0xf0, 0x14, 0x70, 0xb0, 0x03, 0x8c, 0x40, 0x33,
+ 0xb0, 0x11, 0x60, 0x0a, 0x1a, 0x83, 0xec, 0x81, 0xc7, 0x53, 0x40, 0xf2,
+ 0x8f, 0x7d, 0x01, 0x1a, 0xa8, 0x3a, 0x2c, 0x54, 0x02, 0x90, 0x08, 0x34,
+ 0x03, 0x8d, 0x00, 0x43, 0x20, 0xf8, 0x8d, 0xc1, 0x0c, 0xc2, 0xa0, 0xaf,
+ 0xfa, 0x83, 0x7f, 0x43, 0x80, 0x06, 0x4e, 0x94, 0x52, 0x10, 0xf8, 0x56,
+ 0xc0, 0xf5, 0x0c, 0xbc, 0x96, 0x82, 0x14, 0x41, 0x28, 0x28, 0xfc, 0xf3,
+ 0x7f, 0x83, 0x7f, 0x57, 0x80, 0x06, 0x42, 0x41, 0xf0, 0xaf, 0xa7, 0x20,
+ 0xcf, 0x42, 0x9e, 0x86, 0xfa, 0x59, 0xc8, 0x20, 0x0c, 0x7c, 0xf9, 0xf7,
+ 0x05, 0x68, 0x60, 0x54, 0x0e, 0x84, 0x82, 0x65, 0x05, 0x58, 0x02, 0x0a,
+ 0xa0, 0x06, 0x96, 0x45, 0xf0, 0xc2, 0x8f, 0xd3, 0xaf, 0xfc, 0xfb, 0x02,
+ 0x3c, 0x17, 0xfc, 0x54, 0x20, 0xbf, 0x63, 0xa0, 0x19, 0x98, 0x6f, 0x8b,
+ 0x4f, 0x53, 0x90, 0x61, 0x50, 0x03, 0x3f, 0xf1, 0x9d, 0xff, 0xf7, 0x05,
+ 0x68, 0xa0, 0x04, 0x44, 0x23, 0x96, 0x81, 0x52, 0xe2, 0x18, 0xf8, 0x3b,
+ 0x01, 0x0c, 0x83, 0x85, 0x5b, 0xfc, 0xd1, 0x7d, 0x25, 0x00, 0xfe, 0x4d,
+ 0x01, 0x1a, 0x20, 0x09, 0xe4, 0x77, 0x0c, 0x54, 0x00, 0xc7, 0x61, 0x05,
+ 0x38, 0x0c, 0x6a, 0x80, 0xdd, 0x2f, 0xff, 0xbe, 0x00, 0x0d, 0xb8, 0x0f,
+ 0xa6, 0x15, 0xa8, 0x81, 0x4b, 0x02, 0xe4, 0x9f, 0x56, 0xa1, 0xab, 0xbf,
+ 0xf8, 0xef, 0x11, 0x60, 0x14, 0xe2, 0x80, 0x08, 0xdc, 0x10, 0xd0, 0xc6,
+ 0x20, 0xf4, 0xc6, 0xdf, 0x4d, 0x02, 0xca, 0x40, 0x11, 0x95, 0x00, 0xf8,
+ 0xeb, 0x37, 0x63, 0xde, 0x82, 0x53, 0x01, 0xf3, 0x1e, 0x1c, 0x85, 0x89,
+ 0x81, 0x12, 0x50, 0xbe, 0x8a, 0xff, 0x0e, 0x01, 0x1a, 0x70, 0x15, 0x50,
+ 0x01, 0xc5, 0x5e, 0x97, 0x71, 0x4d, 0x40, 0xf6, 0x60, 0x59, 0xa0, 0x08,
+ 0x7c, 0xf5, 0xe5, 0xbf, 0x4b, 0x40, 0x15, 0x79, 0xae, 0x82, 0x02, 0xb7,
+ 0x02, 0xb2, 0x04, 0xae, 0x0b, 0x48, 0x7e, 0x8a, 0xe0, 0x07, 0x7f, 0xc9,
+ 0x1a, 0x6e, 0xff, 0xbb, 0x04, 0xb8, 0x08, 0x46, 0xf1, 0xae, 0x23, 0xd0,
+ 0xf7, 0x44, 0x7d, 0x57, 0x74, 0x19, 0x83, 0xf5, 0xed, 0xf0, 0xe5, 0xbf,
+ 0x53, 0x80, 0x06, 0xd8, 0x08, 0x36, 0xa0, 0x2d, 0xb8, 0x10, 0x60, 0x0f,
+ 0xda, 0x85, 0x2e, 0x7e, 0xf9, 0xef, 0x14, 0xe0, 0x22, 0x18, 0x38, 0xd0,
+ 0xc0, 0x98, 0x8f, 0x01, 0xf3, 0x41, 0x60, 0xc6, 0x0f, 0xfd, 0xf0, 0xe5,
+ 0xbf, 0x57, 0x80, 0x06, 0x70, 0x80, 0x85, 0xb1, 0x6a, 0xc1, 0x65, 0x0f,
+ 0x0e, 0xd8, 0xa1, 0x97, 0x7f, 0x5f, 0xc0, 0xbe, 0x02, 0x23, 0xf0, 0xba,
+ 0x00, 0xa7, 0xe1, 0x25, 0xfe, 0x1b, 0x04, 0x14, 0x48, 0xd1, 0x9d, 0xac,
+ 0x9a, 0x18, 0x1b, 0x02, 0x86, 0xdc, 0x85, 0x1e, 0x93, 0x8f, 0xfc, 0xb7,
+ 0x0b, 0x70, 0x11, 0x84, 0x82, 0x51, 0xe4, 0x94, 0xc0, 0x65, 0x01, 0xc9,
+ 0x9f, 0x5f, 0x03, 0x9f, 0x97, 0xff, 0x2d, 0x02, 0x0a, 0xa7, 0x14, 0x14,
+ 0x2d, 0x17, 0xc7, 0x00, 0x3f, 0x19, 0x9a, 0xf5, 0x20, 0x97, 0xc0, 0x2f,
+ 0x65, 0xef, 0x14, 0xa0, 0x82, 0x17, 0x6e, 0xde, 0x7f, 0x27, 0x60, 0x3c,
+ 0xa1, 0xe3, 0x5b, 0xf0, 0xdf, 0x2a, 0x40, 0x05, 0xe3, 0x48, 0x0f, 0x79,
+ 0x9b, 0x8f, 0x01, 0x0e, 0x02, 0x85, 0x98, 0xb7, 0xf8, 0x09, 0xf8, 0xef,
+ 0x17, 0x30, 0x8e, 0x91, 0x0a, 0x0a, 0x35, 0xb9, 0x8d, 0x00, 0x3e, 0x1b,
+ 0x24, 0x04, 0xf2, 0x52, 0x5f, 0x13, 0x1f, 0xfe, 0x37, 0x09, 0x50, 0xc1,
+ 0x93, 0x96, 0x07, 0x17, 0x05, 0x3c, 0x0d, 0xf0, 0x00, 0xfc, 0xb7, 0x0b,
+ 0x50, 0xc1, 0x38, 0x9a, 0xbf, 0xf2, 0x6b, 0x20, 0xa3, 0xc0, 0xbf, 0xe0,
+ 0x7f, 0x86, 0x00, 0x15, 0x04, 0x35, 0xf8, 0x3c, 0x6f, 0x39, 0x40, 0x41,
+ 0x90, 0x83, 0xff, 0x19, 0x02, 0x54, 0x50, 0x1a, 0xe2, 0x92, 0x5e, 0x88,
+ 0x40, 0x46, 0x81, 0x22, 0x2d, 0xf0, 0xbc, 0x80, 0xff, 0x59, 0x02, 0x92,
+ 0x3f, 0x6e, 0xf9, 0xb0, 0xee, 0xb6, 0x40, 0xf2, 0x85, 0x81, 0xb8, 0x41,
+ 0xff, 0x81, 0x02, 0xa2, 0xe1, 0x55, 0xe1, 0x00, 0x50, 0x6f, 0x88, 0x30,
+ 0x0e, 0x88, 0x1d, 0xbf, 0xfe, 0x4c, 0x01, 0x2a, 0x48, 0x07, 0xdc, 0x6d,
+ 0x81, 0xa0, 0x8f, 0x7b, 0x3c, 0xac, 0xcb, 0x07, 0x0b, 0xd0, 0x01, 0xe4,
+ 0x2c, 0xff, 0xc1, 0x36, 0xc0, 0x41, 0x5c, 0x3e, 0x5a, 0x00, 0xff, 0x32,
+ 0x12, 0x4c, 0x3f, 0x2c, 0x20, 0x21, 0x7f, 0xf6, 0x1d, 0x02, 0x74, 0xc0,
+ 0xd5, 0x67, 0x65, 0x11, 0x78, 0xfd, 0x46, 0x01, 0x22, 0x67, 0xfa, 0x89,
+ 0xaf, 0x07, 0xe1, 0xbf, 0x4e, 0xc0, 0x20, 0xf5, 0xe2, 0x91, 0x06, 0xc8,
+ 0xc4, 0xf1, 0xdd, 0x02, 0x78, 0x22, 0x1e, 0xab, 0xc0, 0xc7, 0x5f, 0x25,
+ 0x60, 0xff, 0xe9, 0x44, 0xff, 0x7e, 0x01, 0xfd, 0x13, 0x8b, 0xfd, 0x25,
+ 0x02, 0xbe, 0xff, 0xcf, 0x9f, 0x80, 0x3f, 0x01, 0x7f, 0x02, 0xfe, 0x01,
+ 0x75, 0xe2, 0x8b, 0x93, 0x95, 0x3a, 0x44, 0x8f, 0x00, 0x00, 0x00, 0x00,
+ 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+unsigned int particle_png_len = 5564;
diff --git a/tools/hopalong/particle.png b/tools/hopalong/particle.png
new file mode 100644
index 0000000..d8bd700
--- /dev/null
+++ b/tools/hopalong/particle.png
Binary files differ
diff --git a/tools/hopalong/vfs.cpp b/tools/hopalong/vfs.cpp
new file mode 100644
index 0000000..c54d241
--- /dev/null
+++ b/tools/hopalong/vfs.cpp
@@ -0,0 +1,133 @@
+#include <rw.h>
+#include <string.h>
+
+#include "vfs.h"
+
+struct FILE_VFS
+{
+ int used;
+ rw::uint8 *data;
+ int pos;
+ int size;
+};
+FILE_VFS vfsfiles[64];
+VFS *globalVFS;
+
+void*
+vfs_fopen(const char *path, const char *mode)
+{
+ int flags = 0;
+ int i, j;
+ char *r, *w, *plus;
+
+// printf("trying to open <%s> mode <%s>\n", path, mode);
+ const char *file = strrchr((char*)path, '/');
+ if(file)
+ file++;
+ else
+ file = path;
+ for(i = 0; i < nelem(vfsfiles); i++){
+ if(!vfsfiles[i].used)
+ goto found;
+ }
+ // no file pointer available
+ return nil;
+found:
+
+ // why can't we pass const char*? urghhh....
+ r = strchr((char*)mode, 'r');
+ w = strchr((char*)mode, 'w');
+ plus = strchr((char*)mode, '+');
+ // cannot write files
+ if(w || plus)
+ return nil;
+
+ for(j = 0; j < globalVFS->numFiles; j++){
+ if(strcmp(globalVFS->files[j].name, file) == 0)
+ goto found2;
+ }
+ // file not found
+ return nil;
+found2:
+
+ vfsfiles[i].used = 1;
+ vfsfiles[i].data = globalVFS->files[j].data;
+ vfsfiles[i].pos = 0;
+ vfsfiles[i].size = globalVFS->files[j].length;
+ return &vfsfiles[i];
+}
+
+int
+vfs_fclose(void *fp)
+{
+ FILE_VFS *f = (FILE_VFS*)fp;
+ if(!f->used)
+ return EOF;
+ f->used = 0;
+ f->data = nil;
+ return 0;
+}
+
+int
+vfs_fseek(void *fp, long offset, int whence)
+{
+ FILE_VFS *f = (FILE_VFS*)fp;
+ switch(whence){
+ case 0:
+ f->pos = offset;
+ break;
+ case 1:
+ f->pos += offset;
+ break;
+ case 2:
+ f->pos = f->size - offset;
+ break;
+ }
+ if(f->pos > f->size) f->pos = f->size;
+ return f->pos;
+}
+
+long
+vfs_ftell(void *fp)
+{
+ FILE_VFS *f = (FILE_VFS*)fp;
+ return f->pos;
+}
+
+size_t
+vfs_fread(void *ptr, size_t size, size_t nmemb, void *fp)
+{
+ FILE_VFS *f = (FILE_VFS*)fp;
+ size_t sz = size*nmemb;
+ if(sz > f->size-f->pos)
+ sz = f->size-f->pos;
+ memcpy(ptr, f->data+f->pos, sz);
+ f->pos += sz;
+ return sz/size;
+}
+
+size_t
+vfs_fwrite(const void *ptr, size_t size, size_t nmemb, void *fp)
+{
+ return 0;
+}
+
+int
+vfs_feof(void *fp)
+{
+ FILE_VFS *f = (FILE_VFS*)fp;
+ return f->pos >= f->size;
+}
+
+void
+installVFS(VFS *vfs)
+{
+ globalVFS = vfs;
+ rw::engine->filefuncs.rwfopen = vfs_fopen;
+ rw::engine->filefuncs.rwfclose = vfs_fclose;
+ rw::engine->filefuncs.rwfseek = vfs_fseek;
+ rw::engine->filefuncs.rwftell = vfs_ftell;
+ rw::engine->filefuncs.rwfread = vfs_fread;
+ rw::engine->filefuncs.rwfwrite = vfs_fwrite;
+ rw::engine->filefuncs.rwfeof = vfs_feof;
+}
diff --git a/tools/hopalong/vfs.h b/tools/hopalong/vfs.h
new file mode 100644
index 0000000..768b193
--- /dev/null
+++ b/tools/hopalong/vfs.h
@@ -0,0 +1,15 @@
+struct VFS_file
+{
+ const char *name;
+ rw::uint8 *data;
+ rw::uint32 length;
+};
+
+struct VFS
+{
+ // TODO: directories?
+ VFS_file *files;
+ int numFiles;
+};
+
+void installVFS(VFS *vfs);
diff --git a/tools/im2d/CMakeLists.txt b/tools/im2d/CMakeLists.txt
new file mode 100644
index 0000000..d30325a
--- /dev/null
+++ b/tools/im2d/CMakeLists.txt
@@ -0,0 +1,23 @@
+add_executable(im2d WIN32
+ im2d.cpp
+ linelist.cpp
+ main.cpp
+ polyline.cpp
+ trifan.cpp
+ trilist.cpp
+ tristrip.cpp
+)
+
+target_link_libraries(im2d
+ PRIVATE
+ librw::skeleton
+ librw::librw
+)
+
+add_custom_command(
+ TARGET im2d POST_BUILD
+ COMMAND "${CMAKE_COMMAND}" -E make_directory "$<TARGET_FILE_DIR:im2d>/files"
+ COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/files" "$<TARGET_FILE_DIR:im2d>/files"
+)
+
+librw_platform_target(im2d)
diff --git a/tools/im2d/files/whiteash.png b/tools/im2d/files/whiteash.png
new file mode 100644
index 0000000..aa60569
--- /dev/null
+++ b/tools/im2d/files/whiteash.png
Binary files differ
diff --git a/tools/im2d/im2d.cpp b/tools/im2d/im2d.cpp
new file mode 100644
index 0000000..da4c084
--- /dev/null
+++ b/tools/im2d/im2d.cpp
@@ -0,0 +1,134 @@
+#include <rw.h>
+#include <skeleton.h>
+
+#include "im2d.h"
+
+bool Im2DColored = true;
+bool Im2DTextured;
+
+rw::int32 Im2DPrimType;
+
+rw::V2d ScreenSize;
+float Scale;
+
+rw::RGBA SolidWhite = {255, 255, 255, 255};
+rw::RGBA SolidBlack = {0, 0, 0, 255};
+rw::RGBA SolidRed = {200, 64, 64, 255};
+rw::RGBA SolidGreen = {64, 200, 64, 255};
+rw::RGBA SolidBlue = {64, 64, 200, 255};
+rw::RGBA SolidYellow = {200, 200, 64, 255};
+rw::RGBA SolidPurple = {200, 64, 200, 255};
+rw::RGBA SolidCyan = {64, 200, 200, 255};
+
+rw::Texture *Im2DTexture;
+
+void
+Im2DInitialize(rw::Camera *camera)
+{
+ ScreenSize.x = camera->frameBuffer->width;
+ ScreenSize.y = camera->frameBuffer->height;
+
+ Scale = ScreenSize.y / 3.0f;
+
+ rw::Image::setSearchPath("files/");
+ Im2DTexture = rw::Texture::read("whiteash", nil);
+
+ LineListCreate(camera);
+ LineListSetColor(!Im2DColored);
+
+ IndexedLineListCreate(camera);
+ IndexedLineListSetColor(!Im2DColored);
+
+ PolyLineCreate(camera);
+ PolyLineSetColor(!Im2DColored);
+
+ IndexedPolyLineCreate(camera);
+ IndexedPolyLineSetColor(!Im2DColored);
+
+ TriListCreate(camera);
+ TriListSetColor(!Im2DColored);
+
+ IndexedTriListCreate(camera);
+ IndexedTriListSetColor(!Im2DColored);
+
+ TriStripCreate(camera);
+ TriStripSetColor(!Im2DColored);
+
+ IndexedTriStripCreate(camera);
+ IndexedTriStripSetColor(!Im2DColored);
+
+ TriFanCreate(camera);
+ TriFanSetColor(!Im2DColored);
+
+ IndexedTriFanCreate(camera);
+ IndexedTriFanSetColor(!Im2DColored);
+}
+
+void
+Im2DTerminate(void)
+{
+ if(Im2DTexture){
+ Im2DTexture->destroy();
+ Im2DTexture = nil;
+ }
+}
+
+void
+Im2DRender(void)
+{
+ rw::SetRenderState(rw::ZTESTENABLE, 0);
+ rw::SetRenderState(rw::ZWRITEENABLE, 0);
+ rw::SetRenderState(rw::SRCBLEND, rw::BLENDSRCALPHA);
+ rw::SetRenderState(rw::DESTBLEND, rw::BLENDINVSRCALPHA);
+ rw::SetRenderState(rw::TEXTUREFILTER, rw::Texture::FilterMode::LINEAR);
+
+ if(Im2DTextured)
+ rw::SetRenderStatePtr(rw::TEXTURERASTER, Im2DTexture->raster);
+ else
+ rw::SetRenderStatePtr(rw::TEXTURERASTER, nil);
+
+ switch(Im2DPrimType){
+ case 0: LineListRender(); break;
+ case 1: IndexedLineListRender(); break;
+ case 2: PolyLineRender(); break;
+ case 3: IndexedPolyLineRender(); break;
+ case 4: TriListRender(); break;
+ case 5: IndexedTriListRender(); break;
+ case 6: TriStripRender(); break;
+ case 7: IndexedTriStripRender(); break;
+ case 8: TriFanRender(); break;
+ case 9: IndexedTriFanRender(); break;
+ }
+}
+
+void
+Im2DSize(rw::Camera *camera, rw::int32 w, rw::int32 h)
+{
+ ScreenSize.x = w;
+ ScreenSize.y = h;
+
+ if(ScreenSize.x > ScreenSize.y)
+ Scale = ScreenSize.y / 3.0f;
+ else
+ Scale = ScreenSize.x / 3.0f;
+
+ LineListCreate(camera);
+
+ IndexedLineListCreate(camera);
+
+ PolyLineCreate(camera);
+
+ IndexedPolyLineCreate(camera);
+
+ TriListCreate(camera);
+
+ IndexedTriListCreate(camera);
+
+ TriStripCreate(camera);
+
+ IndexedTriStripCreate(camera);
+
+ TriFanCreate(camera);
+
+ IndexedTriFanCreate(camera);
+}
diff --git a/tools/im2d/im2d.h b/tools/im2d/im2d.h
new file mode 100644
index 0000000..de82e03
--- /dev/null
+++ b/tools/im2d/im2d.h
@@ -0,0 +1,62 @@
+extern bool Im2DColored;
+extern bool Im2DTextured;
+
+extern rw::int32 Im2DPrimType;
+
+extern rw::V2d ScreenSize;
+extern float Scale;
+
+extern rw::RGBA SolidWhite;
+extern rw::RGBA SolidBlack;
+extern rw::RGBA SolidRed;
+extern rw::RGBA SolidGreen;
+extern rw::RGBA SolidBlue;
+extern rw::RGBA SolidYellow;
+extern rw::RGBA SolidPurple;
+extern rw::RGBA SolidCyan;
+
+
+void Im2DInitialize(rw::Camera *camera);
+void Im2DTerminate(void);
+void Im2DRender(void);
+void Im2DSize(rw::Camera *camera, rw::int32 w, rw::int32 h);
+
+void LineListCreate(rw::Camera *camera);
+void LineListSetColor(bool white);
+void LineListRender(void);
+
+void IndexedLineListCreate(rw::Camera *camera);
+void IndexedLineListSetColor(bool white);
+void IndexedLineListRender(void);
+
+void PolyLineCreate(rw::Camera *camera);
+void PolyLineSetColor(bool white);
+void PolyLineRender(void);
+
+void IndexedPolyLineCreate(rw::Camera *camera);
+void IndexedPolyLineSetColor(bool white);
+void IndexedPolyLineRender(void);
+
+void TriListCreate(rw::Camera *camera);
+void TriListSetColor(bool white);
+void TriListRender(void);
+
+void IndexedTriListCreate(rw::Camera *camera);
+void IndexedTriListSetColor(bool white);
+void IndexedTriListRender(void);
+
+void TriStripCreate(rw::Camera *camera);
+void TriStripSetColor(bool white);
+void TriStripRender(void);
+
+void IndexedTriStripCreate(rw::Camera *camera);
+void IndexedTriStripSetColor(bool white);
+void IndexedTriStripRender(void);
+
+void TriFanCreate(rw::Camera *camera);
+void TriFanSetColor(bool white);
+void TriFanRender(void);
+
+void IndexedTriFanCreate(rw::Camera *camera);
+void IndexedTriFanSetColor(bool white);
+void IndexedTriFanRender(void);
diff --git a/tools/im2d/linelist.cpp b/tools/im2d/linelist.cpp
new file mode 100644
index 0000000..6d76fb2
--- /dev/null
+++ b/tools/im2d/linelist.cpp
@@ -0,0 +1,159 @@
+#include <rw.h>
+#include <skeleton.h>
+
+#include "im2d.h"
+
+float LineListData[32][4] = {
+ { 0.000f, 0.000f, 0.000f, 0.000f},
+ { 0.000f, 1.000f, 1.000f, 1.000f},
+
+ { 0.000f, 0.000f, 0.000f, 0.000f},
+ { 0.383f, 0.924f, 1.000f, 1.000f},
+
+ { 0.000f, 0.000f, 0.000f, 0.000f},
+ { 0.707f, 0.707f, 1.000f, 1.000f},
+
+ { 0.000f, 0.000f, 0.000f, 0.000f},
+ { 0.924f, 0.383f, 1.000f, 1.000f},
+
+ { 0.000f, 0.000f, 0.000f, 0.000f},
+ { 1.000f, 0.000f, 1.000f, 1.000f},
+
+ { 0.000f, 0.000f, 0.000f, 0.000f},
+ { 0.924f, -0.383f, 1.000f, 1.000f},
+
+ { 0.000f, 0.000f, 0.000f, 0.000f},
+ { 0.707f, -0.707f, 1.000f, 1.000f},
+
+ { 0.000f, 0.000f, 0.000f, 0.000f},
+ { 0.383f, -0.924f, 1.000f, 1.000f},
+
+ { 0.000f, 0.000f, 0.000f, 0.000f},
+ { 0.000f, -1.000f, 1.000f, 1.000f},
+
+ { 0.000f, 0.000f, 0.000f, 0.000f},
+ {-0.383f, -0.924f, 1.000f, 1.000f},
+
+ { 0.000f, 0.000f, 0.000f, 0.000f},
+ {-0.707f, -0.707f, 1.000f, 1.000f},
+
+ { 0.000f, 0.000f, 0.000f, 0.000f},
+ {-0.924f, -0.383f, 1.000f, 1.000f},
+
+ { 0.000f, 0.000f, 0.000f, 0.000f},
+ {-1.000f, 0.000f, 1.000f, 1.000f},
+
+ { 0.000f, 0.000f, 0.000f, 0.000f},
+ {-0.924f, 0.383f, 1.000f, 1.000f},
+
+ { 0.000f, 0.000f, 0.000f, 0.000f},
+ {-0.707f, 0.707f, 1.000f, 1.000f},
+
+ { 0.000f, 0.000f, 0.000f, 0.000f},
+ {-0.383f, 0.924f, 1.000f, 1.000f}
+};
+
+float IndexedLineListData[16][4] = {
+ {-1.000f, 1.000f, 0.000f, 1.000f},
+ {-0.500f, 1.000f, 0.250f, 1.000f},
+ { 0.000f, 1.000f, 0.500f, 1.000f},
+ { 0.500f, 1.000f, 0.750f, 1.000f},
+ { 1.000f, 1.000f, 1.000f, 1.000f},
+
+ {-1.000f, 0.500f, 0.000f, 0.750f},
+ {-1.000f, 0.000f, 0.000f, 0.500f},
+ {-1.000f, -0.500f, 0.000f, 0.250f},
+
+ { 1.000f, 0.500f, 1.000f, 0.750f},
+ { 1.000f, 0.000f, 1.000f, 0.500f},
+ { 1.000f, -0.500f, 1.000f, 0.250f},
+
+ {-1.000f, -1.000f, 0.000f, 0.000f},
+ {-0.500f, -1.000f, 0.250f, 0.000f},
+ { 0.000f, -1.000f, 0.500f, 0.000f},
+ { 0.500f, -1.000f, 0.750f, 0.000f},
+ { 1.000f, -1.000f, 1.000f, 0.000f}
+};
+
+rw::uint16 IndexedLineListIndices[20] = {
+ 0, 11, 1, 12, 2, 13, 3, 14, 4, 15,
+ 0, 4, 5, 8, 6, 9, 7, 10, 11, 15
+};
+
+rw::RWDEVICE::Im2DVertex LineList[32];
+rw::RWDEVICE::Im2DVertex IndexedLineList[16];
+
+
+void
+LineListCreate(rw::Camera *camera)
+{
+ float recipZ = 1.0f/camera->nearPlane;
+ for(int i = 0; i < 32; i++){
+ LineList[i].setScreenX(ScreenSize.x/2.0f + LineListData[i][0]*Scale);
+ LineList[i].setScreenY(ScreenSize.y/2.0f - LineListData[i][1]*Scale);
+ LineList[i].setScreenZ(rw::im2d::GetNearZ());
+ LineList[i].setRecipCameraZ(recipZ);
+ LineList[i].setU(LineListData[i][2], recipZ);
+ LineList[i].setV(LineListData[i][3], recipZ);
+ }
+}
+
+void
+LineListSetColor(bool white)
+{
+ rw::RGBA SolidColor1 = SolidRed;
+ rw::RGBA SolidColor2 = SolidWhite;
+
+ if(white){
+ SolidColor1 = SolidWhite;
+ SolidColor2 = SolidWhite;
+ }
+
+ for(int i = 0; i < 32; i += 2){
+ LineList[i].setColor(SolidColor1.red, SolidColor1.green,
+ SolidColor1.blue, SolidColor1.alpha);
+ LineList[i+1].setColor(SolidColor2.red, SolidColor2.green,
+ SolidColor2.blue, SolidColor2.alpha);
+ }
+}
+
+void
+LineListRender(void)
+{
+ rw::im2d::RenderPrimitive(rw::PRIMTYPELINELIST, LineList, 32);
+}
+
+
+void
+IndexedLineListCreate(rw::Camera *camera)
+{
+ float recipZ = 1.0f/camera->nearPlane;
+ for(int i = 0; i < 16; i++){
+ IndexedLineList[i].setScreenX(ScreenSize.x/2.0f + IndexedLineListData[i][0]*Scale);
+ IndexedLineList[i].setScreenY(ScreenSize.y/2.0f - IndexedLineListData[i][1]*Scale);
+ IndexedLineList[i].setScreenZ(rw::im2d::GetNearZ());
+ IndexedLineList[i].setRecipCameraZ(recipZ);
+ IndexedLineList[i].setU(IndexedLineListData[i][2], recipZ);
+ IndexedLineList[i].setV(IndexedLineListData[i][3], recipZ);
+ }
+}
+
+void
+IndexedLineListSetColor(bool white)
+{
+ rw::RGBA SolidColor1 = SolidRed;
+
+ if(white)
+ SolidColor1 = SolidWhite;
+
+ for(int i = 0; i < 16; i++)
+ IndexedLineList[i].setColor(SolidColor1.red, SolidColor1.green,
+ SolidColor1.blue, SolidColor1.alpha);
+}
+
+void
+IndexedLineListRender(void)
+{
+ rw::im2d::RenderIndexedPrimitive(rw::PRIMTYPELINELIST,
+ IndexedLineList, 16, IndexedLineListIndices, 20);
+}
diff --git a/tools/im2d/main.cpp b/tools/im2d/main.cpp
new file mode 100644
index 0000000..e37dfd4
--- /dev/null
+++ b/tools/im2d/main.cpp
@@ -0,0 +1,254 @@
+#include <rw.h>
+#include <skeleton.h>
+#include <assert.h>
+
+#include "im2d.h"
+
+rw::EngineOpenParams engineOpenParams;
+
+rw::RGBA ForegroundColor = { 200, 200, 200, 255 };
+rw::RGBA BackgroundColor = { 64, 64, 64, 0 };
+
+rw::Camera *Camera;
+rw::Charset *Charset;
+
+float TimeDelta;
+
+rw::Camera*
+CreateCamera(void)
+{
+ Camera = sk::CameraCreate(sk::globals.width, sk::globals.height, 1);
+ assert(Camera);
+
+ Camera->setNearPlane(0.1f);
+ Camera->setFarPlane(10.0f);
+
+ return Camera;
+}
+
+void
+Initialize(void)
+{
+ sk::globals.windowtitle = "Im2D example";
+ sk::globals.width = 1280;
+ sk::globals.height = 800;
+ sk::globals.quit = 0;
+}
+
+bool
+Initialize3D(void)
+{
+ if(!sk::InitRW())
+ return false;
+
+ Charset = rw::Charset::create(&ForegroundColor, &BackgroundColor);
+
+ Camera = CreateCamera();
+
+ Im2DInitialize(Camera);
+
+ ImGui_ImplRW_Init();
+ ImGui::StyleColorsClassic();
+
+ return true;
+}
+
+void
+Terminate3D(void)
+{
+ Im2DTerminate();
+
+ if(Camera){
+ Camera->destroy();
+ Camera = nil;
+ }
+
+ if(Charset){
+ Charset->destroy();
+ Charset = nil;
+ }
+
+ sk::TerminateRW();
+}
+
+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
+DisplayOnScreenInfo(void)
+{
+}
+
+void
+Gui(void)
+{
+ static bool showWindow = true;
+ ImGui::Begin("Im2D", &showWindow);
+
+ ImGui::RadioButton("Line-list", &Im2DPrimType, 0);
+ ImGui::RadioButton("Line-list indexed", &Im2DPrimType, 1);
+ ImGui::RadioButton("Poly-line", &Im2DPrimType, 2);
+ ImGui::RadioButton("Poly-line indexed", &Im2DPrimType, 3);
+ ImGui::RadioButton("Tri-list", &Im2DPrimType, 4);
+ ImGui::RadioButton("Tri-list indexed", &Im2DPrimType, 5);
+ ImGui::RadioButton("Tri-strip", &Im2DPrimType, 6);
+ ImGui::RadioButton("Tri-strip indexed", &Im2DPrimType, 7);
+ ImGui::RadioButton("Tri-fan", &Im2DPrimType, 8);
+ ImGui::RadioButton("Tri-fan indexed", &Im2DPrimType, 9);
+
+ ImGui::NewLine();
+
+ ImGui::Checkbox("Textured", &Im2DTextured);
+ if(ImGui::Checkbox("Colored", &Im2DColored)){
+ LineListSetColor(!Im2DColored);
+ IndexedLineListSetColor(!Im2DColored);
+ PolyLineSetColor(!Im2DColored);
+ IndexedPolyLineSetColor(!Im2DColored);
+ TriListSetColor(!Im2DColored);
+ IndexedTriListSetColor(!Im2DColored);
+ TriStripSetColor(!Im2DColored);
+ IndexedTriStripSetColor(!Im2DColored);
+ TriFanSetColor(!Im2DColored);
+ IndexedTriFanSetColor(!Im2DColored);
+ }
+
+ ImGui::End();
+}
+
+void
+Render(void)
+{
+ Camera->clear(&BackgroundColor, rw::Camera::CLEARIMAGE|rw::Camera::CLEARZ);
+
+ Camera->beginUpdate();
+
+ ImGui_ImplRW_NewFrame(TimeDelta);
+
+ Im2DRender();
+ DisplayOnScreenInfo();
+
+ Gui();
+ ImGui::EndFrame();
+ ImGui::Render();
+
+ ImGui_ImplRW_RenderDrawLists(ImGui::GetDrawData());
+
+ Camera->endUpdate();
+
+ Camera->showRaster(0);
+}
+
+void
+Idle(float timeDelta)
+{
+ TimeDelta = timeDelta;
+ Render();
+}
+
+int MouseX, MouseY;
+int MouseDeltaX, MouseDeltaY;
+int MouseButtons;
+
+void
+KeyUp(int key)
+{
+}
+
+void
+KeyDown(int key)
+{
+ switch(key){
+ case sk::KEY_ESC:
+ sk::globals.quit = 1;
+ break;
+ }
+}
+
+void
+MouseBtn(sk::MouseState *mouse)
+{
+ MouseButtons = mouse->buttons;
+}
+
+void
+MouseMove(sk::MouseState *mouse)
+{
+ MouseDeltaX = mouse->posx - MouseX;
+ MouseDeltaY = mouse->posy - MouseY;
+ MouseX = mouse->posx;
+ MouseY = mouse->posy;
+}
+
+sk::EventStatus
+AppEventHandler(sk::Event e, void *param)
+{
+ using namespace sk;
+ Rect *r;
+ MouseState *ms;
+
+ ImGuiEventHandler(e, param);
+ ImGuiIO &io = ImGui::GetIO();
+
+ switch(e){
+ case INITIALIZE:
+ Initialize();
+ return EVENTPROCESSED;
+ case RWINITIALIZE:
+ return Initialize3D() ? EVENTPROCESSED : EVENTERROR;
+ case RWTERMINATE:
+ Terminate3D();
+ return EVENTPROCESSED;
+ case PLUGINATTACH:
+ return attachPlugins() ? EVENTPROCESSED : EVENTERROR;
+ case KEYDOWN:
+ KeyDown(*(int*)param);
+ return EVENTPROCESSED;
+ case KEYUP:
+ KeyUp(*(int*)param);
+ return EVENTPROCESSED;
+ case MOUSEBTN:
+ if(!io.WantCaptureMouse){
+ ms = (MouseState*)param;
+ MouseBtn(ms);
+ }else
+ MouseButtons = 0;
+ return EVENTPROCESSED;
+ case MOUSEMOVE:
+ MouseMove((MouseState*)param);
+ 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){
+ sk::CameraSize(::Camera, r, 0.5f, 4.0f/3.0f);
+ Im2DSize(::Camera, r->w, r->h);
+ }
+ break;
+ case IDLE:
+ Idle(*(float*)param);
+ return EVENTPROCESSED;
+ }
+ return sk::EVENTNOTPROCESSED;
+}
diff --git a/tools/im2d/polyline.cpp b/tools/im2d/polyline.cpp
new file mode 100644
index 0000000..5379117
--- /dev/null
+++ b/tools/im2d/polyline.cpp
@@ -0,0 +1,132 @@
+#include <rw.h>
+#include <skeleton.h>
+
+#include "im2d.h"
+
+float PolyLineData[16][4] = {
+ { 0.000f, 1.000f, 0.500f, 1.000f},
+ { 0.672f, 0.672f, 0.836f, 0.836f},
+ { 0.900f, 0.000f, 0.950f, 0.500f},
+ { 0.601f, -0.601f, 0.800f, 0.200f},
+ { 0.000f, -0.800f, 0.500f, 0.100f},
+ {-0.530f, -0.530f, 0.245f, 0.245f},
+ {-0.700f, 0.000f, 0.150f, 0.500f},
+ {-0.460f, 0.460f, 0.270f, 0.770f},
+ { 0.000f, 0.600f, 0.500f, 0.800f},
+ { 0.389f, 0.389f, 0.695f, 0.695f},
+ { 0.500f, 0.000f, 0.750f, 0.500f},
+ { 0.318f, -0.318f, 0.659f, 0.341f},
+ { 0.000f, -0.400f, 0.500f, 0.300f},
+ {-0.247f, -0.247f, 0.376f, 0.376f},
+ {-0.300f, 0.000f, 0.350f, 0.500f},
+ {-0.177f, 0.177f, 0.411f, 0.589f}
+};
+
+float IndexedPolyLineData[21][4] = {
+ { 0.000f, 1.000f, 0.500f, 1.000f},
+
+ {-0.200f, 0.600f, 0.400f, 0.800f},
+ { 0.200f, 0.600f, 0.600f, 0.800f},
+
+ {-0.400f, 0.200f, 0.300f, 0.600f},
+ { 0.000f, 0.200f, 0.500f, 0.600f},
+ { 0.400f, 0.200f, 0.700f, 0.600f},
+
+ {-0.600f, -0.200f, 0.200f, 0.400f},
+ {-0.200f, -0.200f, 0.400f, 0.400f},
+ { 0.200f, -0.200f, 0.600f, 0.400f},
+ { 0.600f, -0.200f, 0.800f, 0.400f},
+
+ {-0.800f, -0.600f, 0.100f, 0.200f},
+ {-0.400f, -0.600f, 0.300f, 0.200f},
+ { 0.000f, -0.600f, 0.500f, 0.200f},
+ { 0.400f, -0.600f, 0.700f, 0.200f},
+ { 0.800f, -0.600f, 0.900f, 0.200f},
+
+ {-1.000f, -1.000f, 0.000f, 0.000f},
+ {-0.600f, -1.000f, 0.200f, 0.000f},
+ {-0.200f, -1.000f, 0.400f, 0.000f},
+ { 0.200f, -1.000f, 0.600f, 0.000f},
+ { 0.600f, -1.000f, 0.800f, 0.000f},
+ { 1.000f, -1.000f, 1.000f, 0.000f}
+};
+
+rw::uint16 IndexedPolyLineIndices[46] = {
+ 0, 2, 5, 4, 8, 5, 9, 8, 13, 9,
+ 14, 13, 19, 14, 20, 19, 18, 13, 12, 8,
+ 7, 12, 18, 17, 12, 11, 17, 16, 15, 10,
+ 16, 11, 10, 6, 11, 7, 6, 3, 7, 4,
+ 3, 1, 4, 2, 1, 0
+};
+
+rw::RWDEVICE::Im2DVertex PolyLine[16];
+rw::RWDEVICE::Im2DVertex IndexedPolyLine[21];
+
+
+void
+PolyLineCreate(rw::Camera *camera)
+{
+ float recipZ = 1.0f/camera->nearPlane;
+ for(int i = 0; i < 16; i++){
+ PolyLine[i].setScreenX(ScreenSize.x/2.0f + PolyLineData[i][0]*Scale);
+ PolyLine[i].setScreenY(ScreenSize.y/2.0f - PolyLineData[i][1]*Scale);
+ PolyLine[i].setScreenZ(rw::im2d::GetNearZ());
+ PolyLine[i].setRecipCameraZ(recipZ);
+ PolyLine[i].setU(PolyLineData[i][2], recipZ);
+ PolyLine[i].setV(PolyLineData[i][3], recipZ);
+ }
+}
+
+void
+PolyLineSetColor(bool white)
+{
+ rw::RGBA SolidColor1 = SolidBlue;
+
+ if(white)
+ SolidColor1 = SolidWhite;
+
+ for(int i = 0; i < 16; i++)
+ PolyLine[i].setColor(SolidColor1.red, SolidColor1.green,
+ SolidColor1.blue, SolidColor1.alpha);
+}
+
+void
+PolyLineRender(void)
+{
+ rw::im2d::RenderPrimitive(rw::PRIMTYPEPOLYLINE, PolyLine, 16);
+}
+
+
+void
+IndexedPolyLineCreate(rw::Camera *camera)
+{
+ float recipZ = 1.0f/camera->nearPlane;
+ for(int i = 0; i < 21; i++){
+ IndexedPolyLine[i].setScreenX(ScreenSize.x/2.0f + IndexedPolyLineData[i][0]*Scale);
+ IndexedPolyLine[i].setScreenY(ScreenSize.y/2.0f - IndexedPolyLineData[i][1]*Scale);
+ IndexedPolyLine[i].setScreenZ(rw::im2d::GetNearZ());
+ IndexedPolyLine[i].setRecipCameraZ(recipZ);
+ IndexedPolyLine[i].setU(IndexedPolyLineData[i][2], recipZ);
+ IndexedPolyLine[i].setV(IndexedPolyLineData[i][3], recipZ);
+ }
+}
+
+void
+IndexedPolyLineSetColor(bool white)
+{
+ rw::RGBA SolidColor1 = SolidBlue;
+
+ if(white)
+ SolidColor1 = SolidWhite;
+
+ for(int i = 0; i < 21; i++)
+ IndexedPolyLine[i].setColor(SolidColor1.red, SolidColor1.green,
+ SolidColor1.blue, SolidColor1.alpha);
+}
+
+void
+IndexedPolyLineRender(void)
+{
+ rw::im2d::RenderIndexedPrimitive(rw::PRIMTYPEPOLYLINE,
+ IndexedPolyLine, 21, IndexedPolyLineIndices, 46);
+}
diff --git a/tools/im2d/trifan.cpp b/tools/im2d/trifan.cpp
new file mode 100644
index 0000000..b1a703f
--- /dev/null
+++ b/tools/im2d/trifan.cpp
@@ -0,0 +1,119 @@
+#include <rw.h>
+#include <skeleton.h>
+
+#include "im2d.h"
+
+float TriFanData[17][4] = {
+ { 0.000f, 1.000f, 0.500f, 1.000f},
+ {-0.383f, 0.924f, 0.308f, 0.962f},
+ {-0.707f, 0.707f, 0.146f, 0.854f},
+ {-0.924f, 0.383f, 0.038f, 0.692f},
+ {-1.000f, 0.000f, 0.000f, 0.500f},
+ {-0.924f, -0.383f, 0.038f, 0.308f},
+ {-0.707f, -0.707f, 0.146f, 0.146f},
+ {-0.383f, -0.924f, 0.308f, 0.038f},
+ { 0.000f, -1.000f, 0.500f, 0.000f},
+ { 0.383f, -0.924f, 0.692f, 0.038f},
+ { 0.707f, -0.707f, 0.854f, 0.146f},
+ { 0.924f, -0.383f, 0.962f, 0.308f},
+ { 1.000f, 0.000f, 1.000f, 0.500f},
+ { 0.924f, 0.383f, 0.962f, 0.692f},
+ { 0.707f, 0.707f, 0.854f, 0.854f},
+ { 0.383f, 0.924f, 0.692f, 0.962f},
+ { 0.000f, 1.000f, 0.500f, 1.000f}
+};
+
+float IndexedTriFanData[16][4] = {
+ { 0.000f, 1.000f, 0.500f, 1.000f},
+ {-0.383f, 0.924f, 0.308f, 0.962f},
+ {-0.707f, 0.707f, 0.146f, 0.854f},
+ {-0.924f, 0.383f, 0.038f, 0.692f},
+ {-1.000f, 0.000f, 0.000f, 0.500f},
+ {-0.924f, -0.383f, 0.038f, 0.308f},
+ {-0.707f, -0.707f, 0.146f, 0.146f},
+ {-0.383f, -0.924f, 0.308f, 0.038f},
+ { 0.000f, -1.000f, 0.500f, 0.000f},
+ { 0.383f, -0.924f, 0.692f, 0.038f},
+ { 0.707f, -0.707f, 0.854f, 0.146f},
+ { 0.924f, -0.383f, 0.962f, 0.308f},
+ { 1.000f, 0.000f, 1.000f, 0.500f},
+ { 0.924f, 0.383f, 0.962f, 0.692f},
+ { 0.707f, 0.707f, 0.854f, 0.854f},
+ { 0.383f, 0.924f, 0.692f, 0.962f}
+};
+
+rw::uint16 IndexedTriFanIndices[17] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0
+};
+
+rw::RWDEVICE::Im2DVertex TriFan[17];
+rw::RWDEVICE::Im2DVertex IndexedTriFan[16];
+
+
+void
+TriFanCreate(rw::Camera *camera)
+{
+ float recipZ = 1.0f/camera->nearPlane;
+ for(int i = 0; i < 17; i++){
+ TriFan[i].setScreenX(ScreenSize.x/2.0f + TriFanData[i][0]*Scale);
+ TriFan[i].setScreenY(ScreenSize.y/2.0f - TriFanData[i][1]*Scale);
+ TriFan[i].setScreenZ(rw::im2d::GetNearZ());
+ TriFan[i].setRecipCameraZ(recipZ);
+ TriFan[i].setU(TriFanData[i][2], recipZ);
+ TriFan[i].setV(TriFanData[i][3], recipZ);
+ }
+}
+
+void
+TriFanSetColor(bool white)
+{
+ rw::RGBA SolidColor1 = SolidYellow;
+
+ if(white)
+ SolidColor1 = SolidWhite;
+
+ for(int i = 0; i < 17; i++)
+ TriFan[i].setColor(SolidColor1.red, SolidColor1.green,
+ SolidColor1.blue, SolidColor1.alpha);
+}
+
+void
+TriFanRender(void)
+{
+ rw::im2d::RenderPrimitive(rw::PRIMTYPETRIFAN, TriFan, 17);
+}
+
+
+void
+IndexedTriFanCreate(rw::Camera *camera)
+{
+ float recipZ = 1.0f/camera->nearPlane;
+ for(int i = 0; i < 16; i++){
+ IndexedTriFan[i].setScreenX(ScreenSize.x/2.0f + IndexedTriFanData[i][0]*Scale);
+ IndexedTriFan[i].setScreenY(ScreenSize.y/2.0f - IndexedTriFanData[i][1]*Scale);
+ IndexedTriFan[i].setScreenZ(rw::im2d::GetNearZ());
+ IndexedTriFan[i].setRecipCameraZ(recipZ);
+ IndexedTriFan[i].setU(IndexedTriFanData[i][2], recipZ);
+ IndexedTriFan[i].setV(IndexedTriFanData[i][3], recipZ);
+ }
+}
+
+void
+IndexedTriFanSetColor(bool white)
+{
+ rw::RGBA SolidColor1 = SolidGreen;
+
+ if(white)
+ SolidColor1 = SolidWhite;
+
+ for(int i = 0; i < 16; i++)
+ IndexedTriFan[i].setColor(SolidColor1.red, SolidColor1.green,
+ SolidColor1.blue, SolidColor1.alpha);
+}
+
+void
+IndexedTriFanRender(void)
+{
+ rw::im2d::RenderIndexedPrimitive(rw::PRIMTYPETRIFAN,
+ IndexedTriFan, 16, IndexedTriFanIndices, 17);
+}
diff --git a/tools/im2d/trilist.cpp b/tools/im2d/trilist.cpp
new file mode 100644
index 0000000..64ee65f
--- /dev/null
+++ b/tools/im2d/trilist.cpp
@@ -0,0 +1,166 @@
+#include <rw.h>
+#include <skeleton.h>
+
+#include "im2d.h"
+
+float TriListData[18][4] = {
+ { 0.000f, 1.000f, 0.500f, 1.000f},
+ {-0.500f, 0.500f, 0.250f, 0.750f},
+ { 0.500f, 0.500f, 0.750f, 0.750f},
+
+ {-0.500f, 0.500f, 0.250f, 0.750f},
+ { 0.500f, -0.500f, 0.750f, 0.250f},
+ { 0.500f, 0.500f, 0.750f, 0.750f},
+
+ { 0.500f, 0.500f, 0.750f, 0.750f},
+ { 0.500f, -0.500f, 0.750f, 0.250f},
+ { 1.000f, 0.000f, 1.000f, 0.500f},
+
+ { 0.500f, -0.500f, 0.750f, 0.250f},
+ {-0.500f, -0.500f, 0.250f, 0.250f},
+ { 0.000f, -1.000f, 0.500f, 0.000f},
+
+ { 0.500f, -0.500f, 0.750f, 1.250f},
+ {-0.500f, 0.500f, 0.250f, 1.750f},
+ {-0.500f, -0.500f, 0.250f, 1.250f},
+
+ {-0.500f, -0.500f, 0.250f, 0.250f},
+ {-0.500f, 0.500f, 0.250f, 0.750f},
+ {-1.000f, 0.000f, 0.000f, 0.500f}
+};
+
+float IndexedTriListData[21][4] = {
+ { 0.000f, 1.000f, 0.500f, 1.000f},
+
+ {-0.200f, 0.600f, 0.400f, 0.800f},
+ { 0.200f, 0.600f, 0.600f, 0.800f},
+
+ {-0.400f, 0.200f, 0.300f, 0.600f},
+ { 0.000f, 0.200f, 0.500f, 0.600f},
+ { 0.400f, 0.200f, 0.300f, 0.600f},
+
+ {-0.600f, -0.200f, 0.200f, 0.400f},
+ {-0.200f, -0.200f, 0.400f, 0.400f},
+ { 0.200f, -0.200f, 0.600f, 0.400f},
+ { 0.600f, -0.200f, 0.800f, 0.400f},
+
+ {-0.800f, -0.600f, 0.100f, 0.200f},
+ {-0.400f, -0.600f, 0.300f, 0.200f},
+ { 0.000f, -0.600f, 0.500f, 0.200f},
+ { 0.400f, -0.600f, 0.700f, 0.200f},
+ { 0.800f, -0.600f, 0.900f, 0.200f},
+
+ {-1.000f, -1.000f, 0.000f, 0.000f},
+ {-0.600f, -1.000f, 0.200f, 0.000f},
+ {-0.200f, -1.000f, 0.400f, 0.000f},
+ { 0.200f, -1.000f, 0.600f, 0.000f},
+ { 0.600f, -1.000f, 0.800f, 0.000f},
+ { 1.000f, -1.000f, 1.000f, 0.000f}
+};
+
+rw::uint16 IndexedTriListIndices[45] = {
+ 0, 1, 2,
+ 1, 3, 4, 2, 4, 5,
+ 3, 6, 7, 4, 7, 8, 5, 8, 9,
+ 6, 10, 11, 7, 11, 12, 8, 12, 13, 9, 13, 14,
+ 10, 15, 16, 11, 16, 17, 12, 17, 18, 13, 18, 19, 14, 19, 20
+};
+
+rw::RWDEVICE::Im2DVertex TriList[18];
+rw::RWDEVICE::Im2DVertex IndexedTriList[21];
+
+
+void
+TriListCreate(rw::Camera *camera)
+{
+ float recipZ = 1.0f/camera->nearPlane;
+ for(int i = 0; i < 18; i++){
+ TriList[i].setScreenX(ScreenSize.x/2.0f + TriListData[i][0]*Scale);
+ TriList[i].setScreenY(ScreenSize.y/2.0f - TriListData[i][1]*Scale);
+ TriList[i].setScreenZ(rw::im2d::GetNearZ());
+ TriList[i].setRecipCameraZ(recipZ);
+ TriList[i].setU(TriListData[i][2], recipZ);
+ TriList[i].setV(TriListData[i][3], recipZ);
+ }
+}
+
+void
+TriListSetColor(bool white)
+{
+ int i;
+ rw::RGBA SolidColor1 = SolidBlue;
+ rw::RGBA SolidColor2 = SolidRed;
+ rw::RGBA SolidColor3 = SolidGreen;
+ rw::RGBA SolidColor4 = SolidYellow;
+ rw::RGBA SolidColor5 = SolidPurple;
+ rw::RGBA SolidColor6 = SolidCyan;
+
+ if(white){
+ SolidColor1 = SolidWhite;
+ SolidColor2 = SolidWhite;
+ SolidColor3 = SolidWhite;
+ SolidColor4 = SolidWhite;
+ SolidColor5 = SolidWhite;
+ SolidColor6 = SolidWhite;
+ }
+
+ for(i = 0; i < 3; i++)
+ TriList[i].setColor(SolidColor1.red, SolidColor1.green,
+ SolidColor1.blue, SolidColor1.alpha);
+ for(; i < 6; i++)
+ TriList[i].setColor(SolidColor2.red, SolidColor2.green,
+ SolidColor2.blue, SolidColor2.alpha);
+ for(; i < 9; i++)
+ TriList[i].setColor(SolidColor3.red, SolidColor3.green,
+ SolidColor3.blue, SolidColor3.alpha);
+ for(; i < 12; i++)
+ TriList[i].setColor(SolidColor4.red, SolidColor4.green,
+ SolidColor4.blue, SolidColor4.alpha);
+ for(; i < 15; i++)
+ TriList[i].setColor(SolidColor5.red, SolidColor5.green,
+ SolidColor5.blue, SolidColor5.alpha);
+ for(; i < 18; i++)
+ TriList[i].setColor(SolidColor6.red, SolidColor6.green,
+ SolidColor6.blue, SolidColor6.alpha);
+}
+
+void
+TriListRender(void)
+{
+ rw::im2d::RenderPrimitive(rw::PRIMTYPETRILIST, TriList, 18);
+}
+
+
+void
+IndexedTriListCreate(rw::Camera *camera)
+{
+ float recipZ = 1.0f/camera->nearPlane;
+ for(int i = 0; i < 21; i++){
+ IndexedTriList[i].setScreenX(ScreenSize.x/2.0f + IndexedTriListData[i][0]*Scale);
+ IndexedTriList[i].setScreenY(ScreenSize.y/2.0f - IndexedTriListData[i][1]*Scale);
+ IndexedTriList[i].setScreenZ(rw::im2d::GetNearZ());
+ IndexedTriList[i].setRecipCameraZ(recipZ);
+ IndexedTriList[i].setU(IndexedTriListData[i][2], recipZ);
+ IndexedTriList[i].setV(IndexedTriListData[i][3], recipZ);
+ }
+}
+
+void
+IndexedTriListSetColor(bool white)
+{
+ rw::RGBA SolidColor1 = SolidBlue;
+
+ if(white)
+ SolidColor1 = SolidWhite;
+
+ for(int i = 0; i < 21; i++)
+ IndexedTriList[i].setColor(SolidColor1.red, SolidColor1.green,
+ SolidColor1.blue, SolidColor1.alpha);
+}
+
+void
+IndexedTriListRender(void)
+{
+ rw::im2d::RenderIndexedPrimitive(rw::PRIMTYPETRILIST,
+ IndexedTriList, 21, IndexedTriListIndices, 45);
+}
diff --git a/tools/im2d/tristrip.cpp b/tools/im2d/tristrip.cpp
new file mode 100644
index 0000000..41f2d3a
--- /dev/null
+++ b/tools/im2d/tristrip.cpp
@@ -0,0 +1,130 @@
+#include <rw.h>
+#include <skeleton.h>
+
+#include "im2d.h"
+
+float TriStripData[18][4] = {
+ { 0.000f, 1.000f, 0.500f, 1.000f},
+ { 0.000f, 0.500f, 0.500f, 0.750f},
+
+ { 0.707f, 0.707f, 0.853f, 0.853f},
+ { 0.354f, 0.354f, 0.677f, 0.677f},
+
+ { 1.000f, 0.000f, 1.000f, 0.500f},
+ { 0.500f, 0.000f, 0.750f, 0.500f},
+
+ { 0.707f, -0.707f, 0.853f, 0.147f},
+ { 0.354f, -0.354f, 0.677f, 0.323f},
+
+ { 0.000f, -1.000f, 0.500f, 0.000f},
+ { 0.000f, -0.500f, 0.500f, 0.250f},
+
+ {-0.707f, -0.707f, 0.147f, 0.147f},
+ {-0.354f, -0.354f, 0.323f, 0.323f},
+
+ {-1.000f, 0.000f, 0.000f, 0.500f},
+ {-0.500f, 0.000f, 0.250f, 0.500f},
+
+ {-0.707f, 0.707f, 0.147f, 0.853f},
+ {-0.354f, 0.354f, 0.323f, 0.677f},
+
+ { 0.000f, 1.000f, 0.500f, 1.000f},
+ { 0.000f, 0.500f, 0.500f, 0.750f}
+};
+
+float IndexedTriStripData[16][4] = {
+ { 0.000f, 1.000f, 0.500f, 1.000f},
+ { 0.707f, 0.707f, 0.853f, 0.853f},
+ { 1.000f, 0.000f, 1.000f, 0.500f},
+ { 0.707f, -0.707f, 0.853f, 0.147f},
+ { 0.000f, -1.000f, 0.500f, 0.000f},
+ {-0.707f, -0.707f, 0.147f, 0.147f},
+ {-1.000f, 0.000f, 0.000f, 0.500f},
+ {-0.707f, 0.707f, 0.147f, 0.853f},
+
+ { 0.000f, 0.500f, 0.500f, 0.750f},
+ { 0.354f, 0.354f, 0.677f, 0.677f},
+ { 0.500f, 0.000f, 0.750f, 0.500f},
+ { 0.354f, -0.354f, 0.677f, 0.323f},
+ { 0.000f, -0.500f, 0.500f, 0.250f},
+ {-0.354f, -0.354f, 0.323f, 0.323f},
+ {-0.500f, 0.000f, 0.250f, 0.500f},
+ {-0.354f, 0.354f, 0.323f, 0.677f}
+};
+
+rw::uint16 IndexedTriStripIndices[18] = {
+ 0, 8, 1, 9, 2, 10, 3, 11,
+ 4, 12, 5, 13, 6, 14, 7, 15, 0, 8
+};
+
+rw::RWDEVICE::Im2DVertex TriStrip[18];
+rw::RWDEVICE::Im2DVertex IndexedTriStrip[16];
+
+
+void
+TriStripCreate(rw::Camera *camera)
+{
+ float recipZ = 1.0f/camera->nearPlane;
+ for(int i = 0; i < 18; i++){
+ TriStrip[i].setScreenX(ScreenSize.x/2.0f + TriStripData[i][0]*Scale);
+ TriStrip[i].setScreenY(ScreenSize.y/2.0f - TriStripData[i][1]*Scale);
+ TriStrip[i].setScreenZ(rw::im2d::GetNearZ());
+ TriStrip[i].setRecipCameraZ(recipZ);
+ TriStrip[i].setU(TriStripData[i][2], recipZ);
+ TriStrip[i].setV(TriStripData[i][3], recipZ);
+ }
+}
+
+void
+TriStripSetColor(bool white)
+{
+ rw::RGBA SolidColor1 = SolidPurple;
+
+ if(white)
+ SolidColor1 = SolidWhite;
+
+ for(int i = 0; i < 18; i++)
+ TriStrip[i].setColor(SolidColor1.red, SolidColor1.green,
+ SolidColor1.blue, SolidColor1.alpha);
+}
+
+void
+TriStripRender(void)
+{
+ rw::im2d::RenderPrimitive(rw::PRIMTYPETRISTRIP, TriStrip, 18);
+}
+
+
+void
+IndexedTriStripCreate(rw::Camera *camera)
+{
+ float recipZ = 1.0f/camera->nearPlane;
+ for(int i = 0; i < 16; i++){
+ IndexedTriStrip[i].setScreenX(ScreenSize.x/2.0f + IndexedTriStripData[i][0]*Scale);
+ IndexedTriStrip[i].setScreenY(ScreenSize.y/2.0f - IndexedTriStripData[i][1]*Scale);
+ IndexedTriStrip[i].setScreenZ(rw::im2d::GetNearZ());
+ IndexedTriStrip[i].setRecipCameraZ(recipZ);
+ IndexedTriStrip[i].setU(IndexedTriStripData[i][2], recipZ);
+ IndexedTriStrip[i].setV(IndexedTriStripData[i][3], recipZ);
+ }
+}
+
+void
+IndexedTriStripSetColor(bool white)
+{
+ rw::RGBA SolidColor1 = SolidCyan;
+
+ if(white)
+ SolidColor1 = SolidWhite;
+
+ for(int i = 0; i < 16; i++)
+ IndexedTriStrip[i].setColor(SolidColor1.red, SolidColor1.green,
+ SolidColor1.blue, SolidColor1.alpha);
+}
+
+void
+IndexedTriStripRender(void)
+{
+ rw::im2d::RenderIndexedPrimitive(rw::PRIMTYPETRISTRIP,
+ IndexedTriStrip, 16, IndexedTriStripIndices, 18);
+}
diff --git a/tools/im3d/CMakeLists.txt b/tools/im3d/CMakeLists.txt
new file mode 100644
index 0000000..1a49114
--- /dev/null
+++ b/tools/im3d/CMakeLists.txt
@@ -0,0 +1,23 @@
+add_executable(im3d WIN32
+ im3d.cpp
+ linelist.cpp
+ main.cpp
+ polyline.cpp
+ trifan.cpp
+ trilist.cpp
+ tristrip.cpp
+)
+
+target_link_libraries(im3d
+ PRIVATE
+ librw::skeleton
+ librw::librw
+)
+
+add_custom_command(
+ TARGET im3d POST_BUILD
+ COMMAND "${CMAKE_COMMAND}" -E make_directory "$<TARGET_FILE_DIR:im3d>/files"
+ COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/files" "$<TARGET_FILE_DIR:im3d>/files"
+)
+
+librw_platform_target(im3d)
diff --git a/tools/im3d/files/whiteash.png b/tools/im3d/files/whiteash.png
new file mode 100644
index 0000000..aa60569
--- /dev/null
+++ b/tools/im3d/files/whiteash.png
Binary files differ
diff --git a/tools/im3d/im3d.cpp b/tools/im3d/im3d.cpp
new file mode 100644
index 0000000..4588bc1
--- /dev/null
+++ b/tools/im3d/im3d.cpp
@@ -0,0 +1,138 @@
+#include <rw.h>
+#include <skeleton.h>
+
+#include "im3d.h"
+
+bool Im3DColored = true;
+bool Im3DTextured;
+
+rw::int32 Im3DPrimType;
+
+rw::RGBA SolidWhite = {255, 255, 255, 255};
+rw::RGBA SolidBlack = {0, 0, 0, 255};
+rw::RGBA SolidRed = {200, 64, 64, 255};
+rw::RGBA SolidGreen = {64, 200, 64, 255};
+rw::RGBA SolidBlue = {64, 64, 200, 255};
+rw::RGBA SolidYellow = {200, 200, 64, 255};
+rw::RGBA SolidPurple = {200, 64, 200, 255};
+rw::RGBA SolidCyan = {64, 200, 200, 255};
+
+rw::Matrix *Im3DMatrix;
+rw::Texture *Im3DTexture;
+
+void
+Im3DInitialize(void)
+{
+ Im3DMatrix = rw::Matrix::create();
+ assert(Im3DMatrix);
+
+ rw::Matrix *cameraMatrix = &Camera->getFrame()->matrix;
+ rw::V3d pos = rw::scale(cameraMatrix->at, 6.0f);
+ Im3DMatrix->rotate(&cameraMatrix->up, 30.0f);
+ Im3DMatrix->translate(&pos);
+
+ rw::Image::setSearchPath("files/");
+ Im3DTexture = rw::Texture::read("whiteash", nil);
+
+ LineListCreate();
+ LineListSetColor(!Im3DColored);
+
+ IndexedLineListCreate();
+ IndexedLineListSetColor(!Im3DColored);
+
+ PolyLineCreate();
+ PolyLineSetColor(!Im3DColored);
+
+ IndexedPolyLineCreate();
+ IndexedPolyLineSetColor(!Im3DColored);
+
+ TriListCreate();
+ TriListSetColor(!Im3DColored);
+
+ IndexedTriListCreate();
+ IndexedTriListSetColor(!Im3DColored);
+
+ TriStripCreate();
+ TriStripSetColor(!Im3DColored);
+
+ IndexedTriStripCreate();
+ IndexedTriStripSetColor(!Im3DColored);
+
+ TriFanCreate();
+ TriFanSetColor(!Im3DColored);
+
+ IndexedTriFanCreate();
+ IndexedTriFanSetColor(!Im3DColored);
+}
+
+void
+Im3DTerminate(void)
+{
+ if(Im3DMatrix){
+ Im3DMatrix->destroy();
+ Im3DMatrix = nil;
+ }
+
+ if(Im3DTexture){
+ Im3DTexture->destroy();
+ Im3DTexture = nil;
+ }
+}
+
+void
+Im3DRender(void)
+{
+ rw::SetRenderState(rw::ZTESTENABLE, 1);
+ rw::SetRenderState(rw::ZWRITEENABLE, 1);
+ rw::SetRenderState(rw::SRCBLEND, rw::BLENDONE);
+ rw::SetRenderState(rw::DESTBLEND, rw::BLENDZERO);
+ rw::SetRenderState(rw::TEXTUREFILTER, rw::Texture::FilterMode::LINEAR);
+ rw::SetRenderState(rw::CULLMODE, rw::CULLBACK);
+
+ rw::uint32 flags;
+ if(Im3DTextured){
+ rw::SetRenderStatePtr(rw::TEXTURERASTER, Im3DTexture->raster);
+ flags = rw::im3d::VERTEXUV | rw::im3d::ALLOPAQUE;
+ }else{
+ rw::SetRenderStatePtr(rw::TEXTURERASTER, nil);
+ flags = rw::im3d::ALLOPAQUE;
+ }
+
+ switch(Im3DPrimType){
+ case 0: LineListRender(Im3DMatrix, flags); break;
+ case 1: IndexedLineListRender(Im3DMatrix, flags); break;
+ case 2: PolyLineRender(Im3DMatrix, flags); break;
+ case 3: IndexedPolyLineRender(Im3DMatrix, flags); break;
+ case 4: TriListRender(Im3DMatrix, flags); break;
+ case 5: IndexedTriListRender(Im3DMatrix, flags); break;
+ case 6: TriStripRender(Im3DMatrix, flags); break;
+ case 7: IndexedTriStripRender(Im3DMatrix, flags); break;
+ case 8: TriFanRender(Im3DMatrix, flags); break;
+ case 9: IndexedTriFanRender(Im3DMatrix, flags); break;
+ }
+}
+
+void
+Im3DRotate(float xAngle, float yAngle)
+{
+ rw::Matrix *cameraMatrix = &Camera->getFrame()->matrix;
+ rw::V3d pos = Im3DMatrix->pos;
+
+ pos = rw::scale(pos, -1.0f);
+ Im3DMatrix->translate(&pos);
+
+ Im3DMatrix->rotate(&cameraMatrix->up, xAngle);
+ Im3DMatrix->rotate(&cameraMatrix->right, yAngle);
+
+ pos = rw::scale(pos, -1.0f);
+ Im3DMatrix->translate(&pos);
+}
+
+void
+Im3DTranslateZ(float zDelta)
+{
+ rw::Matrix *cameraMatrix = &Camera->getFrame()->matrix;
+ rw::V3d delta = rw::scale(cameraMatrix->at, zDelta);
+ Im3DMatrix->translate(&delta);
+}
+
diff --git a/tools/im3d/im3d.h b/tools/im3d/im3d.h
new file mode 100644
index 0000000..a9651cf
--- /dev/null
+++ b/tools/im3d/im3d.h
@@ -0,0 +1,62 @@
+extern rw::Camera *Camera;
+
+extern bool Im3DColored;
+extern bool Im3DTextured;
+
+extern rw::int32 Im3DPrimType;
+
+extern rw::RGBA SolidWhite;
+extern rw::RGBA SolidBlack;
+extern rw::RGBA SolidRed;
+extern rw::RGBA SolidGreen;
+extern rw::RGBA SolidBlue;
+extern rw::RGBA SolidYellow;
+extern rw::RGBA SolidPurple;
+extern rw::RGBA SolidCyan;
+
+
+void Im3DInitialize(void);
+void Im3DTerminate(void);
+void Im3DRender(void);
+void Im3DRotate(float xAngle, float yAngle);
+void Im3DTranslateZ(float zDelta);
+
+void LineListCreate(void);
+void LineListSetColor(bool white);
+void LineListRender(rw::Matrix *transform, rw::uint32 transformFlags);
+
+void IndexedLineListCreate(void);
+void IndexedLineListSetColor(bool white);
+void IndexedLineListRender(rw::Matrix *transform, rw::uint32 transformFlags);
+
+void PolyLineCreate(void);
+void PolyLineSetColor(bool white);
+void PolyLineRender(rw::Matrix *transform, rw::uint32 transformFlags);
+
+void IndexedPolyLineCreate(void);
+void IndexedPolyLineSetColor(bool white);
+void IndexedPolyLineRender(rw::Matrix *transform, rw::uint32 transformFlags);
+
+void TriListCreate(void);
+void TriListSetColor(bool white);
+void TriListRender(rw::Matrix *transform, rw::uint32 transformFlags);
+
+void IndexedTriListCreate(void);
+void IndexedTriListSetColor(bool white);
+void IndexedTriListRender(rw::Matrix *transform, rw::uint32 transformFlags);
+
+void TriStripCreate(void);
+void TriStripSetColor(bool white);
+void TriStripRender(rw::Matrix *transform, rw::uint32 transformFlags);
+
+void IndexedTriStripCreate(void);
+void IndexedTriStripSetColor(bool white);
+void IndexedTriStripRender(rw::Matrix *transform, rw::uint32 transformFlags);
+
+void TriFanCreate(void);
+void TriFanSetColor(bool white);
+void TriFanRender(rw::Matrix *transform, rw::uint32 transformFlags);
+
+void IndexedTriFanCreate(void);
+void IndexedTriFanSetColor(bool white);
+void IndexedTriFanRender(rw::Matrix *transform, rw::uint32 transformFlags);
diff --git a/tools/im3d/linelist.cpp b/tools/im3d/linelist.cpp
new file mode 100644
index 0000000..768e87a
--- /dev/null
+++ b/tools/im3d/linelist.cpp
@@ -0,0 +1,176 @@
+#include <rw.h>
+#include <skeleton.h>
+
+#include "im3d.h"
+
+float LineListData[28][5] = {
+ { 0.000f, 0.000f, 0.000f, 0.000f, 0.000f},
+ { 0.000f, 1.000f, 0.000f, 1.000f, 1.000f},
+
+ { 0.000f, 0.000f, 0.000f, 0.000f, 0.000f},
+ { 0.000f, -1.000f, 0.000f, 1.000f, 1.000f},
+
+ { 0.000f, 0.000f, 0.000f, 0.000f, 0.000f},
+ { 0.000f, 0.000f, 1.000f, 1.000f, 1.000f},
+
+ { 0.000f, 0.000f, 0.000f, 0.000f, 0.000f},
+ { 0.000f, 0.000f, -1.000f, 1.000f, 1.000f},
+
+ { 0.000f, 0.000f, 0.000f, 0.000f, 0.000f},
+ { 1.000f, 0.000f, 0.000f, 1.000f, 1.000f},
+
+ { 0.000f, 0.000f, 0.000f, 0.000f, 0.000f},
+ {-1.000f, 0.000f, 0.000f, 1.000f, 1.000f},
+
+ { 0.000f, 0.000f, 0.000f, 0.000f, 0.000f},
+ { 0.577f, 0.577f, 0.577f, 1.000f, 1.000f},
+
+ { 0.000f, 0.000f, 0.000f, 0.000f, 0.000f},
+ { 0.577f, -0.577f, 0.577f, 1.000f, 1.000f},
+
+ { 0.000f, 0.000f, 0.000f, 0.000f, 0.000f},
+ {-0.577f, 0.577f, -0.577f, 1.000f, 1.000f},
+
+ { 0.000f, 0.000f, 0.000f, 0.000f, 0.000f},
+ {-0.577f, -0.577f, -0.577f, 1.000f, 1.000f},
+
+ { 0.000f, 0.000f, 0.000f, 0.000f, 0.000f},
+ { 0.577f, -0.577f, -0.577f, 1.000f, 1.000f},
+
+ { 0.000f, 0.000f, 0.000f, 0.000f, 0.000f},
+ { 0.577f, 0.577f, -0.577f, 1.000f, 1.000f},
+
+ { 0.000f, 0.000f, 0.000f, 0.000f, 0.000f},
+ {-0.577f, -0.577f, 0.577f, 1.000f, 1.000f},
+
+ { 0.000f, 0.000f, 0.000f, 0.000f, 0.000f},
+ {-0.577f, 0.577f, 0.577f, 1.000f, 1.000f}
+};
+
+float IndexedLineListData[18][5] = {
+ { 0.000f, 1.000f, 0.000f, 0.000f, 0.000f},
+
+ { 0.577f, 0.577f, 0.577f, 0.000f, 0.000f},
+ { 0.577f, 0.577f, -0.577f, 0.000f, 0.000f},
+ {-0.577f, 0.577f, -0.577f, 0.000f, 0.000f},
+ {-0.577f, 0.577f, 0.577f, 0.000f, 0.000f},
+
+ { 0.000f, 0.000f, 1.000f, 0.000f, 0.000f},
+ { 0.707f, 0.000f, 0.707f, 0.000f, 0.000f},
+ { 1.000f, 0.000f, 0.000f, 0.000f, 0.000f},
+ { 0.707f, 0.000f, -0.707f, 0.000f, 0.000f},
+ { 0.000f, 0.000f, -1.000f, 0.000f, 0.000f},
+ {-0.707f, 0.000f, -0.707f, 0.000f, 0.000f},
+ {-1.000f, 0.000f, 0.000f, 0.000f, 0.000f},
+ {-0.707f, 0.000f, 0.707f, 0.000f, 0.000f},
+
+ { 0.577f, -0.577f, 0.577f, 0.000f, 0.000f},
+ { 0.577f, -0.577f, -0.577f, 0.000f, 0.000f},
+ {-0.577f, -0.577f, -0.577f, 0.000f, 0.000f},
+ {-0.577f, -0.577f, 0.577f, 0.000f, 0.000f},
+
+ { 0.000f, -1.000f, 0.000f, 0.000f, 0.000f}
+};
+
+rw::uint16 IndexedLineListIndices[96] = {
+ 0,1, 0,2, 0,3, 0,4, 1,2, 2,3, 3,4, 4,1,
+
+ 1,5, 1,6, 1,7, 2,7, 2,8, 2,9, 3,9, 3,10, 3,11, 4,11, 4,12, 4,5,
+ 5,6, 6,7, 7,8, 8,9, 9,10, 10,11, 11,12, 12,5,
+ 13,5, 13,6, 13,7, 14,7, 14,8, 14,9, 15,9, 15,10, 15,11, 16,11, 16,12, 16,5,
+
+ 17,13, 17,14, 17,15, 17,16, 13,14, 14,15, 15,16, 16,13
+};
+
+rw::RWDEVICE::Im3DVertex LineList[28];
+rw::RWDEVICE::Im3DVertex IndexedLineList[18];
+
+
+void
+LineListCreate(void)
+{
+ for(int i = 0; i < 28; i++){
+ LineList[i].setX(LineListData[i][0]);
+ LineList[i].setY(LineListData[i][1]);
+ LineList[i].setZ(LineListData[i][2]);
+ LineList[i].setU(LineListData[i][3]);
+ LineList[i].setV(LineListData[i][4]);
+ }
+}
+
+void
+LineListSetColor(bool white)
+{
+ rw::RGBA SolidColor1 = SolidRed;
+ rw::RGBA SolidColor2 = SolidWhite;
+
+ if(white){
+ SolidColor1 = SolidWhite;
+ SolidColor2 = SolidWhite;
+ }
+
+ for(int i = 0; i < 28; i += 2){
+ LineList[i].setColor(SolidColor1.red, SolidColor1.green,
+ SolidColor1.blue, SolidColor1.alpha);
+ LineList[i+1].setColor(SolidColor2.red, SolidColor2.green,
+ SolidColor2.blue, SolidColor2.alpha);
+ }
+}
+
+void
+LineListRender(rw::Matrix *transform, rw::uint32 transformFlags)
+{
+ rw::im3d::Transform(LineList, 28, transform, transformFlags);
+ rw::im3d::RenderPrimitive(rw::PRIMTYPELINELIST);
+ rw::im3d::End();
+}
+
+
+void
+IndexedLineListCreate(void)
+{
+ for(int i = 0; i < 18; i++){
+ IndexedLineList[i].setX(IndexedLineListData[i][0]);
+ IndexedLineList[i].setY(IndexedLineListData[i][1]);
+ IndexedLineList[i].setZ(IndexedLineListData[i][2]);
+ IndexedLineList[i].setU(IndexedLineListData[i][3]);
+ IndexedLineList[i].setV(IndexedLineListData[i][4]);
+ }
+}
+
+void
+IndexedLineListSetColor(bool white)
+{
+ int i;
+ rw::RGBA SolidColor1 = SolidRed;
+ rw::RGBA SolidColor2 = SolidGreen;
+ rw::RGBA SolidColor3 = SolidBlue;
+
+ if(white){
+ SolidColor1 = SolidWhite;
+ SolidColor2 = SolidWhite;
+ SolidColor3 = SolidWhite;
+ }
+
+ IndexedLineList[0].setColor(SolidColor1.red, SolidColor1.green,
+ SolidColor1.blue, SolidColor1.alpha);
+ for(i = 1; i < 5; i++)
+ IndexedLineList[i].setColor(SolidColor2.red, SolidColor2.green,
+ SolidColor2.blue, SolidColor2.alpha);
+ for(; i < 13; i++)
+ IndexedLineList[i].setColor(SolidColor3.red, SolidColor3.green,
+ SolidColor3.blue, SolidColor3.alpha);
+ for(; i < 17; i++)
+ IndexedLineList[i].setColor(SolidColor2.red, SolidColor2.green,
+ SolidColor2.blue, SolidColor2.alpha);
+ IndexedLineList[i].setColor(SolidColor1.red, SolidColor1.green,
+ SolidColor1.blue, SolidColor1.alpha);
+}
+
+void
+IndexedLineListRender(rw::Matrix *transform, rw::uint32 transformFlags)
+{
+ rw::im3d::Transform(IndexedLineList, 18, transform, transformFlags);
+ rw::im3d::RenderIndexedPrimitive(rw::PRIMTYPELINELIST, IndexedLineListIndices, 96);
+ rw::im3d::End();
+}
diff --git a/tools/im3d/main.cpp b/tools/im3d/main.cpp
new file mode 100644
index 0000000..4949d2e
--- /dev/null
+++ b/tools/im3d/main.cpp
@@ -0,0 +1,268 @@
+#include <rw.h>
+#include <skeleton.h>
+#include <assert.h>
+
+#include "im3d.h"
+
+rw::EngineOpenParams engineOpenParams;
+
+rw::RGBA ForegroundColor = { 200, 200, 200, 255 };
+rw::RGBA BackgroundColor = { 64, 64, 64, 0 };
+
+rw::Camera *Camera;
+rw::Charset *Charset;
+
+float TimeDelta;
+
+rw::Camera*
+CreateCamera(void)
+{
+ Camera = sk::CameraCreate(sk::globals.width, sk::globals.height, 1);
+ assert(Camera);
+
+ Camera->setNearPlane(0.1f);
+ Camera->setFarPlane(50.0f);
+
+ return Camera;
+}
+
+void
+Initialize(void)
+{
+ sk::globals.windowtitle = "Im3D example";
+ sk::globals.width = 1280;
+ sk::globals.height = 800;
+ sk::globals.quit = 0;
+}
+
+bool
+Initialize3D(void)
+{
+ if(!sk::InitRW())
+ return false;
+
+ Charset = rw::Charset::create(&ForegroundColor, &BackgroundColor);
+
+ Camera = CreateCamera();
+
+ Im3DInitialize();
+
+ ImGui_ImplRW_Init();
+ ImGui::StyleColorsClassic();
+
+ rw::Rect r;
+ r.x = 0;
+ r.y = 0;
+ r.w = sk::globals.width;
+ r.h = sk::globals.height;
+ sk::CameraSize(::Camera, &r, 0.5f, 4.0f/3.0f);
+
+ return true;
+}
+
+void
+Terminate3D(void)
+{
+ Im3DTerminate();
+
+ if(Camera){
+ Camera->destroy();
+ Camera = nil;
+ }
+
+ if(Charset){
+ Charset->destroy();
+ Charset = nil;
+ }
+
+ sk::TerminateRW();
+}
+
+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
+DisplayOnScreenInfo(void)
+{
+}
+
+void
+Gui(void)
+{
+ static bool showWindow = true;
+ ImGui::Begin("Im2D", &showWindow);
+
+ ImGui::RadioButton("Line-list", &Im3DPrimType, 0);
+ ImGui::RadioButton("Line-list indexed", &Im3DPrimType, 1);
+ ImGui::RadioButton("Poly-line", &Im3DPrimType, 2);
+ ImGui::RadioButton("Poly-line indexed", &Im3DPrimType, 3);
+ ImGui::RadioButton("Tri-list", &Im3DPrimType, 4);
+ ImGui::RadioButton("Tri-list indexed", &Im3DPrimType, 5);
+ ImGui::RadioButton("Tri-strip", &Im3DPrimType, 6);
+ ImGui::RadioButton("Tri-strip indexed", &Im3DPrimType, 7);
+ ImGui::RadioButton("Tri-fan", &Im3DPrimType, 8);
+ ImGui::RadioButton("Tri-fan indexed", &Im3DPrimType, 9);
+
+ ImGui::NewLine();
+
+ ImGui::Checkbox("Textured", &Im3DTextured);
+ if(ImGui::Checkbox("Colored", &Im3DColored)){
+ LineListSetColor(!Im3DColored);
+ IndexedLineListSetColor(!Im3DColored);
+ PolyLineSetColor(!Im3DColored);
+ IndexedPolyLineSetColor(!Im3DColored);
+ TriListSetColor(!Im3DColored);
+ IndexedTriListSetColor(!Im3DColored);
+ TriStripSetColor(!Im3DColored);
+ IndexedTriStripSetColor(!Im3DColored);
+ TriFanSetColor(!Im3DColored);
+ IndexedTriFanSetColor(!Im3DColored);
+ }
+
+ ImGui::End();
+}
+
+void
+Render(void)
+{
+ Camera->clear(&BackgroundColor, rw::Camera::CLEARIMAGE|rw::Camera::CLEARZ);
+
+ Camera->beginUpdate();
+
+ ImGui_ImplRW_NewFrame(TimeDelta);
+
+ Im3DRender();
+ DisplayOnScreenInfo();
+
+ Gui();
+ ImGui::EndFrame();
+ ImGui::Render();
+
+ ImGui_ImplRW_RenderDrawLists(ImGui::GetDrawData());
+
+ Camera->endUpdate();
+
+ Camera->showRaster(0);
+}
+
+void
+Idle(float timeDelta)
+{
+ TimeDelta = timeDelta;
+ Render();
+}
+
+int MouseX, MouseY;
+int MouseDeltaX, MouseDeltaY;
+int MouseButtons;
+
+bool Rotate, Translate;
+
+void
+KeyUp(int key)
+{
+}
+
+void
+KeyDown(int key)
+{
+ switch(key){
+ case sk::KEY_ESC:
+ sk::globals.quit = 1;
+ break;
+ }
+}
+
+void
+MouseBtn(sk::MouseState *mouse)
+{
+ MouseButtons = mouse->buttons;
+ Rotate = !!(MouseButtons&1);
+ Translate = !!(MouseButtons&4);
+}
+
+void
+MouseMove(sk::MouseState *mouse)
+{
+ MouseDeltaX = mouse->posx - MouseX;
+ MouseDeltaY = mouse->posy - MouseY;
+ MouseX = mouse->posx;
+ MouseY = mouse->posy;
+ if(Rotate)
+ Im3DRotate(MouseDeltaX, -MouseDeltaY);
+ if(Translate)
+ Im3DTranslateZ(-MouseDeltaY*0.1f);
+}
+
+sk::EventStatus
+AppEventHandler(sk::Event e, void *param)
+{
+ using namespace sk;
+ Rect *r;
+ MouseState *ms;
+
+ ImGuiEventHandler(e, param);
+ ImGuiIO &io = ImGui::GetIO();
+
+ switch(e){
+ case INITIALIZE:
+ Initialize();
+ return EVENTPROCESSED;
+ case RWINITIALIZE:
+ return Initialize3D() ? EVENTPROCESSED : EVENTERROR;
+ case RWTERMINATE:
+ Terminate3D();
+ return EVENTPROCESSED;
+ case PLUGINATTACH:
+ return attachPlugins() ? EVENTPROCESSED : EVENTERROR;
+ case KEYDOWN:
+ KeyDown(*(int*)param);
+ return EVENTPROCESSED;
+ case KEYUP:
+ KeyUp(*(int*)param);
+ return EVENTPROCESSED;
+ case MOUSEBTN:
+ if(!io.WantCaptureMouse){
+ ms = (MouseState*)param;
+ MouseBtn(ms);
+ }else
+ MouseButtons = 0;
+ return EVENTPROCESSED;
+ case MOUSEMOVE:
+ MouseMove((MouseState*)param);
+ 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){
+ sk::CameraSize(::Camera, r, 0.5f, 4.0f/3.0f);
+ }
+ break;
+ case IDLE:
+ Idle(*(float*)param);
+ return EVENTPROCESSED;
+ }
+ return sk::EVENTNOTPROCESSED;
+}
diff --git a/tools/im3d/polyline.cpp b/tools/im3d/polyline.cpp
new file mode 100644
index 0000000..1a206b0
--- /dev/null
+++ b/tools/im3d/polyline.cpp
@@ -0,0 +1,157 @@
+#include <rw.h>
+#include <skeleton.h>
+
+#include "im3d.h"
+
+float PolyLineData[21][5] = {
+ { 0.000f, 1.000f, 1.000f, 0.500f, 1.000f},
+ { 0.707f, 0.707f, 0.900f, 0.854f, 0.854f},
+ { 1.000f, 0.000f, 0.800f, 1.000f, 0.500f},
+ { 0.707f, -0.707f, 0.700f, 0.854f, 0.146f},
+ { 0.000f, -1.000f, 0.600f, 0.500f, 0.000f},
+ {-0.707f, -0.707f, 0.500f, 0.146f, 0.146f},
+ {-1.000f, -0.000f, 0.400f, 0.000f, 0.500f},
+ {-0.707f, 0.707f, 0.300f, 0.146f, 0.854f},
+
+ { 0.000f, 1.000f, 0.200f, 0.500f, 1.000f},
+ { 0.707f, 0.707f, 0.100f, 0.854f, 0.854f},
+ { 1.000f, 0.000f, 0.000f, 1.000f, 0.500f},
+ { 0.707f, -0.707f, -0.100f, 0.854f, 0.146f},
+ { 0.000f, -1.000f, -0.200f, 0.500f, 0.000f},
+ {-0.707f, -0.707f, -0.300f, 0.146f, 0.146f},
+ {-1.000f, -0.000f, -0.400f, 0.000f, 0.500f},
+ {-0.707f, 0.707f, -0.500f, 0.146f, 0.854f},
+
+ { 0.000f, 1.000f, -0.600f, 0.500f, 1.000f},
+ { 0.707f, 0.707f, -0.700f, 0.854f, 0.854f},
+ { 1.000f, 0.000f, -0.800f, 1.000f, 0.500f},
+ { 0.707f, -0.707f, -0.900f, 0.854f, 0.146f},
+ { 0.000f, -1.000f, -1.000f, 0.500f, 0.000f}
+};
+
+float IndexedPolyLineData[8][5] = {
+ { 1.000f, 1.000f, 1.000f, 1.000f, 0.000f},
+ {-1.000f, 1.000f, 1.000f, 1.000f, 1.000f},
+ {-1.000f, -1.000f, 1.000f, 0.500f, 1.000f},
+ { 1.000f, -1.000f, 1.000f, 0.500f, 0.000f},
+
+ { 1.000f, 1.000f, -1.000f, 0.500f, 0.000f},
+ {-1.000f, 1.000f, -1.000f, 0.500f, 1.000f},
+ {-1.000f, -1.000f, -1.000f, 0.000f, 1.000f},
+ { 1.000f, -1.000f, -1.000f, 0.000f, 0.000f}
+};
+
+rw::uint16 IndexedPolyLineIndices[25] = {
+ 0, 1, 2, 3, 0, 2, 6, 5, 1, 3, 7, 4, 0, 5, 4, 6, 1, 4, 3, 6, 7, 5, 2, 7, 0
+};
+
+rw::RWDEVICE::Im3DVertex PolyLine[21];
+rw::RWDEVICE::Im3DVertex IndexedPolyLine[8];
+
+
+void
+PolyLineCreate(void)
+{
+ for(int i = 0; i < 21; i++){
+ PolyLine[i].setX(PolyLineData[i][0]);
+ PolyLine[i].setY(PolyLineData[i][1]);
+ PolyLine[i].setZ(PolyLineData[i][2]);
+ PolyLine[i].setU(PolyLineData[i][3]);
+ PolyLine[i].setV(PolyLineData[i][4]);
+ }
+}
+
+void
+PolyLineSetColor(bool white)
+{
+ int i;
+ rw::RGBA SolidColor1 = SolidRed;
+ rw::RGBA SolidColor2 = SolidGreen;
+ rw::RGBA SolidColor3 = SolidBlue;
+
+ if(white){
+ SolidColor1 = SolidWhite;
+ SolidColor2 = SolidWhite;
+ SolidColor3 = SolidWhite;
+ }
+
+ for(i = 0; i < 7; i++)
+ PolyLine[i].setColor(SolidColor1.red, SolidColor1.green,
+ SolidColor1.blue, SolidColor1.alpha);
+ for(; i < 14; i++)
+ PolyLine[i].setColor(SolidColor2.red, SolidColor2.green,
+ SolidColor2.blue, SolidColor2.alpha);
+ for(; i < 21; i++)
+ PolyLine[i].setColor(SolidColor3.red, SolidColor3.green,
+ SolidColor3.blue, SolidColor3.alpha);
+}
+
+void
+PolyLineRender(rw::Matrix *transform, rw::uint32 transformFlags)
+{
+ rw::im3d::Transform(PolyLine, 21, transform, transformFlags);
+ rw::im3d::RenderPrimitive(rw::PRIMTYPEPOLYLINE);
+ rw::im3d::End();
+}
+
+
+void
+IndexedPolyLineCreate(void)
+{
+ for(int i = 0; i < 8; i++){
+ IndexedPolyLine[i].setX(IndexedPolyLineData[i][0]);
+ IndexedPolyLine[i].setY(IndexedPolyLineData[i][1]);
+ IndexedPolyLine[i].setZ(IndexedPolyLineData[i][2]);
+ IndexedPolyLine[i].setU(IndexedPolyLineData[i][3]);
+ IndexedPolyLine[i].setV(IndexedPolyLineData[i][4]);
+ }
+}
+
+void
+IndexedPolyLineSetColor(bool white)
+{
+ rw::RGBA SolidColor1 = SolidRed;
+ rw::RGBA SolidColor2 = SolidYellow;
+ rw::RGBA SolidColor3 = SolidBlack;
+ rw::RGBA SolidColor4 = SolidPurple;
+ rw::RGBA SolidColor5 = SolidGreen;
+ rw::RGBA SolidColor6 = SolidCyan;
+ rw::RGBA SolidColor7 = SolidBlue;
+ rw::RGBA SolidColor8 = SolidWhite;
+
+ if(white){
+ SolidColor1 = SolidWhite;
+ SolidColor2 = SolidWhite;
+ SolidColor3 = SolidWhite;
+ SolidColor4 = SolidWhite;
+ SolidColor5 = SolidWhite;
+ SolidColor6 = SolidWhite;
+ SolidColor7 = SolidWhite;
+ SolidColor8 = SolidWhite;
+ }
+
+ IndexedPolyLine[0].setColor(SolidColor1.red, SolidColor1.green,
+ SolidColor1.blue, SolidColor1.alpha);
+ IndexedPolyLine[1].setColor(SolidColor2.red, SolidColor2.green,
+ SolidColor2.blue, SolidColor2.alpha);
+ IndexedPolyLine[2].setColor(SolidColor3.red, SolidColor3.green,
+ SolidColor3.blue, SolidColor3.alpha);
+ IndexedPolyLine[3].setColor(SolidColor4.red, SolidColor4.green,
+ SolidColor4.blue, SolidColor4.alpha);
+ IndexedPolyLine[4].setColor(SolidColor5.red, SolidColor5.green,
+ SolidColor5.blue, SolidColor5.alpha);
+ IndexedPolyLine[5].setColor(SolidColor6.red, SolidColor6.green,
+ SolidColor6.blue, SolidColor6.alpha);
+ IndexedPolyLine[6].setColor(SolidColor7.red, SolidColor7.green,
+ SolidColor7.blue, SolidColor7.alpha);
+ IndexedPolyLine[7].setColor(SolidColor8.red, SolidColor8.green,
+ SolidColor8.blue, SolidColor8.alpha);
+}
+
+void
+IndexedPolyLineRender(rw::Matrix *transform, rw::uint32 transformFlags)
+{
+ rw::im3d::Transform(IndexedPolyLine, 8, transform, transformFlags);
+ rw::im3d::RenderIndexedPrimitive(rw::PRIMTYPEPOLYLINE, IndexedPolyLineIndices, 25);
+ rw::im3d::End();
+}
diff --git a/tools/im3d/trifan.cpp b/tools/im3d/trifan.cpp
new file mode 100644
index 0000000..3bef64b
--- /dev/null
+++ b/tools/im3d/trifan.cpp
@@ -0,0 +1,152 @@
+#include <rw.h>
+#include <skeleton.h>
+
+#include "im3d.h"
+
+float TriFanData[34][5] = {
+ { 0.000f, 0.000f, -1.000f, 0.500f, 0.500f},
+
+ { 0.000f, 1.000f, 0.000f, 0.500f, 1.000f},
+ { 0.383f, 0.924f, 0.000f, 0.691f, 0.962f},
+ { 0.707f, 0.707f, 0.000f, 0.854f, 0.854f},
+ { 0.924f, 0.383f, 0.000f, 0.962f, 0.691f},
+ { 1.000f, 0.000f, 0.000f, 1.000f, 0.500f},
+ { 0.924f, -0.383f, 0.000f, 0.962f, 0.309f},
+ { 0.707f, -0.707f, 0.000f, 0.854f, 0.146f},
+ { 0.383f, -0.924f, 0.000f, 0.691f, 0.038f},
+ { 0.000f, -1.000f, 0.000f, 0.500f, 0.000f},
+ {-0.383f, -0.924f, 0.000f, 0.309f, 0.038f},
+ {-0.707f, -0.707f, 0.000f, 0.146f, 0.146f},
+ {-0.924f, -0.383f, 0.000f, 0.038f, 0.309f},
+ {-1.000f, -0.000f, 0.000f, 0.000f, 0.500f},
+ {-0.924f, 0.383f, 0.000f, 0.038f, 0.691f},
+ {-0.707f, 0.707f, 0.000f, 0.146f, 0.854f},
+ {-0.383f, 0.924f, 0.000f, 0.309f, 0.962f},
+
+ { 0.000f, 1.000f, 0.000f, 0.500f, 1.000f},
+
+ {-0.383f, 0.924f, 0.000f, 0.309f, 0.962f},
+ {-0.707f, 0.707f, 0.000f, 0.146f, 0.854f},
+ {-0.924f, 0.383f, 0.000f, 0.038f, 0.691f},
+ {-1.000f, -0.000f, 0.000f, 0.000f, 0.500f},
+ {-0.924f, -0.383f, 0.000f, 0.038f, 0.309f},
+ {-0.707f, -0.707f, 0.000f, 0.146f, 0.146f},
+ {-0.383f, -0.924f, 0.000f, 0.309f, 0.038f},
+ { 0.000f, -1.000f, 0.000f, 0.500f, 0.000f},
+ { 0.383f, -0.924f, 0.000f, 0.691f, 0.038f},
+ { 0.707f, -0.707f, 0.000f, 0.854f, 0.146f},
+ { 0.924f, -0.383f, 0.000f, 0.962f, 0.309f},
+ { 1.000f, 0.000f, 0.000f, 1.000f, 0.500f},
+ { 0.924f, 0.383f, 0.000f, 0.962f, 0.691f},
+ { 0.707f, 0.707f, 0.000f, 0.854f, 0.854f},
+ { 0.383f, 0.924f, 0.000f, 0.691f, 0.962f},
+ { 0.000f, 1.000f, 0.000f, 0.500f, 1.000f}
+};
+
+float IndexedTriFanData[17][5] = {
+ /* top */
+ { 0.000f, 0.000f, -1.000f, 0.500f, 0.500f},
+ /* circle */
+ { 0.000f, 1.000f, 0.000f, 0.500f, 1.000f},
+ { 0.383f, 0.924f, 0.000f, 0.691f, 0.962f},
+ { 0.707f, 0.707f, 0.000f, 0.854f, 0.854f},
+ { 0.924f, 0.383f, 0.000f, 0.962f, 0.691f},
+ { 1.000f, 0.000f, 0.000f, 1.000f, 0.500f},
+ { 0.924f, -0.383f, 0.000f, 0.962f, 0.309f},
+ { 0.707f, -0.707f, 0.000f, 0.854f, 0.146f},
+ { 0.383f, -0.924f, 0.000f, 0.691f, 0.038f},
+ { 0.000f, -1.000f, 0.000f, 0.500f, 0.000f},
+ {-0.383f, -0.924f, 0.000f, 0.309f, 0.038f},
+ {-0.707f, -0.707f, 0.000f, 0.146f, 0.146f},
+ {-0.924f, -0.383f, 0.000f, 0.038f, 0.309f},
+ {-1.000f, -0.000f, 0.000f, 0.000f, 0.500f},
+ {-0.924f, 0.383f, 0.000f, 0.038f, 0.691f},
+ {-0.707f, 0.707f, 0.000f, 0.146f, 0.854f},
+ {-0.383f, 0.924f, 0.000f, 0.309f, 0.962f}
+};
+
+rw::uint16 IndexedTriFanIndices[34] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1,
+ 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1
+};
+
+rw::RWDEVICE::Im3DVertex TriFan[34];
+rw::RWDEVICE::Im3DVertex IndexedTriFan[17];
+
+
+void
+TriFanCreate(void)
+{
+ for(int i = 0; i < 34; i++){
+ TriFan[i].setX(TriFanData[i][0]);
+ TriFan[i].setY(TriFanData[i][1]);
+ TriFan[i].setZ(TriFanData[i][2]);
+ TriFan[i].setU(TriFanData[i][3]);
+ TriFan[i].setV(TriFanData[i][4]);
+ }
+}
+
+void
+TriFanSetColor(bool white)
+{
+ rw::RGBA SolidColor1 = SolidYellow;
+ rw::RGBA SolidColor2 = SolidBlue;
+
+ if(white){
+ SolidColor1 = SolidWhite;
+ SolidColor2 = SolidWhite;
+ }
+
+ TriFan[0].setColor(SolidColor1.red, SolidColor1.green,
+ SolidColor1.blue, SolidColor1.alpha);
+ for(int i = 1; i < 34; i++)
+ TriFan[i].setColor(SolidColor2.red, SolidColor2.green,
+ SolidColor2.blue, SolidColor2.alpha);
+}
+
+void
+TriFanRender(rw::Matrix *transform, rw::uint32 transformFlags)
+{
+ rw::im3d::Transform(TriFan, 34, transform, transformFlags);
+ rw::im3d::RenderPrimitive(rw::PRIMTYPETRIFAN);
+ rw::im3d::End();
+}
+
+
+void
+IndexedTriFanCreate(void)
+{
+ for(int i = 0; i < 17; i++){
+ IndexedTriFan[i].setX(IndexedTriFanData[i][0]);
+ IndexedTriFan[i].setY(IndexedTriFanData[i][1]);
+ IndexedTriFan[i].setZ(IndexedTriFanData[i][2]);
+ IndexedTriFan[i].setU(IndexedTriFanData[i][3]);
+ IndexedTriFan[i].setV(IndexedTriFanData[i][4]);
+ }
+}
+
+void
+IndexedTriFanSetColor(bool white)
+{
+ rw::RGBA SolidColor1 = SolidGreen;
+ rw::RGBA SolidColor2 = SolidBlack;
+
+ if(white){
+ SolidColor1 = SolidWhite;
+ SolidColor2 = SolidWhite;
+ }
+
+ IndexedTriFan[0].setColor(SolidColor1.red, SolidColor1.green,
+ SolidColor1.blue, SolidColor1.alpha);
+ for(int i = 1; i < 17; i++)
+ IndexedTriFan[i].setColor(SolidColor2.red, SolidColor2.green,
+ SolidColor2.blue, SolidColor2.alpha);
+}
+
+void
+IndexedTriFanRender(rw::Matrix *transform, rw::uint32 transformFlags)
+{
+ rw::im3d::Transform(IndexedTriFan, 17, transform, transformFlags);
+ rw::im3d::RenderIndexedPrimitive(rw::PRIMTYPETRIFAN, IndexedTriFanIndices, 34);
+ rw::im3d::End();
+}
diff --git a/tools/im3d/trilist.cpp b/tools/im3d/trilist.cpp
new file mode 100644
index 0000000..6fa4014
--- /dev/null
+++ b/tools/im3d/trilist.cpp
@@ -0,0 +1,202 @@
+#include <rw.h>
+#include <skeleton.h>
+
+#include "im3d.h"
+
+float TriListData[36][5] = {
+ /* front */
+ { 1.000f, 1.000f, 1.000f, 1.000f, 1.000f},
+ {-1.000f, -1.000f, 1.000f, 0.000f, 0.000f},
+ { 1.000f, -1.000f, 1.000f, 1.000f, 0.000f},
+ { 1.000f, 1.000f, 1.000f, 1.000f, 1.000f},
+ {-1.000f, 1.000f, 1.000f, 0.000f, 1.000f},
+ {-1.000f, -1.000f, 1.000f, 0.000f, 0.000f},
+ /* back */
+ {-1.000f, -1.000f, -1.000f, 1.000f, 1.000f},
+ {-1.000f, 1.000f, -1.000f, 1.000f, 0.000f},
+ { 1.000f, 1.000f, -1.000f, 0.000f, 0.000f},
+ {-1.000f, -1.000f, -1.000f, 1.000f, 1.000f},
+ { 1.000f, 1.000f, -1.000f, 0.000f, 0.000f},
+ { 1.000f, -1.000f, -1.000f, 0.000f, 1.000f},
+ /* top */
+ { 1.000f, 1.000f, 1.000f, 1.000f, 1.000f},
+ { 1.000f, 1.000f, -1.000f, 1.000f, 0.000f},
+ {-1.000f, 1.000f, -1.000f, 0.000f, 0.000f},
+ { 1.000f, 1.000f, 1.000f, 1.000f, 1.000f},
+ {-1.000f, 1.000f, -1.000f, 0.000f, 0.000f},
+ {-1.000f, 1.000f, 1.000f, 0.000f, 1.000f},
+ /* bottom */
+ {-1.000f, -1.000f, -1.000f, 1.000f, 1.000f},
+ { 1.000f, -1.000f, 1.000f, 0.000f, 0.000f},
+ {-1.000f, -1.000f, 1.000f, 1.000f, 0.000f},
+ {-1.000f, -1.000f, -1.000f, 1.000f, 1.000f},
+ { 1.000f, -1.000f, -1.000f, 0.000f, 1.000f},
+ { 1.000f, -1.000f, 1.000f, 0.000f, 0.000f},
+ /* left */
+ {-1.000f, -1.000f, -1.000f, 1.000f, 1.000f},
+ {-1.000f, 1.000f, 1.000f, 0.000f, 0.000f},
+ {-1.000f, 1.000f, -1.000f, 1.000f, 0.000f},
+ {-1.000f, -1.000f, -1.000f, 1.000f, 1.000f},
+ {-1.000f, -1.000f, 1.000f, 0.000f, 1.000f},
+ {-1.000f, 1.000f, 1.000f, 0.000f, 0.000f},
+ /* right */
+ { 1.000f, 1.000f, 1.000f, 1.000f, 1.000f},
+ { 1.000f, -1.000f, 1.000f, 1.000f, 0.000f},
+ { 1.000f, -1.000f, -1.000f, 0.000f, 0.000f},
+ { 1.000f, 1.000f, 1.000f, 1.000f, 1.000f},
+ { 1.000f, -1.000f, -1.000f, 0.000f, 0.000f},
+ { 1.000f, 1.000f, -1.000f, 0.000f, 1.000f}
+};
+
+float IndexedTriListData[8][5] = {
+ { 1.000f, 1.000f, 1.000f, 1.000f, 0.000f},
+ {-1.000f, 1.000f, 1.000f, 1.000f, 1.000f},
+ {-1.000f, -1.000f, 1.000f, 0.500f, 1.000f},
+ { 1.000f, -1.000f, 1.000f, 0.500f, 0.000f},
+
+ { 1.000f, 1.000f, -1.000f, 0.500f, 0.000f},
+ {-1.000f, 1.000f, -1.000f, 0.500f, 1.000f},
+ {-1.000f, -1.000f, -1.000f, 0.000f, 1.000f},
+ { 1.000f, -1.000f, -1.000f, 0.000f, 0.000f}
+};
+
+rw::uint16 IndexedTriListIndices[36] = {
+ /* front */
+ 0, 1, 3, 1, 2, 3,
+ /* back */
+ 7, 5, 4, 5, 7, 6,
+ /* left */
+ 6, 2, 1, 1, 5, 6,
+ /* right */
+ 0, 3, 4, 4, 3, 7,
+ /* top */
+ 1, 0, 4, 1, 4, 5,
+ /* bottom */
+ 2, 6, 3, 6, 7, 3
+};
+
+rw::RWDEVICE::Im3DVertex TriList[36];
+rw::RWDEVICE::Im3DVertex IndexedTriList[8];
+
+
+void
+TriListCreate(void)
+{
+ for(int i = 0; i < 36; i++){
+ TriList[i].setX(TriListData[i][0]);
+ TriList[i].setY(TriListData[i][1]);
+ TriList[i].setZ(TriListData[i][2]);
+ TriList[i].setU(TriListData[i][3]);
+ TriList[i].setV(TriListData[i][4]);
+ }
+}
+
+void
+TriListSetColor(bool white)
+{
+ int i;
+ rw::RGBA SolidColor1 = SolidRed;
+ rw::RGBA SolidColor2 = SolidBlue;
+ rw::RGBA SolidColor3 = SolidGreen;
+ rw::RGBA SolidColor4 = SolidYellow;
+ rw::RGBA SolidColor5 = SolidCyan;
+ rw::RGBA SolidColor6 = SolidPurple;
+
+ if(white){
+ SolidColor1 = SolidWhite;
+ SolidColor2 = SolidWhite;
+ SolidColor3 = SolidWhite;
+ SolidColor4 = SolidWhite;
+ SolidColor5 = SolidWhite;
+ SolidColor6 = SolidWhite;
+ }
+
+ for(i = 0; i < 6; i++)
+ TriList[i].setColor(SolidColor1.red, SolidColor1.green,
+ SolidColor1.blue, SolidColor1.alpha);
+ for(; i < 12; i++)
+ TriList[i].setColor(SolidColor2.red, SolidColor2.green,
+ SolidColor2.blue, SolidColor2.alpha);
+ for(; i < 18; i++)
+ TriList[i].setColor(SolidColor3.red, SolidColor3.green,
+ SolidColor3.blue, SolidColor3.alpha);
+ for(; i < 24; i++)
+ TriList[i].setColor(SolidColor4.red, SolidColor4.green,
+ SolidColor4.blue, SolidColor4.alpha);
+ for(; i < 30; i++)
+ TriList[i].setColor(SolidColor5.red, SolidColor5.green,
+ SolidColor5.blue, SolidColor5.alpha);
+ for(; i < 36; i++)
+ TriList[i].setColor(SolidColor6.red, SolidColor6.green,
+ SolidColor6.blue, SolidColor6.alpha);
+}
+
+void
+TriListRender(rw::Matrix *transform, rw::uint32 transformFlags)
+{
+ rw::im3d::Transform(TriList, 36, transform, transformFlags);
+ rw::im3d::RenderPrimitive(rw::PRIMTYPETRILIST);
+ rw::im3d::End();
+}
+
+
+void
+IndexedTriListCreate(void)
+{
+ for(int i = 0; i < 8; i++){
+ IndexedTriList[i].setX(IndexedTriListData[i][0]);
+ IndexedTriList[i].setY(IndexedTriListData[i][1]);
+ IndexedTriList[i].setZ(IndexedTriListData[i][2]);
+ IndexedTriList[i].setU(IndexedTriListData[i][3]);
+ IndexedTriList[i].setV(IndexedTriListData[i][4]);
+ }
+}
+
+void
+IndexedTriListSetColor(bool white)
+{
+ rw::RGBA SolidColor1 = SolidRed;
+ rw::RGBA SolidColor2 = SolidYellow;
+ rw::RGBA SolidColor3 = SolidBlack;
+ rw::RGBA SolidColor4 = SolidPurple;
+ rw::RGBA SolidColor5 = SolidGreen;
+ rw::RGBA SolidColor6 = SolidCyan;
+ rw::RGBA SolidColor7 = SolidBlue;
+ rw::RGBA SolidColor8 = SolidWhite;
+
+ if(white){
+ SolidColor1 = SolidWhite;
+ SolidColor2 = SolidWhite;
+ SolidColor3 = SolidWhite;
+ SolidColor4 = SolidWhite;
+ SolidColor5 = SolidWhite;
+ SolidColor6 = SolidWhite;
+ SolidColor7 = SolidWhite;
+ SolidColor8 = SolidWhite;
+ }
+
+ IndexedTriList[0].setColor(SolidColor1.red, SolidColor1.green,
+ SolidColor1.blue, SolidColor1.alpha);
+ IndexedTriList[1].setColor(SolidColor2.red, SolidColor2.green,
+ SolidColor2.blue, SolidColor2.alpha);
+ IndexedTriList[2].setColor(SolidColor3.red, SolidColor3.green,
+ SolidColor3.blue, SolidColor3.alpha);
+ IndexedTriList[3].setColor(SolidColor4.red, SolidColor4.green,
+ SolidColor4.blue, SolidColor4.alpha);
+ IndexedTriList[4].setColor(SolidColor5.red, SolidColor5.green,
+ SolidColor5.blue, SolidColor5.alpha);
+ IndexedTriList[5].setColor(SolidColor6.red, SolidColor6.green,
+ SolidColor6.blue, SolidColor6.alpha);
+ IndexedTriList[6].setColor(SolidColor7.red, SolidColor7.green,
+ SolidColor7.blue, SolidColor7.alpha);
+ IndexedTriList[7].setColor(SolidColor8.red, SolidColor8.green,
+ SolidColor8.blue, SolidColor8.alpha);
+}
+
+void
+IndexedTriListRender(rw::Matrix *transform, rw::uint32 transformFlags)
+{
+ rw::im3d::Transform(IndexedTriList, 8, transform, transformFlags);
+ rw::im3d::RenderIndexedPrimitive(rw::PRIMTYPETRILIST, IndexedTriListIndices, 36);
+ rw::im3d::End();
+}
diff --git a/tools/im3d/tristrip.cpp b/tools/im3d/tristrip.cpp
new file mode 100644
index 0000000..93c1496
--- /dev/null
+++ b/tools/im3d/tristrip.cpp
@@ -0,0 +1,169 @@
+#include <rw.h>
+#include <skeleton.h>
+
+#include "im3d.h"
+
+float TriStripData[36][5] = {
+ { 0.000f, 1.000f, -1.000f, 0.000f, 0.000f},
+ { 0.000f, 1.000f, 1.000f, 0.000f, 1.000f},
+
+ { 0.707f, 0.707f, -1.000f, 0.125f, 0.000f},
+ { 0.707f, 0.707f, 1.000f, 0.125f, 1.000f},
+
+ { 1.000f, 0.000f, -1.000f, 0.250f, 0.000f},
+ { 1.000f, 0.000f, 1.000f, 0.250f, 1.000f},
+
+ { 0.707f, -0.707f, -1.000f, 0.375f, 0.000f},
+ { 0.707f, -0.707f, 1.000f, 0.375f, 1.000f},
+
+ { 0.000f, -1.000f, -1.000f, 0.500f, 0.000f},
+ { 0.000f, -1.000f, 1.000f, 0.500f, 1.000f},
+
+ {-0.707f, -0.707f, -1.000f, 0.625f, 0.000f},
+ {-0.707f, -0.707f, 1.000f, 0.625f, 1.000f},
+
+ {-1.000f, -0.000f, -1.000f, 0.750f, 0.000f},
+ {-1.000f, -0.000f, 1.000f, 0.750f, 1.000f},
+
+ {-0.707f, 0.707f, -1.000f, 0.875f, 0.000f},
+ {-0.707f, 0.707f, 1.000f, 0.875f, 1.000f},
+
+ { 0.000f, 1.000f, -1.000f, 1.000f, 0.000f},
+ { 0.000f, 1.000f, 1.000f, 1.000f, 1.000f},
+
+ { 0.000f, 1.000f, 1.000f, 0.000f, 0.000f},
+ { 0.000f, 1.000f, -1.000f, 0.000f, 1.000f},
+
+ { 0.707f, 0.707f, 1.000f, 0.125f, 0.000f},
+ { 0.707f, 0.707f, -1.000f, 0.125f, 1.000f},
+
+ { 1.000f, 0.000f, 1.000f, 0.250f, 0.000f},
+ { 1.000f, 0.000f, -1.000f, 0.250f, 1.000f},
+
+ { 0.707f, -0.707f, 1.000f, 0.375f, 0.000f},
+ { 0.707f, -0.707f, -1.000f, 0.375f, 1.000f},
+
+ { 0.000f, -1.000f, 1.000f, 0.500f, 0.000f},
+ { 0.000f, -1.000f, -1.000f, 0.500f, 1.000f},
+
+ {-0.707f, -0.707f, 1.000f, 0.625f, 0.000f},
+ {-0.707f, -0.707f, -1.000f, 0.625f, 1.000f},
+
+ {-1.000f, -0.000f, 1.000f, 0.750f, 0.000f},
+ {-1.000f, -0.000f, -1.000f, 0.750f, 1.000f},
+
+ {-0.707f, 0.707f, 1.000f, 0.875f, 0.000f},
+ {-0.707f, 0.707f, -1.000f, 0.875f, 1.000f},
+
+ { 0.000f, 1.000f, 1.000f, 1.000f, 0.000f},
+ { 0.000f, 1.000f, -1.000f, 1.000f, 1.000f}
+};
+
+float IndexedTriStripData[16][5] = {
+ { 0.000f, 1.000f, 1.000f, 0.000f, 0.000f},
+ { 0.707f, 0.707f, 1.000f, 0.250f, 0.000f},
+ { 1.000f, 0.000f, 1.000f, 0.500f, 0.000f},
+ { 0.707f, -0.707f, 1.000f, 0.750f, 0.000f},
+ { 0.000f, -1.000f, 1.000f, 1.000f, 0.000f},
+ {-0.707f, -0.707f, 1.000f, 0.750f, 0.000f},
+ {-1.000f, -0.000f, 1.000f, 0.500f, 0.000f},
+ {-0.707f, 0.707f, 1.000f, 0.250f, 0.000f},
+
+ { 0.000f, 1.000f, -1.000f, 0.000f, 1.000f},
+ { 0.707f, 0.707f, -1.000f, 0.250f, 1.000f},
+ { 1.000f, 0.000f, -1.000f, 0.500f, 1.000f},
+ { 0.707f, -0.707f, -1.000f, 0.750f, 1.000f},
+ { 0.000f, -1.000f, -1.000f, 1.000f, 1.000f},
+ {-0.707f, -0.707f, -1.000f, 0.750f, 1.000f},
+ {-1.000f, -0.000f, -1.000f, 0.500f, 1.000f},
+ {-0.707f, 0.707f, -1.000f, 0.250f, 1.000f},
+};
+
+rw::uint16 IndexedTriStripIndices[36] = {
+ 0, 8, 1, 9, 2, 10, 3, 11, 4, 12, 5, 13, 6, 14, 7, 15, 0, 8,
+ 8, 0, 9, 1, 10, 2, 11, 3, 12, 4, 13, 5, 14, 6, 15, 7, 8, 0
+};
+
+rw::RWDEVICE::Im3DVertex TriStrip[36];
+rw::RWDEVICE::Im3DVertex IndexedTriStrip[16];
+
+
+void
+TriStripCreate(void)
+{
+ for(int i = 0; i < 36; i++){
+ TriStrip[i].setX(TriStripData[i][0]);
+ TriStrip[i].setY(TriStripData[i][1]);
+ TriStrip[i].setZ(TriStripData[i][2]);
+ TriStrip[i].setU(TriStripData[i][3]);
+ TriStrip[i].setV(TriStripData[i][4]);
+ }
+}
+
+void
+TriStripSetColor(bool white)
+{
+ rw::RGBA SolidColor1 = SolidRed;
+ rw::RGBA SolidColor2 = SolidYellow;
+
+ if(white){
+ SolidColor1 = SolidWhite;
+ SolidColor2 = SolidWhite;
+ }
+
+ for(int i = 0; i < 36; i += 2){
+ TriStrip[i].setColor(SolidColor1.red, SolidColor1.green,
+ SolidColor1.blue, SolidColor1.alpha);
+ TriStrip[i+1].setColor(SolidColor2.red, SolidColor2.green,
+ SolidColor2.blue, SolidColor2.alpha);
+ }
+}
+
+void
+TriStripRender(rw::Matrix *transform, rw::uint32 transformFlags)
+{
+ rw::im3d::Transform(TriStrip, 36, transform, transformFlags);
+ rw::im3d::RenderPrimitive(rw::PRIMTYPETRISTRIP);
+ rw::im3d::End();
+}
+
+
+void
+IndexedTriStripCreate(void)
+{
+ for(int i = 0; i < 16; i++){
+ IndexedTriStrip[i].setX(IndexedTriStripData[i][0]);
+ IndexedTriStrip[i].setY(IndexedTriStripData[i][1]);
+ IndexedTriStrip[i].setZ(IndexedTriStripData[i][2]);
+ IndexedTriStrip[i].setU(IndexedTriStripData[i][3]);
+ IndexedTriStrip[i].setV(IndexedTriStripData[i][4]);
+ }
+}
+
+void
+IndexedTriStripSetColor(bool white)
+{
+ int i;
+ rw::RGBA SolidColor1 = SolidBlue;
+ rw::RGBA SolidColor2 = SolidGreen;
+
+ if(white){
+ SolidColor1 = SolidWhite;
+ SolidColor2 = SolidWhite;
+ }
+
+ for(i = 0; i < 8; i++)
+ IndexedTriStrip[i].setColor(SolidColor1.red, SolidColor1.green,
+ SolidColor1.blue, SolidColor1.alpha);
+ for(; i < 16; i++)
+ IndexedTriStrip[i].setColor(SolidColor2.red, SolidColor2.green,
+ SolidColor2.blue, SolidColor2.alpha);
+}
+
+void
+IndexedTriStripRender(rw::Matrix *transform, rw::uint32 transformFlags)
+{
+ rw::im3d::Transform(IndexedTriStrip, 16, transform, transformFlags);
+ rw::im3d::RenderIndexedPrimitive(rw::PRIMTYPETRISTRIP, IndexedTriStripIndices, 36);
+ rw::im3d::End();
+}
diff --git a/tools/imguitest/CMakeLists.txt b/tools/imguitest/CMakeLists.txt
new file mode 100644
index 0000000..4f2e31d
--- /dev/null
+++ b/tools/imguitest/CMakeLists.txt
@@ -0,0 +1,17 @@
+add_executable(imguitest WIN32
+ main.cpp
+)
+
+target_link_libraries(imguitest
+ PUBLIC
+ librw::skeleton
+ librw::librw
+)
+
+if(LIBRW_INSTALL)
+ install(TARGETS imguitest
+ RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
+ )
+endif()
+
+librw_platform_target(imguitest INSTALL)
diff --git a/tools/imguitest/main.cpp b/tools/imguitest/main.cpp
new file mode 100644
index 0000000..749c06f
--- /dev/null
+++ b/tools/imguitest/main.cpp
@@ -0,0 +1,175 @@
+#include <rw.h>
+#include <skeleton.h>
+#include <assert.h>
+
+rw::V3d zero = { 0.0f, 0.0f, 0.0f };
+struct SceneGlobals {
+ rw::World *world;
+ rw::Camera *camera;
+} Scene;
+rw::EngineOpenParams engineOpenParams;
+
+void
+Init(void)
+{
+ sk::globals.windowtitle = "ImGui test";
+ sk::globals.width = 1280;
+ sk::globals.height = 800;
+ 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;
+}
+
+bool
+InitRW(void)
+{
+// rw::platform = rw::PLATFORM_D3D8;
+ if(!sk::InitRW())
+ return false;
+
+ Scene.world = rw::World::create();
+
+ rw::Light *ambient = rw::Light::create(rw::Light::AMBIENT);
+ ambient->setColor(0.2f, 0.2f, 0.2f);
+ 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);
+
+ Scene.camera = sk::CameraCreate(sk::globals.width, sk::globals.height, 1);
+ Scene.world->addCamera(Scene.camera);
+
+ ImGui_ImplRW_Init();
+ ImGui::StyleColorsClassic();
+
+ return true;
+}
+
+void
+Draw(float timeDelta)
+{
+ static bool show_demo_window = true;
+ static bool show_another_window = false;
+ static ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
+
+ rw::RGBA clearcol = rw::makeRGBA(clear_color.x*255, clear_color.y*255, clear_color.z*255, clear_color.w*255);
+ Scene.camera->clear(&clearcol, rw::Camera::CLEARIMAGE|rw::Camera::CLEARZ);
+ Scene.camera->beginUpdate();
+
+ ImGui_ImplRW_NewFrame(timeDelta);
+
+ // 1. Show a simple window.
+ // Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets automatically appears in a window called "Debug".
+ {
+ static float f = 0.0f;
+ ImGui::Text("Hello, world!"); // Some text (you can use a format string too)
+ ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float as a slider from 0.0f to 1.0f
+ ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats as a color
+ if(ImGui::Button("Demo Window")) // Use buttons to toggle our bools. We could use Checkbox() as well.
+ show_demo_window ^= 1;
+ if(ImGui::Button("Another Window"))
+ show_another_window ^= 1;
+ ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
+ }
+
+ // 2. Show another simple window. In most cases you will use an explicit Begin/End pair to name the window.
+ if(show_another_window){
+ ImGui::Begin("Another Window", &show_another_window);
+ ImGui::Text("Hello from another window!");
+ ImGui::End();
+ }
+
+ // 3. Show the ImGui demo window. Most of the sample code is in ImGui::ShowDemoWindow().
+ if(show_demo_window){
+ ImGui::SetNextWindowPos(ImVec2(650, 20), ImGuiCond_FirstUseEver); // Normally user code doesn't need/want to call this because positions are saved in .ini file anyway. Here we just want to make the demo initial state a bit more friendly!
+ ImGui::ShowDemoWindow(&show_demo_window);
+ }
+
+
+ ImGui::EndFrame();
+ ImGui::Render();
+
+ ImGui_ImplRW_RenderDrawLists(ImGui::GetDrawData());
+
+ Scene.camera->endUpdate();
+ Scene.camera->showRaster(0);
+}
+
+
+void
+KeyUp(int key)
+{
+}
+
+void
+KeyDown(int key)
+{
+ switch(key){
+ case sk::KEY_ESC:
+ sk::globals.quit = 1;
+ break;
+ }
+}
+
+sk::EventStatus
+AppEventHandler(sk::Event e, void *param)
+{
+ using namespace sk;
+ Rect *r;
+
+ ImGuiEventHandler(e, param);
+
+ 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 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;
+ // TODO: set aspect ratio
+ if(Scene.camera)
+ sk::CameraSize(Scene.camera, r);
+ break;
+ case IDLE:
+ Draw(*(float*)param);
+ return EVENTPROCESSED;
+ }
+ return sk::EVENTNOTPROCESSED;
+}
diff --git a/tools/lights/CMakeLists.txt b/tools/lights/CMakeLists.txt
new file mode 100644
index 0000000..c51a2e3
--- /dev/null
+++ b/tools/lights/CMakeLists.txt
@@ -0,0 +1,18 @@
+add_executable(lights WIN32
+ main.cpp
+ lights.cpp
+)
+
+target_link_libraries(lights
+ PRIVATE
+ librw::skeleton
+ librw::librw
+)
+
+add_custom_command(
+ TARGET lights POST_BUILD
+ COMMAND "${CMAKE_COMMAND}" -E make_directory "$<TARGET_FILE_DIR:lights>/files"
+ COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/checker.dff" "$<TARGET_FILE_DIR:lights>"
+)
+
+librw_platform_target(lights)
diff --git a/tools/lights/checker.dff b/tools/lights/checker.dff
new file mode 100644
index 0000000..f955e8c
--- /dev/null
+++ b/tools/lights/checker.dff
Binary files differ
diff --git a/tools/lights/lights.cpp b/tools/lights/lights.cpp
new file mode 100644
index 0000000..345df9f
--- /dev/null
+++ b/tools/lights/lights.cpp
@@ -0,0 +1,463 @@
+#include <rw.h>
+#include <skeleton.h>
+
+#include "main.h"
+
+rw::Light *BaseAmbientLight;
+bool BaseAmbientLightOn;
+
+rw::Light *CurrentLight;
+rw::Light *AmbientLight;
+rw::Light *PointLight;
+rw::Light *DirectLight;
+rw::Light *SpotLight;
+rw::Light *SpotSoftLight;
+
+float LightRadius = 100.0f;
+float LightConeAngle = 45.0f;
+rw::RGBAf LightColor = { 1.0f, 1.0f, 1.0f, 1.0f };
+
+rw::RGBA LightSolidColor = { 255, 255, 0, 255 };
+bool LightOn = true;
+bool LightDrawOn = true;
+rw::V3d LightPos = {0.0f, 0.0f, 75.0f};
+rw::int32 LightTypeIndex = 1;
+
+rw::BBox RoomBBox;
+
+rw::Light*
+CreateBaseAmbientLight(void)
+{
+ rw::Light *light = rw::Light::create(rw::Light::AMBIENT);
+ assert(light);
+ light->setColor(0.5f, 0.5f, 0.5f);
+ return light;
+}
+
+rw::Light*
+CreateAmbientLight(void)
+{
+ return rw::Light::create(rw::Light::AMBIENT);
+}
+
+rw::Light*
+CreateDirectLight(void)
+{
+ rw::Light *light = rw::Light::create(rw::Light::DIRECTIONAL);
+ assert(light);
+ rw::Frame *frame = rw::Frame::create();
+ assert(frame);
+ frame->rotate(&Xaxis, 45.0f, rw::COMBINEREPLACE);
+ rw::V3d pos = LightPos;
+ frame->translate(&pos, rw::COMBINEPOSTCONCAT);
+ light->setFrame(frame);
+ return light;
+}
+
+rw::Light*
+CreatePointLight(void)
+{
+ rw::Light *light = rw::Light::create(rw::Light::POINT);
+ assert(light);
+ light->radius = LightRadius;
+ rw::Frame *frame = rw::Frame::create();
+ assert(frame);
+ rw::V3d pos = LightPos;
+ frame->translate(&pos, rw::COMBINEREPLACE);
+ light->setFrame(frame);
+ return light;
+}
+
+rw::Light*
+CreateSpotLight(void)
+{
+ rw::Light *light = rw::Light::create(rw::Light::SPOT);
+ assert(light);
+ light->radius = LightRadius;
+ light->setAngle(LightConeAngle/180.0f*M_PI);
+ rw::Frame *frame = rw::Frame::create();
+ assert(frame);
+ frame->rotate(&Xaxis, 45.0f, rw::COMBINEREPLACE);
+ rw::V3d pos = LightPos;
+ frame->translate(&pos, rw::COMBINEPOSTCONCAT);
+ light->setFrame(frame);
+ return light;
+}
+
+rw::Light*
+CreateSpotSoftLight(void)
+{
+ rw::Light *light = rw::Light::create(rw::Light::SOFTSPOT);
+ assert(light);
+ light->radius = LightRadius;
+ light->setAngle(LightConeAngle/180.0f*M_PI);
+ rw::Frame *frame = rw::Frame::create();
+ assert(frame);
+ frame->rotate(&Xaxis, 45.0f, rw::COMBINEREPLACE);
+ rw::V3d pos = LightPos;
+ frame->translate(&pos, rw::COMBINEPOSTCONCAT);
+ light->setFrame(frame);
+ return light;
+}
+
+void
+DestroyLight(rw::Light **light)
+{
+ if(*light == nil)
+ return;
+ rw::World *world = (*light)->world;
+ if(world)
+ world->removeLight(*light);
+ rw::Frame *frame = (*light)->getFrame();
+ if(frame){
+ (*light)->setFrame(nil);
+ frame->destroy();
+ }
+
+ (*light)->destroy();
+ *light = nil;
+}
+
+void
+LightsDestroy(void)
+{
+ DestroyLight(&SpotSoftLight);
+ DestroyLight(&SpotLight);
+ DestroyLight(&PointLight);
+ DestroyLight(&DirectLight);
+ DestroyLight(&AmbientLight);
+ DestroyLight(&BaseAmbientLight);
+}
+
+void
+LightsUpdate(void)
+{
+ static rw::int32 oldLightTypeIndex = -1;
+
+ // Switch to a different light
+ if((LightOn && oldLightTypeIndex != LightTypeIndex) || CurrentLight == nil){
+ oldLightTypeIndex = LightTypeIndex;
+
+ // remove first
+ if(CurrentLight)
+ CurrentLight->world->removeLight(CurrentLight);
+
+ switch(LightTypeIndex){
+ case 0: CurrentLight = AmbientLight; break;
+ case 1: CurrentLight = PointLight; break;
+ case 2: CurrentLight = DirectLight; break;
+ case 3: CurrentLight = SpotLight; break;
+ case 4: CurrentLight = SpotSoftLight; break;
+ }
+ World->addLight(CurrentLight);
+ }
+
+ if(CurrentLight){
+ CurrentLight->setColor(LightColor.red, LightColor.green, LightColor.blue);
+ CurrentLight->radius = LightRadius;
+ CurrentLight->setAngle(LightConeAngle / 180.0f * M_PI);
+ }
+
+ // Remove light from world if not used
+ if(!LightOn && CurrentLight){
+ CurrentLight->world->removeLight(CurrentLight);
+ CurrentLight = nil;
+ }
+}
+
+#define POINT_LIGHT_RADIUS_FACTOR 0.05f
+
+void
+DrawPointLight(void)
+{
+ enum { NUMVERTS = 50 };
+ rw::RWDEVICE::Im3DVertex shape[NUMVERTS];
+ rw::int32 i;
+ rw::V3d point;
+
+ rw::V3d *pos = &CurrentLight->getFrame()->getLTM()->pos;
+ for(i = 0; i < NUMVERTS; i++){
+ point.x = pos->x +
+ cosf(i/(NUMVERTS/2.0f) * M_PI) * LightRadius * POINT_LIGHT_RADIUS_FACTOR;
+ point.y = pos->y +
+ sinf(i/(NUMVERTS/2.0f) * M_PI) * LightRadius * POINT_LIGHT_RADIUS_FACTOR;
+ point.z = pos->z;
+
+ shape[i].setColor(LightSolidColor.red, LightSolidColor.green,
+ LightSolidColor.blue, LightSolidColor.alpha);
+ shape[i].setX(point.x);
+ shape[i].setY(point.y);
+ shape[i].setZ(point.z);
+ }
+
+ rw::im3d::Transform(shape, NUMVERTS, nil, rw::im3d::ALLOPAQUE);
+ rw::im3d::RenderPrimitive(rw::PRIMTYPEPOLYLINE);
+ rw::im3d::RenderLine(NUMVERTS-1, 0);
+ rw::im3d::End();
+}
+
+void
+DrawCone(float coneAngle, float coneSize, float coneRatio)
+{
+ enum { NUMVERTS = 10 };
+ rw::RWDEVICE::Im3DVertex shape[NUMVERTS+1];
+ rw::int16 indices[NUMVERTS*3];
+ rw::int32 i;
+
+ rw::Matrix *matrix = CurrentLight->getFrame()->getLTM();
+ rw::V3d *pos = &matrix->pos;
+
+ // cone
+ for(i = 1; i < NUMVERTS+1; i++){
+ float cosValue = cosf(i/(NUMVERTS/2.0f) * M_PI) *
+ sinf(coneAngle/180.0f*M_PI);
+ float sinValue = sinf(i/(NUMVERTS/2.0f) * M_PI) *
+ sinf(coneAngle/180.0f*M_PI);
+
+ float coneAngleD = cosf(coneAngle/180.0f*M_PI);
+
+ rw::V3d up = rw::scale(matrix->up, sinValue*coneSize);
+ rw::V3d right = rw::scale(matrix->right, cosValue*coneSize);
+ rw::V3d at = rw::scale(matrix->at, coneAngleD*coneSize*coneRatio);
+
+ shape[i].setX(pos->x + at.x + up.x + right.x);
+ shape[i].setY(pos->y + at.y + up.y + right.y);
+ shape[i].setZ(pos->z + at.z + up.z + right.z);
+ }
+
+ for(i = 0; i < NUMVERTS; i++){
+ indices[i*3 + 0] = 0;
+ indices[i*3 + 1] = i+2;
+ indices[i*3 + 2] = i+1;
+ }
+ indices[NUMVERTS*3-2] = 1;
+
+ for(i = 0; i < NUMVERTS+1; i++)
+ shape[i].setColor(LightSolidColor.red, LightSolidColor.green,
+ LightSolidColor.blue, 128);
+
+ shape[0].setX(pos->x);
+ shape[0].setY(pos->y);
+ shape[0].setZ(pos->z);
+
+ rw::SetRenderState(rw::VERTEXALPHA, 1);
+ rw::SetRenderState(rw::SRCBLEND, rw::BLENDSRCALPHA);
+ rw::SetRenderState(rw::DESTBLEND, rw::BLENDINVSRCALPHA);
+
+ rw::im3d::Transform(shape, NUMVERTS+1, nil, 0);
+ rw::im3d::RenderPrimitive(rw::PRIMTYPETRIFAN);
+ rw::im3d::RenderTriangle(0, NUMVERTS, 1);
+ rw::im3d::RenderIndexedPrimitive(rw::PRIMTYPETRILIST, indices, NUMVERTS*3);
+ rw::im3d::End();
+
+
+ for(i = 0; i < NUMVERTS+1; i++)
+ shape[i].setColor(LightSolidColor.red, LightSolidColor.green,
+ LightSolidColor.blue, 255);
+
+ float coneAngleD = cosf(coneAngle/180.0f*M_PI);
+ rw::V3d at = rw::scale(matrix->at, coneAngleD*coneSize*coneRatio);
+ shape[0].setX(pos->x + at.x);
+ shape[0].setY(pos->y + at.y);
+ shape[0].setZ(pos->z + at.z);
+
+ rw::im3d::Transform(shape, NUMVERTS+1, nil, rw::im3d::ALLOPAQUE);
+ if(coneRatio > 0.0f){
+ rw::im3d::RenderPrimitive(rw::PRIMTYPETRIFAN);
+ rw::im3d::RenderTriangle(0, NUMVERTS, 1);
+ }else
+ rw::im3d::RenderIndexedPrimitive(rw::PRIMTYPETRIFAN, indices, NUMVERTS*3);
+ rw::im3d::End();
+
+
+ // lines
+ at = rw::scale(matrix->at, -0.05f);
+ shape[0].setX(pos->x + at.x);
+ shape[0].setY(pos->y + at.y);
+ shape[0].setZ(pos->z + at.z);
+ rw::im3d::Transform(shape, NUMVERTS+1, nil, rw::im3d::ALLOPAQUE);
+ rw::im3d::RenderIndexedPrimitive(rw::PRIMTYPEPOLYLINE, indices, NUMVERTS*3);
+ rw::im3d::End();
+}
+
+void
+DrawDirectLight(void)
+{
+ enum { NUMVERTS = 20 };
+ const float DIAMETER = 1.5f;
+ const float CONE_ANGLE = 45.0f;
+ const float CONE_SIZE = 3.0f;
+ const float LENGTH = 5.0f;
+ rw::RWDEVICE::Im3DVertex shape[NUMVERTS*2+1];
+ rw::int16 indices[NUMVERTS*3];
+ rw::int32 i;
+
+ rw::Matrix *matrix = CurrentLight->getFrame()->getLTM();
+ rw::V3d *pos = &matrix->pos;
+
+ // cylinder
+ for(i = 0; i < NUMVERTS*2; i += 2){
+ float cosValue = cosf(i/(NUMVERTS/2.0f) * M_PI);
+ float sinValue = sinf(i/(NUMVERTS/2.0f) * M_PI);
+
+ rw::V3d up = rw::scale(matrix->up, sinValue*DIAMETER);
+ rw::V3d right = rw::scale(matrix->right, cosValue*DIAMETER);
+ rw::V3d at = rw::scale(matrix->at, -(CONE_SIZE + 1.0f));
+
+ shape[i].setX(pos->x + at.x + up.x + right.x);
+ shape[i].setY(pos->y + at.y + up.y + right.y);
+ shape[i].setZ(pos->z + at.z + up.z + right.z);
+
+
+ at = rw::scale(matrix->at, -(LENGTH + CONE_SIZE));
+
+ shape[i+1].setX(pos->x + at.x + up.x + right.x);
+ shape[i+1].setY(pos->y + at.y + up.y + right.y);
+ shape[i+1].setZ(pos->z + at.z + up.z + right.z);
+ }
+
+ for(i = 0; i < NUMVERTS*2+1; i++)
+ shape[i].setColor(LightSolidColor.red, LightSolidColor.green,
+ LightSolidColor.blue, 128);
+
+ rw::SetRenderState(rw::VERTEXALPHA, 1);
+ rw::SetRenderState(rw::SRCBLEND, rw::BLENDSRCALPHA);
+ rw::SetRenderState(rw::DESTBLEND, rw::BLENDINVSRCALPHA);
+
+ rw::im3d::Transform(shape, NUMVERTS*2, nil, 0);
+ rw::im3d::RenderPrimitive(rw::PRIMTYPETRISTRIP);
+ rw::im3d::RenderTriangle(2*NUMVERTS-2, 2*NUMVERTS-1, 0);
+ rw::im3d::RenderTriangle(2*NUMVERTS-1, 1, 0);
+ rw::im3d::End();
+
+
+ // bottom cap
+ for(i = 0; i < NUMVERTS*2+1; i++)
+ shape[i].setColor(LightSolidColor.red, LightSolidColor.green,
+ LightSolidColor.blue, 255);
+
+ rw::V3d at = rw::scale(matrix->at, -(LENGTH + CONE_SIZE));
+ shape[NUMVERTS*2].setX(pos->x + at.x);
+ shape[NUMVERTS*2].setY(pos->y + at.y);
+ shape[NUMVERTS*2].setZ(pos->z + at.z);
+
+ for(i = 0; i < NUMVERTS; i++){
+ indices[i*3+0] = NUMVERTS*2;
+ indices[i*3+1] = (i+1)*2 + 1;
+ indices[i*3+2] = i*2 + 1;
+ }
+ indices[NUMVERTS*3-2] = 1;
+
+ rw::im3d::Transform(shape, NUMVERTS*2+1, nil, rw::im3d::ALLOPAQUE);
+ rw::im3d::RenderIndexedPrimitive(rw::PRIMTYPETRILIST, indices, NUMVERTS*3);
+ rw::im3d::End();
+
+
+ // top cap
+ at = rw::scale(matrix->at, -(CONE_SIZE + 1.0f));
+ shape[NUMVERTS*2].setX(pos->x + at.x);
+ shape[NUMVERTS*2].setY(pos->y + at.y);
+ shape[NUMVERTS*2].setZ(pos->z + at.z);
+
+ for(i = 0; i < NUMVERTS; i++){
+ indices[i*3+0] = NUMVERTS*2;
+ indices[i*3+1] = i*2;
+ indices[i*3+2] = (i+1)*2;
+ }
+
+ rw::im3d::Transform(shape, NUMVERTS*2+1, nil, rw::im3d::ALLOPAQUE);
+ rw::im3d::RenderIndexedPrimitive(rw::PRIMTYPETRILIST, indices, NUMVERTS*3);
+ rw::im3d::End();
+
+
+ // cone
+ DrawCone(CONE_ANGLE, CONE_SIZE, -2.0f);
+}
+
+void
+DrawCurrentLight(void)
+{
+ rw::SetRenderStatePtr(rw::TEXTURERASTER, nil);
+ rw::SetRenderState(rw::CULLMODE, rw::CULLBACK);
+ rw::SetRenderState(rw::ZTESTENABLE, 1);
+
+ switch(LightTypeIndex){
+ case 1: DrawPointLight(); break;
+ case 2: DrawDirectLight(); break;
+ case 3:
+ case 4: DrawCone(LightConeAngle, LightRadius*POINT_LIGHT_RADIUS_FACTOR, 1.0f); break;
+ }
+}
+
+
+
+void
+LightRotate(float xAngle, float yAngle)
+{
+ if(CurrentLight == nil || CurrentLight == AmbientLight || CurrentLight == PointLight)
+ return;
+
+ rw::Matrix *cameraMatrix = &Camera->getFrame()->matrix;
+ rw::Frame *lightFrame = CurrentLight->getFrame();
+ rw::V3d pos = lightFrame->matrix.pos;
+
+ pos = rw::scale(pos, -1.0f);
+ lightFrame->translate(&pos, rw::COMBINEPOSTCONCAT);
+
+ lightFrame->rotate(&cameraMatrix->up, xAngle, rw::COMBINEPOSTCONCAT);
+ lightFrame->rotate(&cameraMatrix->right, yAngle, rw::COMBINEPOSTCONCAT);
+
+ pos = rw::scale(pos, -1.0f);
+ lightFrame->translate(&pos, rw::COMBINEPOSTCONCAT);
+}
+
+void
+ClampPosition(rw::V3d *pos, rw::V3d *delta, rw::BBox *bbox)
+{
+ if(pos->x + delta->x < bbox->inf.x)
+ delta->x = bbox->inf.x - pos->x;
+ else if(pos->x + delta->x > bbox->sup.x)
+ delta->x = bbox->sup.x - pos->x;
+
+ if(pos->y + delta->y < bbox->inf.y)
+ delta->y = bbox->inf.y - pos->y;
+ else if(pos->y + delta->y > bbox->sup.y)
+ delta->y = bbox->sup.y - pos->y;
+
+ if(pos->z + delta->z < bbox->inf.z)
+ delta->z = bbox->inf.z - pos->z;
+ else if(pos->z + delta->z > bbox->sup.z)
+ delta->z = bbox->sup.z - pos->z;
+}
+
+void
+LightTranslateXY(float xDelta, float yDelta)
+{
+ if(CurrentLight == nil || CurrentLight == AmbientLight || CurrentLight == DirectLight)
+ return;
+
+ rw::Matrix *cameraMatrix = &Camera->getFrame()->matrix;
+ rw::Frame *lightFrame = CurrentLight->getFrame();
+ rw::V3d right = rw::scale(cameraMatrix->right, xDelta);
+ rw::V3d up = rw::scale(cameraMatrix->up, yDelta);
+ rw::V3d delta = rw::add(right, up);
+
+ ClampPosition(&lightFrame->matrix.pos, &delta, &RoomBBox);
+
+ lightFrame->translate(&delta, rw::COMBINEPOSTCONCAT);
+}
+
+void
+LightTranslateZ(float zDelta)
+{
+ if(CurrentLight == nil || CurrentLight == AmbientLight || CurrentLight == DirectLight)
+ return;
+
+ rw::Matrix *cameraMatrix = &Camera->getFrame()->matrix;
+ rw::Frame *lightFrame = CurrentLight->getFrame();
+ rw::V3d delta = rw::scale(cameraMatrix->at, zDelta);
+
+ ClampPosition(&lightFrame->matrix.pos, &delta, &RoomBBox);
+
+ lightFrame->translate(&delta, rw::COMBINEPOSTCONCAT);
+}
diff --git a/tools/lights/lights.h b/tools/lights/lights.h
new file mode 100644
index 0000000..5400a61
--- /dev/null
+++ b/tools/lights/lights.h
@@ -0,0 +1,37 @@
+
+
+extern rw::Light *BaseAmbientLight;
+extern bool BaseAmbientLightOn;
+
+extern rw::Light *CurrentLight;
+extern rw::Light *AmbientLight;
+extern rw::Light *PointLight;
+extern rw::Light *DirectLight;
+extern rw::Light *SpotLight;
+extern rw::Light *SpotSoftLight;
+
+extern float LightRadius;
+extern float LightConeAngle;
+extern rw::RGBAf LightColor;
+
+extern bool LightOn;
+extern bool LightDrawOn;
+extern rw::V3d LightPos;
+extern rw::int32 LightTypeIndex;
+
+extern rw::BBox RoomBBox;
+
+rw::Light *CreateBaseAmbientLight(void);
+rw::Light *CreateAmbientLight(void);
+rw::Light *CreateDirectLight(void);
+rw::Light *CreatePointLight(void);
+rw::Light *CreateSpotLight(void);
+rw::Light *CreateSpotSoftLight(void);
+
+void LightsDestroy(void);
+void LightsUpdate(void);
+void DrawCurrentLight(void);
+
+void LightRotate(float xAngle, float yAngle);
+void LightTranslateXY(float xDelta, float yDelta);
+void LightTranslateZ(float zDelta);
diff --git a/tools/lights/main.cpp b/tools/lights/main.cpp
new file mode 100644
index 0000000..3589d05
--- /dev/null
+++ b/tools/lights/main.cpp
@@ -0,0 +1,396 @@
+#include <rw.h>
+#include <skeleton.h>
+#include <assert.h>
+
+#include "main.h"
+#include "lights.h"
+
+rw::V3d zero = { 0.0f, 0.0f, 0.0f };
+rw::EngineOpenParams engineOpenParams;
+float FOV = 70.0f;
+
+rw::RGBA ForegroundColor = { 200, 200, 200, 255 };
+rw::RGBA BackgroundColor = { 64, 64, 64, 0 };
+
+rw::World *World;
+rw::Camera *Camera;
+
+rw::V3d Xaxis = { 1.0f, 0.0, 0.0f };
+rw::V3d Yaxis = { 0.0f, 1.0, 0.0f };
+rw::V3d Zaxis = { 0.0f, 0.0, 1.0f };
+
+rw::World*
+CreateWorld(void)
+{
+ rw::BBox bb;
+
+ bb.inf.x = bb.inf.y = bb.inf.z = -100.0f;
+ bb.sup.x = bb.sup.y = bb.sup.z = 100.0f;
+
+ return rw::World::create(&bb);
+}
+
+rw::Camera*
+CreateCamera(rw::World *world)
+{
+ rw::Camera *camera;
+ camera = sk::CameraCreate(sk::globals.width, sk::globals.height, 1);
+ assert(camera);
+ camera->setNearPlane(0.1f);
+ camera->setFarPlane(300.0f);
+ camera->setFOV(FOV, (float)sk::globals.width/sk::globals.height);
+ world->addCamera(camera);
+ return camera;
+}
+
+bool
+CreateTestScene(rw::World *world)
+{
+ rw::Clump *clump;
+ rw::StreamFile in;
+ const char *filename = "checker.dff";
+ if(in.open(filename, "rb") == NULL){
+ printf("couldn't open file\n");
+ return false;
+ }
+ if(!rw::findChunk(&in, rw::ID_CLUMP, NULL, NULL))
+ return false;
+ clump = rw::Clump::streamRead(&in);
+ in.close();
+ if(clump == nil)
+ return false;
+
+ rw::Clump *clone;
+ rw::Frame *clumpFrame;
+ rw::V3d pos;
+ float zOffset = 75.0f;
+
+ // Bottom panel
+ clumpFrame = clump->getFrame();
+ clumpFrame->rotate(&Xaxis, 90.0f, rw::COMBINEREPLACE);
+
+ pos.x = 0.0f;
+ pos.y = -25.0f;
+ pos.z = zOffset;
+ clumpFrame->translate(&pos, rw::COMBINEPOSTCONCAT);
+
+ // only need to add once
+ world->addClump(clump);
+
+ // Top panel
+ clone = clump->clone();
+ clumpFrame = clone->getFrame();
+ clumpFrame->rotate(&Xaxis, -90.0f, rw::COMBINEREPLACE);
+
+ pos.x = 0.0f;
+ pos.y = 25.0f;
+ pos.z = zOffset;
+ clumpFrame->translate(&pos, rw::COMBINEPOSTCONCAT);
+
+ // Left panel
+ clone = clump->clone();
+ clumpFrame = clone->getFrame();
+ clumpFrame->rotate(&Xaxis, 0.0f, rw::COMBINEREPLACE);
+ clumpFrame->rotate(&Yaxis, 90.0f, rw::COMBINEPOSTCONCAT);
+
+ pos.x = 25.0f;
+ pos.y = 0.0f;
+ pos.z = zOffset;
+ clumpFrame->translate(&pos, rw::COMBINEPOSTCONCAT);
+
+ // Right panel
+ clone = clump->clone();
+ clumpFrame = clone->getFrame();
+ clumpFrame->rotate(&Xaxis, 0.0f, rw::COMBINEREPLACE);
+ clumpFrame->rotate(&Yaxis, -90.0f, rw::COMBINEPOSTCONCAT);
+
+ pos.x = -25.0f;
+ pos.y = 0.0f;
+ pos.z = zOffset;
+ clumpFrame->translate(&pos, rw::COMBINEPOSTCONCAT);
+
+ // Back panel
+ clone = clump->clone();
+ clumpFrame = clone->getFrame();
+ clumpFrame->rotate(&Xaxis, 0.0f, rw::COMBINEREPLACE);
+
+ pos.x = 0.0f;
+ pos.y = 0.0f;
+ pos.z = zOffset + 25.0f;
+ clumpFrame->translate(&pos, rw::COMBINEPOSTCONCAT);
+
+ // BBox
+ pos.x = 25.0f;
+ pos.y = 25.0f;
+ pos.z = zOffset - 25.0f;
+ RoomBBox.initialize(&pos);
+ pos.x = -25.0f;
+ pos.y = -25.0f;
+ pos.z = zOffset + 25.0f;
+ RoomBBox.addPoint(&pos);
+
+ return 1;
+}
+
+void
+Initialize(void)
+{
+ sk::globals.windowtitle = "Lights example";
+ sk::globals.width = 1280;
+ sk::globals.height = 800;
+ sk::globals.quit = 0;
+}
+
+bool
+Initialize3D(void)
+{
+ if(!sk::InitRW())
+ return false;
+
+ World = CreateWorld();
+
+ BaseAmbientLight = CreateBaseAmbientLight();
+ AmbientLight = CreateAmbientLight();
+ DirectLight = CreateDirectLight();
+ PointLight = CreatePointLight();
+ SpotLight = CreateSpotLight();
+ SpotSoftLight = CreateSpotSoftLight();
+
+ Camera = CreateCamera(World);
+
+ CreateTestScene(World);
+
+ ImGui_ImplRW_Init();
+ ImGui::StyleColorsClassic();
+
+ return true;
+}
+
+void
+Terminate3D(void)
+{
+ FORLIST(lnk, World->clumps){
+ rw::Clump *clump = rw::Clump::fromWorld(lnk);
+ World->removeClump(clump);
+ clump->destroy();
+ }
+
+ if(Camera){
+ World->removeCamera(Camera);
+ sk::CameraDestroy(Camera);
+ Camera = nil;
+ }
+
+ LightsDestroy();
+
+ if(World){
+ World->destroy();
+ World = nil;
+ }
+
+ sk::TerminateRW();
+}
+
+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
+Gui(void)
+{
+// ImGui::ShowDemoWindow(nil);
+
+ static bool showLightWindow = true;
+ ImGui::Begin("Lights", &showLightWindow);
+
+ ImGui::Checkbox("Light", &LightOn);
+ ImGui::Checkbox("Draw Light", &LightDrawOn);
+ if(ImGui::Checkbox("Base Ambient", &BaseAmbientLightOn)){
+ if(BaseAmbientLightOn){
+ if(BaseAmbientLight->world == nil)
+ World->addLight(BaseAmbientLight);
+ }else{
+ if(BaseAmbientLight->world)
+ World->removeLight(BaseAmbientLight);
+ }
+ }
+
+ ImGui::RadioButton("Ambient Light", &LightTypeIndex, 0);
+ ImGui::RadioButton("Point Light", &LightTypeIndex, 1);
+ ImGui::RadioButton("Directional Light", &LightTypeIndex, 2);
+ ImGui::RadioButton("Spot Light", &LightTypeIndex, 3);
+ ImGui::RadioButton("Soft Spot Light", &LightTypeIndex, 4);
+
+ ImGui::ColorEdit3("Color", (float*)&LightColor);
+ float radAngle = LightConeAngle/180.0f*M_PI;
+ ImGui::SliderAngle("Cone angle", &radAngle, 0.0f, 180.0f);
+ LightConeAngle = radAngle/M_PI*180.0f;
+ ImGui::SliderFloat("Radius", &LightRadius, 0.1f, 500.0f);
+
+ ImGui::End();
+}
+
+void
+Render(float timeDelta)
+{
+ Camera->clear(&BackgroundColor, rw::Camera::CLEARIMAGE|rw::Camera::CLEARZ);
+ Camera->beginUpdate();
+
+ ImGui_ImplRW_NewFrame(timeDelta);
+
+ World->render();
+
+ if(LightDrawOn && CurrentLight)
+ DrawCurrentLight();
+
+ Gui();
+
+ ImGui::EndFrame();
+ ImGui::Render();
+
+ ImGui_ImplRW_RenderDrawLists(ImGui::GetDrawData());
+
+ Camera->endUpdate();
+ Camera->showRaster(0);
+}
+
+void
+Idle(float timeDelta)
+{
+ LightsUpdate();
+
+ Render(timeDelta);
+}
+
+int MouseX, MouseY;
+int MouseDeltaX, MouseDeltaY;
+int MouseButtons;
+
+int CtrlDown;
+
+bool RotateLight;
+bool TranslateLightXY;
+bool TranslateLightZ;
+
+void
+KeyUp(int key)
+{
+ switch(key){
+ case sk::KEY_LCTRL:
+ case sk::KEY_RCTRL:
+ CtrlDown = 0;
+ break;
+ }
+}
+
+void
+KeyDown(int key)
+{
+ switch(key){
+ case sk::KEY_LCTRL:
+ case sk::KEY_RCTRL:
+ CtrlDown = 1;
+ break;
+ case sk::KEY_ESC:
+ sk::globals.quit = 1;
+ break;
+ }
+}
+
+void
+MouseBtn(sk::MouseState *mouse)
+{
+ MouseButtons = mouse->buttons;
+ RotateLight = !CtrlDown && !!(MouseButtons&1);
+ TranslateLightXY = CtrlDown && !!(MouseButtons&1);
+ TranslateLightZ = CtrlDown && !!(MouseButtons&4);
+}
+
+void
+MouseMove(sk::MouseState *mouse)
+{
+ MouseDeltaX = mouse->posx - MouseX;
+ MouseDeltaY = mouse->posy - MouseY;
+ MouseX = mouse->posx;
+ MouseY = mouse->posy;
+
+ if(RotateLight)
+ LightRotate(-MouseDeltaX, MouseDeltaY);
+ if(TranslateLightXY)
+ LightTranslateXY(-MouseDeltaX*0.1f, -MouseDeltaY*0.1f);
+ if(TranslateLightZ)
+ LightTranslateZ(-MouseDeltaY*0.1f);
+}
+
+sk::EventStatus
+AppEventHandler(sk::Event e, void *param)
+{
+ using namespace sk;
+ Rect *r;
+ MouseState *ms;
+
+ ImGuiEventHandler(e, param);
+ ImGuiIO &io = ImGui::GetIO();
+
+ switch(e){
+ case INITIALIZE:
+ Initialize();
+ return EVENTPROCESSED;
+ case RWINITIALIZE:
+ return Initialize3D() ? EVENTPROCESSED : EVENTERROR;
+ case RWTERMINATE:
+ Terminate3D();
+ return EVENTPROCESSED;
+ case PLUGINATTACH:
+ return attachPlugins() ? EVENTPROCESSED : EVENTERROR;
+ case KEYDOWN:
+ KeyDown(*(int*)param);
+ return EVENTPROCESSED;
+ case KEYUP:
+ KeyUp(*(int*)param);
+ return EVENTPROCESSED;
+ case MOUSEBTN:
+ if(!io.WantCaptureMouse){
+ ms = (MouseState*)param;
+ MouseBtn(ms);
+ }else
+ MouseButtons = 0;
+ return EVENTPROCESSED;
+ case MOUSEMOVE:
+ MouseMove((MouseState*)param);
+ 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){
+ sk::CameraSize(::Camera, r);
+ ::Camera->setFOV(FOV, (float)sk::globals.width/sk::globals.height);
+ }
+ break;
+ case IDLE:
+ Idle(*(float*)param);
+ return EVENTPROCESSED;
+ }
+ return sk::EVENTNOTPROCESSED;
+}
diff --git a/tools/lights/main.h b/tools/lights/main.h
new file mode 100644
index 0000000..6358f2d
--- /dev/null
+++ b/tools/lights/main.h
@@ -0,0 +1,7 @@
+
+extern rw::World *World;
+extern rw::Camera *Camera;
+
+extern rw::V3d Xaxis;
+extern rw::V3d Yaxis;
+extern rw::V3d Zaxis;
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;
+}
diff --git a/tools/ps2test/CMakeLists.txt b/tools/ps2test/CMakeLists.txt
new file mode 100644
index 0000000..5195a19
--- /dev/null
+++ b/tools/ps2test/CMakeLists.txt
@@ -0,0 +1,26 @@
+add_executable(ps2test
+ gs.h
+ main.cpp
+ mem.h
+ ps2.h
+
+ vu/defaultpipe.dsm
+ vu/skinpipe.dsm
+
+ vu/light.vu
+ vu/setup_persp.vu
+)
+
+target_link_libraries(ps2test
+ PRIVATE
+ librw::librw
+ kernel
+)
+
+librw_platform_target(ps2test INSTALL)
+
+if(LIBRW_INSTALL)
+ install(TARGETS ps2test
+ RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
+ )
+endif()
diff --git a/tools/ps2test/gs.h b/tools/ps2test/gs.h
new file mode 100644
index 0000000..361ad8c
--- /dev/null
+++ b/tools/ps2test/gs.h
@@ -0,0 +1,437 @@
+#define GS_NONINTERLACED 0
+#define GS_INTERLACED 1
+
+#define GS_NTSC 2
+#define GS_PAL 3
+#define GS_VESA1A 0x1a
+#define GS_VESA1B 0x1b
+#define GS_VESA1C 0x1c
+#define GS_VESA1D 0x1d
+#define GS_VESA2A 0x2a
+#define GS_VESA2B 0x2b
+#define GS_VESA2C 0x2c
+#define GS_VESA2D 0x2d
+#define GS_VESA2E 0x2e
+#define GS_VESA3B 0x3b
+#define GS_VESA3C 0x3c
+#define GS_VESA3D 0x3d
+#define GS_VESA3E 0x3e
+#define GS_VESA4A 0x4a
+#define GS_VESA4B 0x4b
+#define GS_DTV480P 0x50
+
+#define GS_FIELD 0
+#define GS_FRAME 1
+
+#define GS_PSMCT32 0
+#define GS_PSMCT24 1
+#define GS_PSMCT16 2
+#define GS_PSMCT16S 10
+#define GS_PS_GPU24 18
+
+#define GS_PSMZ32 0
+#define GS_PSMZ24 1
+#define GS_PSMZ16 2
+#define GS_PSMZ16S 10
+
+#define GS_ZTST_NEVER 0
+#define GS_ZTST_ALWAYS 1
+#define GS_ZTST_GREATER 2
+#define GS_ZTST_GEQUAL 3
+
+#define GS_PRIM_POINT 0
+#define GS_PRIM_LINE 1
+#define GS_PRIM_LINE_STRIP 2
+#define GS_PRIM_TRI 3
+#define GS_PRIM_TRI_STRIP 4
+#define GS_PRIM_TRI_FAN 5
+#define GS_PRIM_SPRITE 6
+#define GS_PRIM_NO_SPEC 7
+#define GS_IIP_FLAT 0
+#define GS_IIP_GOURAUD 1
+
+/* GS general purpose registers */
+
+#define GS_PRIM 0x00
+#define GS_RGBAQ 0x01
+#define GS_ST 0x02
+#define GS_UV 0x03
+#define GS_XYZF2 0x04
+#define GS_XYZ2 0x05
+#define GS_TEX0_1 0x06
+#define GS_TEX0_2 0x07
+#define GS_CLAMP_1 0x08
+#define GS_CLAMP_2 0x09
+#define GS_FOG 0x0a
+#define GS_XYZF3 0x0c
+#define GS_XYZ3 0x0d
+#define GS_TEX1_1 0x14
+#define GS_TEX1_2 0x15
+#define GS_TEX2_1 0x16
+#define GS_TEX2_2 0x17
+#define GS_XYOFFSET_1 0x18
+#define GS_XYOFFSET_2 0x19
+#define GS_PRMODECONT 0x1a
+#define GS_PRMODE 0x1b
+#define GS_TEXCLUT 0x1c
+#define GS_SCANMSK 0x22
+#define GS_MIPTBP1_1 0x34
+#define GS_MIPTBP1_2 0x35
+#define GS_MIPTBP2_1 0x36
+#define GS_MIPTBP2_2 0x37
+#define GS_TEXA 0x3b
+#define GS_FOGCOL 0x3d
+#define GS_TEXFLUSH 0x3f
+#define GS_SCISSOR_1 0x40
+#define GS_SCISSOR_2 0x41
+#define GS_ALPHA_1 0x42
+#define GS_ALPHA_2 0x43
+#define GS_DIMX 0x44
+#define GS_DTHE 0x45
+#define GS_COLCLAMP 0x46
+#define GS_TEST_1 0x47
+#define GS_TEST_2 0x48
+#define GS_PABE 0x49
+#define GS_FBA_1 0x4a
+#define GS_FBA_2 0x4b
+#define GS_FRAME_1 0x4c
+#define GS_FRAME_2 0x4d
+#define GS_ZBUF_1 0x4e
+#define GS_ZBUF_2 0x4f
+#define GS_BITBLTBUF 0x50
+#define GS_TRXPOS 0x51
+#define GS_TRXREG 0x52
+#define GS_TRXDIR 0x53
+#define GS_HWREG 0x54
+#define GS_SIGNAL 0x60
+#define GS_FINISH 0x61
+#define GS_LABEL 0x62
+
+typedef union
+{
+ struct {
+ uint64 EN1 : 1;
+ uint64 EN2 : 1;
+ uint64 CRTMD : 3;
+ uint64 MMOD : 1;
+ uint64 AMOD : 1;
+ uint64 SLBG : 1;
+ uint64 ALP : 8;
+ } f;
+ uint64 d;
+} GsPmode;
+
+#define GS_MAKE_PMODE(EN1,EN2,MMOD,AMOD,SLBG,ALP) \
+ (BIT64(EN1,0) | BIT64(EN2,1) | BIT64(1,2) | \
+ BIT64(MMOD,5) | BIT64(AMOD,6) | BIT64(SLBG,7) | BIT64(ALP,8))
+
+typedef union
+{
+ struct {
+ uint64 INT : 1;
+ uint64 FFMD : 1;
+ uint64 DPMS : 2;
+ } f;
+ uint64 d;
+} GsSmode2;
+
+#define GS_MAKE_SMODE2(INT,FFMD,DPMS) \
+ (BIT64(INT,0) | BIT64(FFMD,1) | BIT64(DPMS,2))
+
+typedef union
+{
+ struct {
+ uint64 FBP : 9;
+ uint64 FBW : 6;
+ uint64 PSM : 5;
+ uint64 : 12;
+ uint64 DBX : 11;
+ uint64 DBY : 11;
+ } f;
+ uint64 d;
+} GsDispfb;
+
+#define GS_MAKE_DISPFB(FBP,FBW,PSM,DBX,DBY) \
+ (BIT64(FBP,0) | BIT64(FBW,9) | BIT64(PSM,15) | \
+ BIT64(DBX,32) | BIT64(DBY,43))
+
+typedef union
+{
+ struct {
+ uint64 DX : 12;
+ uint64 DY : 11;
+ uint64 MAGH : 4;
+ uint64 MAGV : 2;
+ uint64 : 3;
+ uint64 DW : 12;
+ uint64 DH : 11;
+ } f;
+ uint64 d;
+} GsDisplay;
+
+#define GS_MAKE_DISPLAY(DX,DY,MAGH,MAGV,DW,DH) \
+ (BIT64(DX,0) | BIT64(DY,12) | BIT64(MAGH,23) | \
+ BIT64(MAGV,27) | BIT64(DW,32) | BIT64(DH,44))
+
+typedef union
+{
+ struct {
+ uint64 EXBP : 14;
+ uint64 EXBW : 6;
+ uint64 FBIN : 2;
+ uint64 WFFMD : 1;
+ uint64 EMODA : 2;
+ uint64 EMODC : 2;
+ uint64 : 5;
+ uint64 WDX : 11;
+ uint64 WDY : 11;
+ } f;
+ uint64 d;
+} GsExtbuf;
+
+#define GS_MAKE_EXTBUF(EXBP,EXBW,FBIN,WFFMD,EMODA,EMODC,WDX,WDY) \
+ (BIT64(EXBP,0) | BIT64(EXBW,14) | BIT64(FBIN,20) | \
+ BIT64(WFFMD,22) | BIT64(EMODA,23) | BIT64(EMODC,25) | \
+ BIT64(WDX,32) | BIT64(WDY,43))
+
+typedef union
+{
+ struct {
+ uint64 SX : 12;
+ uint64 SY : 11;
+ uint64 SMPH : 4;
+ uint64 SMPV : 2;
+ uint64 : 3;
+ uint64 WW : 12;
+ uint64 WH : 11;
+ } f;
+ uint64 d;
+} GsExtdata;
+
+#define GS_MAKE_EXTDATA(SX,SY,SMPH,SMPV,WW,WH) \
+ (BIT64(SX,0) | BIT64(SY,12) | BIT64(SMPH,23) | \
+ BIT64(SMPV,27) | BIT64(WW,32) | BIT64(WH,44))
+
+typedef union
+{
+ struct {
+ uint64 WRITE : 1;
+ } f;
+ uint64 d;
+} GsExtwrite;
+
+typedef union
+{
+ struct {
+ uint64 R : 8;
+ uint64 G : 8;
+ uint64 B : 8;
+ } f;
+ uint64 d;
+} GsBgcolor;
+
+#define GS_MAKE_BGCOLOR(R,G,B) \
+ (BIT64(R,0) | BIT64(G,8) | BIT64(B,16))
+
+typedef union
+{
+ struct {
+ uint64 SIGNAL : 1;
+ uint64 FINISH : 1;
+ uint64 HSINT : 1;
+ uint64 VSINT : 1;
+ uint64 EDWINT : 1;
+ uint64 : 3;
+ uint64 FLUSH : 1;
+ uint64 RESET : 1;
+ uint64 : 2;
+ uint64 NFIELD : 1;
+ uint64 FIELD : 1;
+ uint64 FIFO : 2;
+ uint64 REV : 8;
+ uint64 ID : 8;
+ } f;
+ uint64 d;
+} GsCsr;
+
+#define GS_CSR_SIGNAL_O 0
+#define GS_CSR_FINISH_O 1
+#define GS_CSR_HSINT_O 2
+#define GS_CSR_VSINT_O 3
+#define GS_CSR_EDWINT_O 4
+#define GS_CSR_FLUSH_O 8
+#define GS_CSR_RESET_O 9
+#define GS_CSR_NFIELD_O 12
+#define GS_CSR_FIELD_O 13
+#define GS_CSR_FIFO_O 14
+#define GS_CSR_REV_O 16
+#define GS_CSR_ID_O 24
+
+typedef union
+{
+ struct {
+ uint64 : 8;
+ uint64 SIGMSK : 1;
+ uint64 FINISHMSK : 1;
+ uint64 HSMSKMSK : 1;
+ uint64 VSMSKMSK : 1;
+ uint64 EDWMSKMSK : 1;
+ } f;
+ uint64 d;
+} GsImr;
+
+typedef union
+{
+ struct {
+ uint64 DIR : 1;
+ } f;
+ uint64 d;
+} GsBusdir;
+
+typedef union
+{
+ struct {
+ uint64 SIGID : 32;
+ uint64 LBLID : 32;
+ } f;
+ uint64 d;
+} GsSiglblid;
+
+
+typedef union
+{
+ struct {
+ uint64 FBP : 9;
+ uint64 : 7;
+ uint64 FBW : 6;
+ uint64 : 2;
+ uint64 PSM : 6;
+ uint64 : 2;
+ uint64 FBMSK : 32;
+ } f;
+ uint64 d;
+} GsFrame;
+
+#define GS_MAKE_FRAME(FBP,FBW,PSM,FBMASK) \
+ (BIT64(FBP,0) | BIT64(FBW,16) | BIT64(PSM,24) | BIT64(FBMASK,32))
+
+typedef union
+{
+ struct {
+ uint64 ZBP : 9;
+ uint64 : 15;
+ uint64 PSM : 4;
+ uint64 : 4;
+ uint64 ZMSDK : 1;
+ } f;
+ uint64 d;
+} GsZbuf;
+
+#define GS_MAKE_ZBUF(ZBP,PSM,ZMSK) \
+ (BIT64(ZBP,0) | BIT64(PSM,24) | BIT64(ZMSK,32))
+
+typedef union
+{
+ struct {
+ uint64 OFX : 16;
+ uint64 : 16;
+ uint64 OFY : 16;
+ } f;
+ uint64 d;
+} GsXyOffset;
+
+#define GS_MAKE_XYOFFSET(OFX,OFY) \
+ (BIT64(OFX,0) | BIT64(OFY,32))
+
+typedef union
+{
+ struct {
+ uint64 SCAX0 : 11;
+ uint64 : 5;
+ uint64 SCAX1 : 11;
+ uint64 : 5;
+ uint64 SCAY0 : 11;
+ uint64 : 5;
+ uint64 SCAY1 : 11;
+ } f;
+ uint64 d;
+} GsScissor;
+
+#define GS_MAKE_SCISSOR(SCAX0,SCAX1,SCAY0,SCAY1) \
+ (BIT64(SCAX0,0) | BIT64(SCAX1,16) | BIT64(SCAY0,32) | BIT64(SCAY1,48))
+
+#define GS_MAKE_TEST(ATE,ATST,AREF,AFAIL,DATE,DATM,ZTE,ZTST) \
+ (BIT64(ATE,0) | BIT64(ATST,1) | BIT64(AREF,4) | BIT64(AFAIL,12) | \
+ BIT64(DATE,14) | BIT64(DATM,15) | BIT64(ZTE,16) | BIT64(ZTST,17))
+
+#define GS_MAKE_PRIM(PRIM,IIP,TME,FGE,ABE,AA1,FST,CTXT,FIX) \
+ (BIT64(PRIM,0) | BIT64(IIP,3) | BIT64(TME,4) | BIT64(FGE,5) | \
+ BIT64(ABE,6) | BIT64(AA1,7) | BIT64(FST,8) | BIT64(CTXT,9) | BIT64(FIX,10))
+
+#define GS_MAKE_RGBAQ(R,G,B,A,Q) \
+ (BIT64(R,0) | BIT64(G,8) | BIT64(B,16) | BIT64(A,24) | BIT64(Q,32))
+
+#define GS_MAKE_XYZ(X,Y,Z) \
+ (BIT64(X,0) | BIT64(Y,16) | BIT64(Z,32))
+
+#define GIF_PACKED 0
+#define GIF_REGLIST 1
+#define GIF_IMAGE 2
+
+#define GIF_MAKE_TAG(NLOOP,EOP,PRE,PRIM,FLG,NREG) \
+ (BIT64(NLOOP,0) | BIT64(EOP,15) | BIT64(PRE,46) | \
+ BIT64(PRIM,47) | BIT64(FLG,58) | BIT64(NREG,60))
+
+/* This is global and not tied to a user context because
+ * it is set up by kernel functions and not really changed
+ * afterwards. */
+typedef struct GsCrtState GsCrtState;
+struct GsCrtState
+{
+ short inter, mode, ff;
+};
+extern GsCrtState gsCrtState;
+
+typedef struct GsDispCtx GsDispCtx;
+struct GsDispCtx
+{
+ // two circuits
+ GsPmode pmode;
+ GsDispfb dispfb1;
+ GsDispfb dispfb2;
+ GsDisplay display1;
+ GsDisplay display2;
+ GsBgcolor bgcolor;
+};
+
+typedef struct GsDrawCtx GsDrawCtx;
+struct GsDrawCtx
+{
+ //two contexts
+ uint128 gifTag;
+ GsFrame frame1;
+ uint64 ad_frame1;
+ GsFrame frame2;
+ uint64 ad_frame2;
+ GsZbuf zbuf1;
+ uint64 ad_zbuf1;
+ GsZbuf zbuf2;
+ uint64 ad_zbuf2;
+ GsXyOffset xyoffset1;
+ uint64 ad_xyoffset1;
+ GsXyOffset xyoffset2;
+ uint64 ad_xyoffset2;
+ GsScissor scissor1;
+ uint64 ad_scissor1;
+ GsScissor scissor2;
+ uint64 ad_scissor2;
+};
+
+typedef struct GsCtx GsCtx;
+struct GsCtx
+{
+ // display context; two buffers
+ GsDispCtx disp[2];
+ // draw context; two buffers
+ GsDrawCtx draw[2];
+};
diff --git a/tools/ps2test/main.cpp b/tools/ps2test/main.cpp
new file mode 100755
index 0000000..6eb0f25
--- /dev/null
+++ b/tools/ps2test/main.cpp
@@ -0,0 +1,788 @@
+#include <cstdio>
+#include <cassert>
+#include <cerrno>
+
+#define PAL
+
+#include <rw.h>
+using rw::uint8;
+using rw::uint16;
+using rw::uint32;
+using rw::uint64;
+using rw::int8;
+using rw::int16;
+using rw::int32;
+using rw::int64;
+using rw::bool32;
+using rw::float32;
+typedef uint8 uchar;
+typedef uint16 ushort;
+typedef uint32 uint;
+
+#define WIDTH 640
+#ifdef PAL
+#define HEIGHT 512
+#define VMODE GS_PAL
+#else
+#define HEIGHT 448
+#define VMODE GS_NTSC
+#endif
+
+#include "ps2.h"
+
+// getting undefined references otherwise :/
+int *__errno() { return &errno; }
+
+// NONINTERLACED and FRAME have half of the FIELD vertical resolution!
+// NONINTERLACED has half the vertical units
+
+uint128 packetbuf[128];
+uint128 vuXYZScale;
+uint128 vuXYZOffset;
+extern uint32 geometryCall[];
+extern uint32 skinPipe[];
+
+uint128 *curVifPtr;
+uint128 lightpacket[128];
+int32 numLightQ;
+
+
+
+rw::World *world;
+rw::Camera *camera;
+
+
+int frames;
+
+void
+printquad(uint128 p)
+{
+ uint64 *lp;
+ lp = (uint64*)&p;
+ printf("%016lx %016lx\n", lp[1], lp[0]);
+}
+
+void
+printquad4(uint128 p)
+{
+ uint32 *lp;
+ lp = (uint32*)&p;
+ printf("%08x %08x %08x %08x\n", lp[0], lp[1], lp[2], lp[3]);
+}
+
+void
+dump4(uint128 *p, int n)
+{
+printf("data at %p\n", p);
+ while(n--)
+ printquad4(*p++);
+}
+
+struct DmaChannel {
+ uint32 chcr; uint32 pad0[3];
+ uint32 madr; uint32 pad1[3];
+ uint32 qwc; uint32 pad2[3];
+ uint32 tadr; uint32 pad3[3];
+ uint32 asr0; uint32 pad4[3];
+ uint32 asr1; uint32 pad5[3];
+ uint32 pad6[8];
+ uint32 sadr;
+};
+
+static struct DmaChannel *dmaChannels[] = {
+ (struct DmaChannel *) &D0_CHCR,
+ (struct DmaChannel *) &D1_CHCR,
+ (struct DmaChannel *) &D2_CHCR,
+ (struct DmaChannel *) &D3_CHCR,
+ (struct DmaChannel *) &D4_CHCR,
+ (struct DmaChannel *) &D5_CHCR,
+ (struct DmaChannel *) &D6_CHCR,
+ (struct DmaChannel *) &D7_CHCR,
+ (struct DmaChannel *) &D8_CHCR,
+ (struct DmaChannel *) &D9_CHCR
+};
+
+void
+dmaReset(void)
+{
+ /* don't clear the SIF channels */
+ int doclear[] = { 1, 1, 1, 1, 1, 0, 0, 0, 1, 1 };
+ int i;
+
+ D_CTRL = 0;
+ for(i = 0; i < 10; i++)
+ if(doclear[i]){
+ dmaChannels[i]->chcr = 0;
+ dmaChannels[i]->madr = 0;
+ dmaChannels[i]->qwc = 0;
+ dmaChannels[i]->tadr = 0;
+ dmaChannels[i]->asr0 = 0;
+ dmaChannels[i]->asr1 = 0;
+ dmaChannels[i]->sadr = 0;
+ }
+ D_CTRL = 1;
+}
+
+void
+waitDMA(volatile uint32 *chcr)
+{
+ while(*chcr & (1<<8));
+}
+
+void
+qwcpy(uint128 *dst, uint128 *src, int n)
+{
+ while(n--) *dst++ = *src++;
+}
+
+void
+toGIF(void *src, int n)
+{
+ FlushCache(0);
+ D2_QWC = n;
+ D2_MADR = (uint32)src;
+ D2_CHCR = 1<<8;
+ waitDMA(&D2_CHCR);
+}
+
+void
+toGIFchain(void *src)
+{
+ FlushCache(0);
+ D2_QWC = 0;
+ D2_TADR = (uint32)src & 0x0FFFFFFF;
+ D2_CHCR = 1<<0 | 1<<2 | 1<<6 | 1<<8;
+ waitDMA(&D2_CHCR);
+}
+
+void
+toVIF1chain(void *src)
+{
+ FlushCache(0);
+ D1_QWC = 0;
+ D1_TADR = (uint32)src & 0x0FFFFFFF;
+ D1_CHCR = 1<<0 | 1<<2 | 1<<6 | 1<<8;
+ waitDMA(&D1_CHCR);
+}
+
+
+GsCrtState gsCrtState;
+
+int psmsizemap[64] = {
+ 4, // GS_PSMCT32
+ 4, // GS_PSMCT24
+ 2, // GS_PSMCT16
+ 0, 0, 0, 0, 0, 0, 0,
+ 2, // GS_PSMCT16S
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 4, // GS_PSMZ32
+ 4, // GS_PSMZ24
+ 2, // GS_PSMZ16
+ 2, // GS_PSMZ16S
+ 0, 0, 0, 0, 0
+};
+
+void
+GsResetCrt(uchar inter, uchar mode, uchar ff)
+{
+ gsCrtState.inter = inter;
+ gsCrtState.mode = mode;
+ gsCrtState.ff = ff;
+ GS_CSR = 1 << GS_CSR_RESET_O;
+ __asm__("sync.p; nop");
+ GsPutIMR(0xff00);
+ SetGsCrt(gsCrtState.inter, gsCrtState.mode, gsCrtState.ff);
+}
+
+uint gsAllocPtr = 0;
+
+void
+GsInitDispCtx(GsDispCtx *disp, int width, int height, int psm)
+{
+ int magh, magv;
+ int dx, dy;
+ int dw, dh;
+
+ dx = gsCrtState.mode == GS_NTSC ? 636 : 656;
+ dy = gsCrtState.mode == GS_NTSC ? 25 : 36;
+ magh = 2560/width - 1;
+ magv = 0;
+ dw = 2560-1;
+ dh = height-1;
+
+ if(gsCrtState.inter == GS_INTERLACED){
+ dy *= 2;
+ if(gsCrtState.ff == GS_FRAME)
+ dh = (dh+1)*2-1;
+ }
+
+ disp->pmode.d = GS_MAKE_PMODE(0, 1, 1, 1, 0, 0x00);
+// disp->bgcolor.d = 0x404040;
+ disp->bgcolor.d = 0x000000;
+ disp->dispfb1.d = 0;
+ disp->dispfb2.d = GS_MAKE_DISPFB(0, width/64, psm, 0, 0);
+ disp->display1.d = 0;
+ disp->display2.d = GS_MAKE_DISPLAY(dx, dy, magh, magv, dw, dh);
+}
+
+void
+GsPutDispCtx(GsDispCtx *disp)
+{
+ GS_PMODE = disp->pmode.d;
+ GS_DISPFB1 = disp->dispfb1.d;
+ GS_DISPLAY1 = disp->display1.d;
+ GS_DISPFB2 = disp->dispfb2.d;
+ GS_DISPLAY2 = disp->display2.d;
+ GS_BGCOLOR = disp->bgcolor.d;
+}
+
+void
+GsInitDrawCtx(GsDrawCtx *draw, int width, int height, int psm, int zpsm)
+{
+ MAKE128(draw->gifTag, 0xe,
+ GIF_MAKE_TAG(8, 1, 0, 0, GIF_PACKED, 1));
+ draw->frame1.d = GS_MAKE_FRAME(0, width/64, psm, 0);
+ draw->ad_frame1 = GS_FRAME_1;
+ draw->frame2.d = draw->frame1.d;
+ draw->ad_frame2 = GS_FRAME_2;
+ draw->zbuf1.d = GS_MAKE_ZBUF(0, zpsm, 0);
+ draw->ad_zbuf1 = GS_ZBUF_1;
+ draw->zbuf2.d = draw->zbuf1.d;
+ draw->ad_zbuf2 = GS_ZBUF_2;
+ draw->xyoffset1.d = GS_MAKE_XYOFFSET(2048<<4, 2048<<4);
+ draw->ad_xyoffset1 = GS_XYOFFSET_1;
+ draw->xyoffset2.d = draw->xyoffset1.d;
+ draw->ad_xyoffset2 = GS_XYOFFSET_2;
+ draw->scissor1.d = GS_MAKE_SCISSOR(0, width-1, 0, height-1);
+ draw->ad_scissor1 = GS_SCISSOR_1;
+ draw->scissor2.d = draw->scissor1.d;
+ draw->ad_scissor2 = GS_SCISSOR_2;
+}
+
+void
+GsPutDrawCtx(GsDrawCtx *draw)
+{
+// printquad(*(uint128*)&draw->frame1);
+ toGIF(draw, 9);
+}
+
+void
+GsInitCtx(GsCtx *ctx, int width, int height, int psm, int zpsm)
+{
+ uint fbsz, zbsz;
+ uint fbp, zbp;
+ fbsz = (width*height*psmsizemap[psm] + 2047)/2048;
+ zbsz = (width*height*psmsizemap[0x30|zpsm] + 2047)/2048;
+ gsAllocPtr = 2*fbsz + zbsz;
+ fbp = fbsz;
+ zbp = fbsz*2;
+
+ GsInitDispCtx(&ctx->disp[0], width, height, psm);
+ GsInitDispCtx(&ctx->disp[1], width, height, psm);
+ GsInitDrawCtx(&ctx->draw[0], width, height, psm, zpsm);
+ GsInitDrawCtx(&ctx->draw[1], width, height, psm, zpsm);
+ ctx->disp[1].dispfb2.f.FBP = fbp/4;
+ ctx->draw[0].frame1.f.FBP = fbp/4;
+ ctx->draw[0].frame2.f.FBP = fbp/4;
+ ctx->draw[0].zbuf1.f.ZBP = zbp/4;
+ ctx->draw[0].zbuf2.f.ZBP = zbp/4;
+ ctx->draw[1].zbuf1.f.ZBP = zbp/4;
+ ctx->draw[1].zbuf2.f.ZBP = zbp/4;
+}
+
+void
+initrender(void)
+{
+ uint128 *p, tmp;
+ p = packetbuf;
+ MAKE128(tmp, 0xe, GIF_MAKE_TAG(2, 1, 0, 0, GIF_PACKED, 1));
+ *p++ = tmp;
+ MAKE128(tmp, GS_PRMODECONT, 1);
+ *p++ = tmp;
+ MAKE128(tmp, GS_COLCLAMP, 1);
+ *p++ = tmp;
+ toGIF(packetbuf, 3);
+}
+
+void
+clearscreen(int r, int g, int b)
+{
+ int x, y;
+ uint128 *p, tmp;
+ p = packetbuf;
+
+ x = (2048 + WIDTH)<<4;
+ y = (2048 + HEIGHT)<<4;
+
+ MAKE128(tmp, 0xe, GIF_MAKE_TAG(5, 1, 0, 0, GIF_PACKED, 1));
+ *p++ = tmp;
+ MAKE128(tmp, GS_TEST_1, GS_MAKE_TEST(0, 0, 0, 0, 0, 0, 1, 1));
+ *p++ = tmp;
+ MAKE128(tmp, GS_PRIM, GS_MAKE_PRIM(GS_PRIM_SPRITE,0,0,0,0,0,0,0,0));
+ *p++ = tmp;
+ MAKE128(tmp, GS_RGBAQ, GS_MAKE_RGBAQ(r, g, b, 0, 0));
+ *p++ = tmp;
+ MAKE128(tmp, GS_XYZ2, GS_MAKE_XYZ(2048<<4, 2048<<4, 0));
+ *p++ = tmp;
+ MAKE128(tmp, GS_XYZ2, GS_MAKE_XYZ(x, y, 0));
+ *p++ = tmp;
+ toGIF(packetbuf, 6);
+}
+
+void
+drawtest(void)
+{
+ int x0, x1, x2, x3;
+ int y0, y1, y2;
+ uint128 *p, tmp;
+ int n;
+
+ x0 = 2048<<4;
+ x1 = (2048 + 210)<<4;
+ x2 = (2048 + 430)<<4;
+ x3 = (2048 + 640)<<4;
+ y0 = 2048<<4;
+ y1 = (2048 + 224)<<4;
+ y2 = (2048 + 448)<<4;
+
+ n = 2 + 3*7;
+ p = packetbuf;
+ MAKEQ(tmp, 0x70000000 | n+1, 0, 0, 0);
+ *p++ = tmp;
+ MAKE128(tmp, 0xe, GIF_MAKE_TAG(n, 1, 0, 0, GIF_PACKED, 1));
+ *p++ = tmp;
+ MAKE128(tmp, GS_TEST_1, GS_MAKE_TEST(0, 0, 0, 0, 0, 0, 1, 1));
+ *p++ = tmp;
+ MAKE128(tmp, GS_PRIM, GS_MAKE_PRIM(GS_PRIM_SPRITE,0,0,0,0,0,0,0,0));
+ *p++ = tmp;
+ MAKE128(tmp, GS_RGBAQ, GS_MAKE_RGBAQ(255, 0, 0, 0, 0));
+ *p++ = tmp;
+ MAKE128(tmp, GS_XYZ2, GS_MAKE_XYZ(x0, y0, 0));
+ *p++ = tmp;
+ MAKE128(tmp, GS_XYZ2, GS_MAKE_XYZ(x1, y1, 0));
+ *p++ = tmp;
+ MAKE128(tmp, GS_RGBAQ, GS_MAKE_RGBAQ(0, 255, 0, 0, 0));
+ *p++ = tmp;
+ MAKE128(tmp, GS_XYZ2, GS_MAKE_XYZ(x1, y0, 0));
+ *p++ = tmp;
+ MAKE128(tmp, GS_XYZ2, GS_MAKE_XYZ(x2, y1, 0));
+ *p++ = tmp;
+ MAKE128(tmp, GS_RGBAQ, GS_MAKE_RGBAQ(0, 0, 255, 0, 0));
+ *p++ = tmp;
+ MAKE128(tmp, GS_XYZ2, GS_MAKE_XYZ(x2, y0, 0));
+ *p++ = tmp;
+ MAKE128(tmp, GS_XYZ2, GS_MAKE_XYZ(x3, y1, 0));
+ *p++ = tmp;
+ MAKE128(tmp, GS_RGBAQ, GS_MAKE_RGBAQ(0, 255, 255, 0, 0));
+ *p++ = tmp;
+ MAKE128(tmp, GS_XYZ2, GS_MAKE_XYZ(x0, y1, 0));
+ *p++ = tmp;
+ MAKE128(tmp, GS_XYZ2, GS_MAKE_XYZ(x1, y2, 0));
+ *p++ = tmp;
+ MAKE128(tmp, GS_RGBAQ, GS_MAKE_RGBAQ(255, 0, 255, 0, 0));
+ *p++ = tmp;
+ MAKE128(tmp, GS_XYZ2, GS_MAKE_XYZ(x1, y1, 0));
+ *p++ = tmp;
+ MAKE128(tmp, GS_XYZ2, GS_MAKE_XYZ(x2, y2, 0));
+ *p++ = tmp;
+ MAKE128(tmp, GS_RGBAQ, GS_MAKE_RGBAQ(255, 255, 0, 0, 0));
+ *p++ = tmp;
+ MAKE128(tmp, GS_XYZ2, GS_MAKE_XYZ(x2, y1, 0));
+ *p++ = tmp;
+ MAKE128(tmp, GS_XYZ2, GS_MAKE_XYZ(x3, y2, 0));
+ *p++ = tmp;
+ MAKE128(tmp, GS_RGBAQ, GS_MAKE_RGBAQ(255, 255, 255, 0, 0));
+ *p++ = tmp;
+ MAKE128(tmp, GS_XYZ2, GS_MAKE_XYZ((2048+20)<<4, y0, 0));
+ *p++ = tmp;
+ MAKE128(tmp, GS_XYZ2, GS_MAKE_XYZ(x3, (2048+20)<<4, 0));
+ *p++ = tmp;
+ toGIFchain(packetbuf);
+}
+
+void
+drawtri(void)
+{
+ uint128 *p, tmp;
+ uint32 *ip;
+ int nverts, n;
+
+ nverts = 3;
+ n = 2*nverts;
+ p = packetbuf;
+ MAKEQ(tmp, 0x70000000 | n+1, 0, 0, 0);
+ *p++ = tmp;
+ MAKE128(tmp, (0x5<<4) | 0x1,
+ GIF_MAKE_TAG(nverts, 1, 1, GS_MAKE_PRIM(GS_PRIM_TRI, 1, 0, 0, 0, 0, 0, 0, 0), GIF_PACKED, 2));
+ *p++ = tmp;
+ MAKEQ(tmp, 255, 0, 0, 0);
+ *p++ = tmp;
+ MAKEQ(tmp, (2048+85)<<4, (2048+70)<<4, 0, 0);
+ *p++ = tmp;
+ MAKEQ(tmp, 0, 255, 0, 0);
+ *p++ = tmp;
+ MAKEQ(tmp, (2048+260)<<4, (2048+200)<<4, 0, 0);
+ *p++ = tmp;
+ MAKEQ(tmp, 0, 0, 255, 0);
+ *p++ = tmp;
+ MAKEQ(tmp, (2048+180)<<4, (2048+350)<<4, 0, 0);
+ *p++ = tmp;
+ toGIFchain(packetbuf);
+}
+
+void
+printMatrix(rw::Matrix *m)
+{
+ rw::V3d *x = &m->right;
+ rw::V3d *y = &m->up;
+ rw::V3d *z = &m->at;
+ rw::V3d *w = &m->pos;
+ printf(
+ "[ [ %8.4f, %8.4f, %8.4f, %8.4f ]\n"
+ " [ %8.4f, %8.4f, %8.4f, %8.4f ]\n"
+ " [ %8.4f, %8.4f, %8.4f, %8.4f ]\n"
+ " [ %8.4f, %8.4f, %8.4f, %8.4f ] ]\n"
+ " %08x == flags\n",
+ x->x, y->x, z->x, w->x,
+ x->y, y->y, z->y, w->y,
+ x->z, y->z, z->z, w->z,
+ 0.0f, 0.0f, 0.0f, 1.0f,
+ m->flags);
+}
+
+// This is not proper data, just for testing
+void
+setupLight(rw::Atomic *atomic)
+{
+ using namespace rw;
+ Matrix *lightmat;
+ float32 *lp;
+
+ numLightQ = 0;
+ lp = (float32*)lightpacket;
+
+ // TODO: this is the wrong matrix. we actually want to
+ // transform the light, not all normals.
+ lightmat = atomic->getFrame()->getLTM();
+ *lp++ = lightmat->right.x;
+ *lp++ = lightmat->right.y;
+ *lp++ = lightmat->right.z;
+ *lp++ = 0.0f;
+ *lp++ = lightmat->up.x;
+ *lp++ = lightmat->up.y;
+ *lp++ = lightmat->up.z;
+ *lp++ = 0.0f;
+ *lp++ = lightmat->at.x;
+ *lp++ = lightmat->at.y;
+ *lp++ = lightmat->at.z;
+ *lp++ = 0.0f;
+ *lp++ = lightmat->pos.x;
+ *lp++ = lightmat->pos.y;
+ *lp++ = lightmat->pos.z;
+ *lp++ = 1.0f;
+ // TODO: make a proper light block
+ // ambient
+ *lp++ = 80.0f;
+ *lp++ = 80.0f;
+ *lp++ = 80.0f;
+ *lp++ = 0.0f;
+ // directional
+ *lp++ = 0.5f;
+ *lp++ = -0.5f;
+ *lp++ = -0.7071f;
+ *lp++ = 0.0f;
+ numLightQ = 6;
+}
+
+void
+setupTransform(rw::Atomic *atomic, rw::Matrix *trans)
+{
+ rw::Matrix::mult(trans, atomic->getFrame()->getLTM(), &camera->viewMatrix);
+}
+
+enum {
+ DMAcnt = 0x10000000,
+ DMAref = 0x30000000,
+ DMAcall = 0x50000000,
+ DMAret = 0x60000000,
+ DMAend = 0x70000000,
+
+ V4_32 = 0x6C
+};
+
+#define UNPACK(type, nq, offset) ((type)<<24 | (nq)<<16 | (offset))
+#define STCYCL(WL,CL) (0x01000000 | (WL)<<8 | (CL))
+
+void
+drawAtomic(rw::Atomic *atomic)
+{
+ using namespace rw;
+
+ Matrix trans;
+ Geometry *geo;
+ ps2::ObjPipeline *pipe;
+ ps2::MatPipeline *matpipe;
+ Material *material;
+ uint128 tmp, *lp;
+ uint32 *vec;
+ RGBAf color;
+ int i;
+
+ geo = atomic->geometry;
+ pipe = (ps2::ObjPipeline*)atomic->getPipeline();
+ if(pipe->platform != PLATFORM_PS2)
+ return;
+
+ setupLight(atomic);
+ setupTransform(atomic, &trans);
+
+ curVifPtr = packetbuf;
+ // upload lights
+ MAKEQ(tmp, DMAcnt | numLightQ+8, 0, STCYCL(4,4), UNPACK(V4_32, numLightQ, 0x3d0));
+ *curVifPtr++ = tmp;
+ for(lp = lightpacket; numLightQ--;)
+ *curVifPtr++ = *lp++;
+
+ // upload transformation matrix
+ MAKEQ(tmp, 0, 0, STCYCL(4,4), UNPACK(V4_32, 4, 0x3f0));
+ *curVifPtr++ = tmp;
+ vec = (uint32*)&trans.right;
+ MAKEQ(tmp, vec[0], vec[1], vec[2], vec[2]);
+ *curVifPtr++ = tmp;
+ vec = (uint32*)&trans.up;
+ MAKEQ(tmp, vec[0], vec[1], vec[2], vec[2]);
+ *curVifPtr++ = tmp;
+ vec = (uint32*)&trans.at;
+ MAKEQ(tmp, vec[0], vec[1], vec[2], vec[2]);
+ *curVifPtr++ = tmp;
+ vec = (uint32*)&trans.pos;
+ MAKEQ(tmp, vec[0], vec[1], vec[2], vec[2]);
+ *curVifPtr++ = tmp;
+
+ // upload camera/screen info
+ MAKEQ(tmp, 0, 0, STCYCL(4,4), UNPACK(V4_32, 2, 0x3f7));
+ *curVifPtr++ = tmp;
+ *curVifPtr++ = vuXYZScale;
+ *curVifPtr++ = vuXYZOffset;
+
+ assert(geo->instData != NULL);
+ rw::ps2::InstanceDataHeader *instData =
+ (rw::ps2::InstanceDataHeader*)geo->instData;
+ rw::MeshHeader *meshHeader = geo->meshHeader;
+ rw::Mesh *mesh;
+ for(i = 0; i < instData->numMeshes; i++){
+ material = instData->instanceMeshes[i].material;
+ matpipe = pipe->groupPipeline;
+ if(matpipe == nil)
+ matpipe = (ps2::MatPipeline*)material->pipeline;
+ if(matpipe == nil)
+ matpipe = ps2::defaultMatPipe;
+
+ // call vu code
+ MAKEQ(tmp, DMAcall, (uint32)skinPipe, 0, 0);
+ *curVifPtr++ = tmp;
+ // unpack GIF tag, material color, surface properties
+ MAKEQ(tmp, DMAcnt | 3, 0, STCYCL(4,4), UNPACK(V4_32, 3, 0x3fa));
+ *curVifPtr++ = tmp;
+ MAKE128(tmp, 0x412,
+ GIF_MAKE_TAG(0, 1, 1, GS_MAKE_PRIM(GS_PRIM_TRI_STRIP,1,0,0,0,0,0,0,0), GIF_PACKED, 3));
+ *curVifPtr++ = tmp;
+ convColor(&color, &material->color);
+ color.alpha *= 128.0f/255.0f;
+ MAKEQ(tmp, *(uint32*)&color.red, *(uint32*)&color.green,
+ *(uint32*)&color.blue, *(uint32*)&color.alpha);
+ *curVifPtr++ = tmp;
+ MAKEQ(tmp, *(uint32*)&material->surfaceProps.ambient,
+ *(uint32*)&material->surfaceProps.specular,
+ *(uint32*)&material->surfaceProps.diffuse,
+ 0.0f); // extra
+ *curVifPtr++ = tmp;
+ // call geometry
+ MAKEQ(tmp, DMAcall, (uint32)instData->instanceMeshes[i].data, 0x03000000, 0x02000000 | matpipe->vifOffset);
+ *curVifPtr++ = tmp;
+ }
+ MAKEQ(tmp, DMAend, 0, 0, 0);
+ *curVifPtr++ = tmp;
+// for(lp = packetbuf; lp < curVifPtr; lp++)
+// printquad4(*lp);
+ toVIF1chain(packetbuf);
+}
+
+void
+beginCamera(void)
+{
+ uint128 *p, tmp;
+ float32 *f;
+
+ p = packetbuf;
+ MAKE128(tmp, 0xe, GIF_MAKE_TAG(2, 1, 0, 0, GIF_PACKED, 1));
+ *p++ = tmp;
+ MAKE128(tmp, GS_XYOFFSET_1, GS_MAKE_XYOFFSET(2048-WIDTH/2 <<4, 2048-HEIGHT/2 <<4));
+ *p++ = tmp;
+ MAKE128(tmp, GS_TEST_1, GS_MAKE_TEST(0, 0, 0, 0, 0, 0, 1, 2));
+ *p++ = tmp;
+ toGIF(packetbuf, 3);
+ f = (float32*)&vuXYZScale;
+ f[0] = WIDTH;
+ f[1] = HEIGHT;
+ f[2] = camera->zScale;
+ f[3] = 0.0f;
+ f = (float32*)&vuXYZOffset;
+ f[0] = 2048.0f;
+ f[1] = 2048.0f;
+ f[2] = camera->zShift;
+ f[3] = 0.0f;
+}
+
+rw::EngineOpenParams engineOpenParams;
+
+void
+pluginattach(void)
+{
+ rw::ps2::registerPDSPlugin(40);
+ rw::ps2::registerPluginPDSPipes();
+
+ rw::registerMeshPlugin();
+ rw::registerNativeDataPlugin();
+ rw::registerAtomicRightsPlugin();
+ rw::registerMaterialRightsPlugin();
+ rw::xbox::registerVertexFormatPlugin();
+ rw::registerSkinPlugin();
+ rw::registerHAnimPlugin();
+ rw::registerMatFXPlugin();
+ rw::registerUVAnimPlugin();
+ rw::ps2::registerADCPlugin();
+}
+
+bool32
+initrw(void)
+{
+ rw::version = 0x34000;
+ rw::platform = rw::PLATFORM_PS2;
+ if(!rw::Engine::init())
+ return 0;
+ pluginattach();
+ if(!rw::Engine::open(&engineOpenParams))
+ return 0;
+ if(!rw::Engine::start())
+ return 0;
+ rw::Texture::setLoadTextures(0);
+
+ rw::TexDictionary::setCurrent(rw::TexDictionary::create());
+ rw::Image::setSearchPath(".");
+
+ world = rw::World::create();
+ camera = rw::Camera::create();
+ camera->frameBuffer = rw::Raster::create(WIDTH, HEIGHT, 0, rw::Raster::CAMERA);
+ camera->zBuffer = rw::Raster::create(WIDTH, HEIGHT, 0, rw::Raster::ZBUFFER);
+ camera->setFrame(rw::Frame::create());
+ rw::V3d t = { 0.0f, 0.0f, -4.0f };
+// rw::V3d t = { 0.0f, 0.0f, -40.0f };
+ camera->getFrame()->translate(&t, rw::COMBINEPOSTCONCAT);
+ rw::V3d axis = { 0.0f, 1.0f, 0.0f };
+ camera->getFrame()->rotate(&axis, 40.0f, rw::COMBINEPOSTCONCAT);
+ camera->setNearPlane(0.1f);
+ camera->setFarPlane(450.0f);
+ camera->setFOV(60.0f, 4.0f/3.0f);
+ world->addCamera(camera);
+ return 1;
+}
+
+int vsynchInt = 0;
+
+int
+vsynch(int id)
+{
+ vsynchInt = 1;
+ frames++;
+ ExitHandler();
+ return 0;
+}
+
+int
+main()
+{
+ FlushCache(0);
+ if(!initrw()){
+ printf("init failed!\n");
+ for(;;);
+ }
+
+ rw::uint32 len;
+ rw::uint8 *data = rw::getFileContents("host:player.DFF", &len);
+// rw::uint8 *data = rw::getFileContents("host:od_newscafe_dy.dff", &len);
+ rw::StreamMemory in;
+ in.open(data, len);
+ rw::findChunk(&in, rw::ID_CLUMP, NULL, NULL);
+ rw::Clump *clump = rw::Clump::streamRead(&in);
+ in.close();
+ rwFree(data);
+
+ GsCtx gsCtx;
+
+ dmaReset();
+
+// GsResetCrt(GS_NONINTERLACED, GS_NTSC, 0);
+// GsInitCtx(&gsCtx, 640, 224, GS_PSMCT32, GS_PSMZ32);
+
+// GsResetCrt(GS_INTERLACED, GS_NTSC, GS_FRAME);
+// GsInitCtx(&gsCtx, 640, 224, GS_PSMCT32, GS_PSMZ32);
+
+ GsResetCrt(GS_INTERLACED, VMODE, GS_FIELD);
+ GsInitCtx(&gsCtx, WIDTH, HEIGHT, GS_PSMCT32, GS_PSMZ32);
+
+ initrender();
+
+ AddIntcHandler(2, vsynch, 0);
+ EnableIntc(2);
+
+ GsPutDrawCtx(&gsCtx.draw[0]);
+ GsPutDispCtx(&gsCtx.disp[1]);
+ // PCSX2 needs a delay for some reason
+ { int i; for(i = 0; i < 1000000; i++); }
+ clearscreen(0x80, 0x80, 0x80);
+// drawtest();
+// drawtri();
+
+ float angle = 40.0f;
+ int drawbuf = 0;
+ int dispbuf = 1;
+ for(;;){
+ clearscreen(0x80, 0x80, 0x80);
+
+ rw::V3d t = { 0.0f, 0.0f, -4.0f };
+ camera->getFrame()->translate(&t, rw::COMBINEREPLACE);
+ rw::V3d axis = { 0.0f, 1.0f, 0.0f };
+ camera->getFrame()->rotate(&axis, angle, rw::COMBINEPOSTCONCAT);
+ angle += 1.0f;
+
+ camera->beginUpdate();
+ beginCamera();
+ FORLIST(lnk, clump->atomics)
+ drawAtomic(rw::Atomic::fromClump(lnk));
+ camera->endUpdate();
+ printf("");
+
+ while(vsynchInt == 0);
+ GsPutDrawCtx(&gsCtx.draw[drawbuf]);
+ GsPutDispCtx(&gsCtx.disp[dispbuf]);
+ drawbuf = !drawbuf;
+ dispbuf = !dispbuf;
+ vsynchInt = 0;
+ }
+/*
+ camera->beginUpdate();
+ beginCamera();
+ FORLIST(lnk, clump->atomics)
+ drawAtomic(rw::Atomic::fromClump(lnk));
+ camera->endUpdate();
+
+ printf("hello %p\n", clump);
+ for(;;)
+ printf("");
+*/
+
+ return 0;
+}
diff --git a/tools/ps2test/mem.h b/tools/ps2test/mem.h
new file mode 100644
index 0000000..088c1d4
--- /dev/null
+++ b/tools/ps2test/mem.h
@@ -0,0 +1,95 @@
+/* FIFOs */
+#define VIF0_FIFO (*(volatile uint128*)0x10004000)
+#define VIF1_FIFO (*(volatile uint128*)0x10005000)
+#define GIF_FIFO (*(volatile uint128*)0x10006000)
+#define IPU_out_FIFO (*(volatile uint128*)0x10007000)
+#define IPU_in_FIFO (*(volatile uint128*)0x10007010)
+
+/* DMA channels */
+// to VIF0
+#define D0_CHCR (*(volatile uint32*)0x10008000)
+#define D0_MADR (*(volatile uint32*)0x10008010)
+#define D0_QWC (*(volatile uint32*)0x10008020)
+#define D0_TADR (*(volatile uint32*)0x10008030)
+#define D0_ASR0 (*(volatile uint32*)0x10008040)
+#define D0_ASR1 (*(volatile uint32*)0x10008050)
+// VIF1
+#define D1_CHCR (*(volatile uint32*)0x10009000)
+#define D1_MADR (*(volatile uint32*)0x10009010)
+#define D1_QWC (*(volatile uint32*)0x10009020)
+#define D1_TADR (*(volatile uint32*)0x10009030)
+#define D1_ASR0 (*(volatile uint32*)0x10009040)
+#define D1_ASR1 (*(volatile uint32*)0x10009050)
+// to GIF
+#define D2_CHCR (*(volatile uint32*)0x1000a000)
+#define D2_MADR (*(volatile uint32*)0x1000a010)
+#define D2_QWC (*(volatile uint32*)0x1000a020)
+#define D2_TADR (*(volatile uint32*)0x1000a030)
+#define D2_ASR0 (*(volatile uint32*)0x1000a040)
+#define D2_ASR1 (*(volatile uint32*)0x1000a050)
+// fromIPU
+#define D3_CHCR (*(volatile uint32*)0x1000b000)
+#define D3_MADR (*(volatile uint32*)0x1000b010)
+#define D3_QWC (*(volatile uint32*)0x1000b020)
+// toIPU
+#define D4_CHCR (*(volatile uint32*)0x1000b400)
+#define D4_MADR (*(volatile uint32*)0x1000b410)
+#define D4_QWC (*(volatile uint32*)0x1000b420)
+#define D4_TADR (*(volatile uint32*)0x1000b430)
+// from SIF0
+#define D5_CHCR (*(volatile uint32*)0x1000c000)
+#define D5_MADR (*(volatile uint32*)0x1000c010)
+#define D5_QWC (*(volatile uint32*)0x1000c020)
+// to SIF1
+#define D6_CHCR (*(volatile uint32*)0x1000c400)
+#define D6_MADR (*(volatile uint32*)0x1000c410)
+#define D6_QWC (*(volatile uint32*)0x1000c420)
+#define D6_TADR (*(volatile uint32*)0x1000c430)
+// SIF2
+#define D7_CHCR (*(volatile uint32*)0x1000c800)
+#define D7_MADR (*(volatile uint32*)0x1000c810)
+#define D7_QWC (*(volatile uint32*)0x1000c820)
+// fromSPR
+#define D8_CHCR (*(volatile uint32*)0x1000d000)
+#define D8_MADR (*(volatile uint32*)0x1000d010)
+#define D8_QWC (*(volatile uint32*)0x1000d020)
+#define D8_SADR (*(volatile uint32*)0x1000d080)
+// toSPR
+#define D9_CHCR (*(volatile uint32*)0x1000d400)
+#define D9_MADR (*(volatile uint32*)0x1000d410)
+#define D9_QWC (*(volatile uint32*)0x1000d420)
+#define D9_TADR (*(volatile uint32*)0x1000d430)
+#define D9_SADR (*(volatile uint32*)0x1000d480)
+
+/* DMA controller */
+#define D_CTRL (*(volatile uint32*)0x1000e000)
+#define D_STAT (*(volatile uint32*)0x1000e010)
+#define D_PCR (*(volatile uint32*)0x1000e020)
+#define D_SQWC (*(volatile uint32*)0x1000e030)
+#define D_RBSR (*(volatile uint32*)0x1000e040)
+#define D_RBOR (*(volatile uint32*)0x1000e050)
+#define D_STADR (*(volatile uint32*)0x1000e060)
+#define D_ENABLER (*(volatile uint32*)0x1000f520)
+#define D_ENABLEW (*(volatile uint32*)0x1000f590)
+
+
+/* GS privileged registers */
+#define GS_PMODE (*(volatile uint64*)0x12000000)
+#define GS_SMODE1 (*(volatile uint64*)0x12000010)
+#define GS_SMODE2 (*(volatile uint64*)0x12000020)
+#define GS_SRFSH (*(volatile uint64*)0x12000030)
+#define GS_SYNCH1 (*(volatile uint64*)0x12000040)
+#define GS_SYNCH2 (*(volatile uint64*)0x12000050)
+#define GS_SYNCV (*(volatile uint64*)0x12000060)
+#define GS_DISPFB1 (*(volatile uint64*)0x12000070)
+#define GS_DISPLAY1 (*(volatile uint64*)0x12000080)
+#define GS_DISPFB2 (*(volatile uint64*)0x12000090)
+#define GS_DISPLAY2 (*(volatile uint64*)0x120000a0)
+#define GS_EXTBUF (*(volatile uint64*)0x120000b0)
+#define GS_EXTDATA (*(volatile uint64*)0x120000c0)
+#define GS_EXTWRITE (*(volatile uint64*)0x120000d0)
+#define GS_BGCOLOR (*(volatile uint64*)0x120000e0)
+#define GS_CSR (*(volatile uint64*)0x12001000)
+#define GS_IMR (*(volatile uint64*)0x12001010)
+#define GS_BUSDIR (*(volatile uint64*)0x12001040)
+#define GS_SIGLBLID (*(volatile uint64*)0x12001080)
diff --git a/tools/ps2test/ps2.h b/tools/ps2test/ps2.h
new file mode 100644
index 0000000..ced015a
--- /dev/null
+++ b/tools/ps2test/ps2.h
@@ -0,0 +1,23 @@
+#include <kernel.h>
+
+typedef int quad __attribute__((mode(TI)));
+typedef int int128 __attribute__((mode(TI)));
+typedef unsigned int uquad __attribute__((mode(TI)));
+typedef unsigned int uint128 __attribute__((mode(TI)));
+
+#define MAKE128(RES,MSB,LSB) \
+ __asm__ ( "pcpyld %0, %1, %2" : "=r" (RES) : "r" ((uint64)MSB), "r" ((uint64)LSB))
+#define UINT64(LOW,HIGH) (((uint64)HIGH)<<32 | ((uint64)LOW))
+#define MAKEQ(RES,W0,W1,W2,W3) MAKE128(RES,UINT64(W2,W3),UINT64(W0,W1))
+
+#define BIT64(v,s) (((uint64)(v)) << (s))
+
+#include "mem.h"
+#include "gs.h"
+
+extern uint128 packetbuf[128];
+
+void waitDMA(volatile uint32 *chcr);
+void toGIF(void *src, int n);
+
+void drawcube(void);
diff --git a/tools/ps2test/vu/defaultpipe.dsm b/tools/ps2test/vu/defaultpipe.dsm
new file mode 100644
index 0000000..ae3fa39
--- /dev/null
+++ b/tools/ps2test/vu/defaultpipe.dsm
@@ -0,0 +1,93 @@
+.global defaultPipe
+
+.equ vertexTop, 0x3d0
+.equ numInAttribs, 4
+.equ numOutAttribs, 3
+.equ numOutBuf, 2
+.equ vertCount, ((vertexTop-numOutBuf)/(numInAttribs*2+numOutAttribs*numOutBuf))
+.equ offset, (vertCount*numInAttribs)
+.equ outBuf1, (2*offset)
+.equ outSize, ((vertexTop-outBuf1-2)/2)
+.equ outBuf2, (outBuf1+outSize)
+
+.equ lightMat, 0x3d0
+.equ ambientLight, 0x3d4
+.equ lightDir, 0x3d5
+
+.equ matrix, 0x3f0
+.equ XYZScale, 0x3f7
+.equ XYZOffset, 0x3f8
+.equ gifTag, 0x3fa
+.equ matColor, 0x3fb
+.equ surfProps, 0x3fc
+
+
+.balign 16,0
+defaultPipe:
+DMAret *
+MPG 0, *
+.vu
+Start:
+#include "setup_persp.vu"
+Cnt:
+ NOP XTOP VI02 ; input pointer
+ NOP LQ VF01, gifTag(VI00)
+ NOP XITOP VI01 ; vertex count
+ NOP IADDIU VI05, VI00, 0x4000
+ NOP IADD VI05, VI05, VI05
+ NOP IOR VI05, VI05, VI01
+ NOP SQ VF01, 0(VI12)
+ NOP ISW.x VI05, 0(VI12)
+ NOP IADDIU VI03, VI12, 1 ; output pointer
+ NOP LQ VF18, lightMat(VI00)
+ NOP LQ VF19, lightMat+1(VI00)
+ NOP LQ VF20, lightMat+2(VI00)
+
+Loop:
+ NOP LQI VF01, (VI02++) ; vertex
+ NOP LQI VF02, (VI02++) ; UV
+ NOP LQI VF03, (VI02++) ; color
+ NOP LQI VF04, (VI02++) ; normal
+
+ MULAw.xyzw ACC, VF31, VF00w NOP ; transform vertex
+ MADDAx.xyw ACC, VF28, VF01x NOP
+ MADDAy.xyw ACC, VF29, VF01y NOP
+ MADDz.xyzw VF01, VF30, VF01z NOP
+ ITOF0 VF03, VF03 NOP
+ ITOF0[I] VF04, VF04 LOI 0.0078125 ; - normal scale
+ NOP NOP
+ NOP DIV Q, VF00w, VF01w
+ NOP WAITQ
+ MULq VF01, VF01, Q NOP ; perspective division
+ MULi VF04, VF04, I NOP ; scale normal
+ NOP MR32.z VF02, VF00
+ NOP NOP
+ SUB.w VF01, VF01, VF01 NOP
+ MULAx.xyz ACC, VF18, VF04x NOP ; transform normal
+ MADDAy.xyz ACC, VF19, VF04y NOP
+ MADDz.xyz VF04, VF20, VF04z NOP
+ ADD.xyz VF01, VF01, VF25 NOP
+ MULq VF02, VF02, Q NOP
+ NOP NOP
+ FTOI0 VF03, VF03 NOP
+ FTOI4 VF01, VF01 NOP
+ NOP SQ VF04, -2(VI02) ; store normal
+ NOP IADDI VI01, VI01, -1
+ NOP SQI VF02, (VI03++) ; STQ
+ NOP SQI VF03, (VI03++) ; color
+ NOP SQI VF01, (VI03++) ; vertex
+ NOP IBNE VI01, VI00, Loop
+ NOP NOP
+
+#include "light.vu"
+
+ NOP XGKICK VI12
+ NOP IADD VI15,VI00,VI12
+ NOP IADD VI12,VI00,VI13
+ NOP[E] IADD VI13,VI00,VI15
+ NOP NOP
+ NOP B Cnt
+ NOP NOP
+
+.EndMPG
+.EndDmaData
diff --git a/tools/ps2test/vu/light.vu b/tools/ps2test/vu/light.vu
new file mode 100644
index 0000000..8f7a31c
--- /dev/null
+++ b/tools/ps2test/vu/light.vu
@@ -0,0 +1,94 @@
+; Ambient light:
+ NOP LQ VF26, ambientLight(VI00)
+ NOP XITOP VI01
+ NOP IADDIU VI03, VI12, 2
+Ambloop:
+ NOP LQ VF03, 0(VI03) ; output color
+ NOP NOP
+ NOP NOP
+ NOP NOP
+ ITOF0 VF03, VF03 NOP
+ NOP NOP
+ NOP NOP
+ NOP NOP
+ ADD.xyz VF03, VF03, VF26 NOP
+ NOP NOP
+ NOP NOP
+ NOP NOP
+ FTOI0 VF03, VF03 NOP
+ NOP IADDI VI01, VI01, -1
+ NOP IADDIU VI03, VI03, numOutAttribs
+ NOP IBNE VI01, VI00, Ambloop
+ NOP SQ VF03, -numOutAttribs(VI03)
+; end amblight
+
+; Direct Light
+ NOP LQ VF26, lightDir(VI00)
+ NOP XITOP VI01
+ NOP XTOP VI02
+ NOP IADDIU VI03, VI12, 2
+ SUB.xyz VF26, VF00, VF26 NOP
+Dirloop:
+ NOP LQ VF01, 3(VI02); ; normal
+ NOP LQ VF02, 0(VI03); ; output color
+ NOP NOP
+ NOP NOP
+ MUL VF03, VF01, VF26 NOP
+ ITOF0 VF02, VF02 NOP
+ NOP NOP
+ NOP NOP
+ ADDy.x VF03, VF03, VF03y NOP
+ NOP NOP
+ NOP NOP
+ NOP NOP
+ ADDz.x VF03, VF03, VF03z NOP
+ NOP NOP
+ NOP NOP
+ NOP NOP
+ MAX.x VF03, VF00, VF03 NOP ; clamp to 0
+ NOP[I] LOI 255
+ NOP NOP
+ NOP NOP
+ MULi.x VF03, VF03, I NOP
+ NOP NOP
+ NOP NOP
+ NOP NOP
+ ADDx.xyz VF02, VF02, VF03x NOP
+ NOP NOP
+ NOP NOP
+ NOP NOP
+ FTOI0 VF02, VF02 NOP
+ NOP IADDI VI01, VI01, -1
+ NOP IADDIU VI02, VI02, numInAttribs
+ NOP IADDIU VI03, VI03, numOutAttribs
+ NOP IBNE VI01, VI00, Dirloop
+ NOP SQ VF02, -numOutAttribs(VI03)
+; end dirlight
+
+; Material color and clamp
+ NOP LQ VF27, matColor(VI00)
+ NOP XITOP VI01
+ NOP IADDIU VI03, VI12, 2
+Colorloop:
+ NOP LQ VF03, 0(VI03)
+ NOP NOP
+ NOP NOP
+ NOP NOP
+ ITOF0 VF03, VF03 NOP
+ NOP NOP
+ NOP NOP
+ NOP NOP
+ MUL VF03, VF03, VF27 NOP
+ NOP[I] LOI 255
+ NOP NOP
+ NOP NOP
+ MINIi VF03, VF03, I NOP
+ NOP NOP
+ NOP NOP
+ NOP NOP
+ FTOI0 VF03, VF03 NOP
+ NOP IADDI VI01, VI01, -1
+ NOP IADDIU VI03, VI03, numOutAttribs
+ NOP IBNE VI01, VI00, Colorloop
+ NOP SQ VF03, -numOutAttribs(VI03)
+; end material color
diff --git a/tools/ps2test/vu/setup_persp.vu b/tools/ps2test/vu/setup_persp.vu
new file mode 100644
index 0000000..b9ea42f
--- /dev/null
+++ b/tools/ps2test/vu/setup_persp.vu
@@ -0,0 +1,39 @@
+/* This is the the projection matrix we start with:
+ * 1/2w 0 ox/2w + 1/2 -ox/2w
+ * 0 -1/2h -oy/2h + 1/2 oy/2h
+ * 0 0 1 0
+ * 0 0 1 0
+ * To get rid of the +1/2 in the combined matrix we
+ * subtract the z-row/2 from the x- and y-rows.
+ *
+ * The z-row is then set to [0 0 0 1] such that multiplication
+ * by XYZscale gives [0 0 0 zScale]. After perspective division
+ * and addition of XYZoffset we then get zScale/w + zShift for z.
+ *
+ * XYZScale scales xy to the resolution and z by zScale.
+ * XYZOffset translates xy to the GS coordinate system (where
+ * [2048, 2048] is the center of the frame buffer) and add zShift to z.
+ */
+
+; constant:
+; VF28-VF31 transformation matrix
+; VF25 XYZ offset
+
+
+ SUB.z VF28, VF28, VF28 LOI 0.5 ; right.z = 0
+ SUB.z VF29, VF29, VF29 LQ VF28, matrix(VI00) ; up.z = 0 - load matrix
+ SUB.z VF30, VF30, VF30 LQ VF29, matrix+1(VI00) ; at.z = 0 - load matrix
+ ADDw.z VF31, VF00, VF00 LQ VF30, matrix+2(VI00) ; at.z = 1 - load matrix
+ NOP LQ VF31, matrix+3(VI00) ; - load matrix
+ MULi.w VF20, VF28, I LQ.xyz VF01, XYZScale(VI00) ; fix matrix - load scale
+ MULi.w VF21, VF29, I NOP ; fix matrix
+ MULi.w VF22, VF30, I NOP ; fix matrix
+ MULi.w VF23, VF31, I NOP ; fix matrix
+ SUBw.xy VF28, VF28, VF20 NOP ; fix matrix
+ SUBw.xy VF29, VF29, VF21 NOP ; fix matrix
+ SUBw.xy VF30, VF30, VF22 NOP ; fix matrix
+ SUBw.xy VF31, VF31, VF23 NOP ; fix matrix
+ MUL.xy VF28, VF28, VF01 LQ.xyz VF25, XYZOffset(VI00) ; scale matrix
+ MUL.xy VF29, VF29, VF01 IADDIU VI12, VI00, outBuf1 ; scale matrix
+ MUL.xy VF30, VF30, VF01 IADDIU VI13, VI00, outBuf2 ; scale matrix
+ MUL.xyz VF31, VF31, VF01 NOP ; scale matrix
diff --git a/tools/ps2test/vu/skinpipe.dsm b/tools/ps2test/vu/skinpipe.dsm
new file mode 100644
index 0000000..18536cd
--- /dev/null
+++ b/tools/ps2test/vu/skinpipe.dsm
@@ -0,0 +1,94 @@
+.global skinPipe
+
+.equ vertexTop, 0x2d0
+.equ numInAttribs, 5
+.equ numOutAttribs, 3
+.equ numOutBuf, 2
+.equ vertCount, ((vertexTop-numOutBuf)/(numInAttribs*2+numOutAttribs*numOutBuf))
+.equ offset, (vertCount*numInAttribs)
+.equ outBuf1, (2*offset)
+.equ outSize, ((vertexTop-outBuf1-2)/2)
+.equ outBuf2, (outBuf1+outSize)
+
+.equ lightMat, 0x3d0
+.equ ambientLight, 0x3d4
+.equ lightDir, 0x3d5
+
+.equ matrix, 0x3f0
+.equ XYZScale, 0x3f7
+.equ XYZOffset, 0x3f8
+.equ gifTag, 0x3fa
+.equ matColor, 0x3fb
+.equ surfProps, 0x3fc
+
+
+.balign 16,0
+skinPipe:
+DMAret *
+MPG 0, *
+.vu
+Start:
+#include "setup_persp.vu"
+Cnt:
+ NOP XTOP VI02 ; input pointer
+ NOP LQ VF01, gifTag(VI00)
+ NOP XITOP VI01 ; vertex count
+ NOP IADDIU VI05, VI00, 0x4000
+ NOP IADD VI05, VI05, VI05
+ NOP IOR VI05, VI05, VI01
+ NOP SQ VF01, 0(VI12)
+ NOP ISW.x VI05, 0(VI12)
+ NOP IADDIU VI03, VI12, 1 ; output pointer
+ NOP LQ VF18, lightMat(VI00)
+ NOP LQ VF19, lightMat+1(VI00)
+ NOP LQ VF20, lightMat+2(VI00)
+
+Loop:
+ NOP LQI VF01, (VI02++) ; vertex
+ NOP LQI VF02, (VI02++) ; UV
+ NOP LQI VF03, (VI02++) ; color
+ NOP LQI VF04, (VI02++) ; normal
+ NOP IADDIU VI02, VI02, 1 ; skip weights
+
+ MULAw.xyzw ACC, VF31, VF00w NOP ; transform vertex
+ MADDAx.xyw ACC, VF28, VF01x NOP
+ MADDAy.xyw ACC, VF29, VF01y NOP
+ MADDz.xyzw VF01, VF30, VF01z NOP
+ ITOF0 VF03, VF03 NOP
+ ITOF0[I] VF04, VF04 LOI 0.0078125 ; - normal scale
+ NOP NOP
+ NOP DIV Q, VF00w, VF01w
+ NOP WAITQ
+ MULq VF01, VF01, Q NOP ; perspective division
+ MULi VF04, VF04, I NOP ; scale normal
+ NOP MR32.z VF02, VF00
+ NOP NOP
+ SUB.w VF01, VF01, VF01 NOP
+ MULAx.xyz ACC, VF18, VF04x NOP ; transform normal
+ MADDAy.xyz ACC, VF19, VF04y NOP
+ MADDz.xyz VF04, VF20, VF04z NOP
+ ADD.xyz VF01, VF01, VF25 NOP
+ MULq VF02, VF02, Q NOP
+ NOP NOP
+ FTOI0 VF03, VF03 NOP
+ FTOI4 VF01, VF01 NOP
+ NOP SQ VF04, -2(VI02) ; store normal
+ NOP IADDI VI01, VI01, -1
+ NOP SQI VF02, (VI03++) ; STQ
+ NOP SQI VF03, (VI03++) ; color
+ NOP SQI VF01, (VI03++) ; vertex
+ NOP IBNE VI01, VI00, Loop
+ NOP NOP
+
+#include "light.vu"
+
+ NOP XGKICK VI12
+ NOP IADD VI15,VI00,VI12
+ NOP IADD VI12,VI00,VI13
+ NOP[E] IADD VI13,VI00,VI15
+ NOP NOP
+ NOP B Cnt
+ NOP NOP
+
+.EndMPG
+.EndDmaData
diff --git a/tools/ska2anm/CMakeLists.txt b/tools/ska2anm/CMakeLists.txt
new file mode 100644
index 0000000..9a315c2
--- /dev/null
+++ b/tools/ska2anm/CMakeLists.txt
@@ -0,0 +1,20 @@
+add_executable(ska2anm
+ ska2anm.cpp
+)
+
+target_link_libraries(ska2anm
+ PUBLIC
+ librw::librw
+)
+
+if(LIBRW_GL3_GFXLIB MATCHES "SDL[23]")
+ target_compile_definitions(ska2anm PRIVATE SDL_MAIN_HANDLED)
+endif()
+
+if(LIBRW_INSTALL)
+ install(TARGETS ska2anm
+ RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
+ )
+endif()
+
+librw_platform_target(ska2anm INSTALL)
diff --git a/tools/ska2anm/ska2anm.cpp b/tools/ska2anm/ska2anm.cpp
new file mode 100644
index 0000000..900ba82
--- /dev/null
+++ b/tools/ska2anm/ska2anm.cpp
@@ -0,0 +1,83 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include <rw.h>
+#include <args.h>
+
+using namespace rw;
+
+char *argv0;
+
+void
+usage(void)
+{
+ fprintf(stderr, "usage: %s in.ska [out.anm]\n", argv0);
+ fprintf(stderr, " or: %s in.anm [out.ska]\n", argv0);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ rw::Engine::init();
+ rw::registerHAnimPlugin();
+ rw::Engine::open(nil);
+ rw::Engine::start();
+
+ ARGBEGIN{
+ case 'v':
+ sscanf(EARGF(usage()), "%x", &rw::version);
+ break;
+ default:
+ usage();
+ }ARGEND;
+
+ if(argc < 1)
+ usage();
+
+ StreamFile stream;
+ if(!stream.open(argv[0], "rb")){
+ fprintf(stderr, "Error: couldn't open %s\n", argv[0]);
+ return 1;
+ }
+
+ int32 firstword = stream.readU32();
+ stream.seek(0, 0);
+ Animation *anim = nil;
+ if(firstword == ID_ANIMANIMATION){
+ // it's an anm file
+ if(findChunk(&stream, ID_ANIMANIMATION, nil, nil))
+ anim = Animation::streamRead(&stream);
+ }else{
+ // it's a ska file
+ anim = Animation::streamReadLegacy(&stream);
+ }
+ stream.close();
+
+ if(anim == nil){
+ fprintf(stderr, "Error: couldn't read anim file\n");
+ return 1;
+ }
+
+ const char *file;
+ if(argc > 1)
+ file = argv[1];
+ else if(firstword == ID_ANIMANIMATION)
+ file = "out.ska";
+ else
+ file = "out.anm";
+ if(!stream.open(file, "wb")){
+ fprintf(stderr, "Error: couldn't open %s\n", file);
+ return 1;
+ }
+ if(firstword == ID_ANIMANIMATION)
+ anim->streamWriteLegacy(&stream);
+ else
+ anim->streamWrite(&stream);
+
+ anim->destroy();
+
+ return 0;
+}
diff --git a/tools/subrast/CMakeLists.txt b/tools/subrast/CMakeLists.txt
new file mode 100644
index 0000000..6393734
--- /dev/null
+++ b/tools/subrast/CMakeLists.txt
@@ -0,0 +1,19 @@
+add_executable(subrast WIN32
+ main.cpp
+ subrast.cpp
+ subrast.h
+)
+
+target_link_libraries(subrast
+ PUBLIC
+ librw::skeleton
+ librw::librw
+)
+
+add_custom_command(
+ TARGET subrast POST_BUILD
+ COMMAND "${CMAKE_COMMAND}" -E make_directory "$<TARGET_FILE_DIR:subrast>/files"
+ COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/files" "$<TARGET_FILE_DIR:subrast>/files"
+)
+
+librw_platform_target(subrast)
diff --git a/tools/subrast/files/clump.dff b/tools/subrast/files/clump.dff
new file mode 100644
index 0000000..b65e41d
--- /dev/null
+++ b/tools/subrast/files/clump.dff
Binary files differ
diff --git a/tools/subrast/files/textures/whiteash.png b/tools/subrast/files/textures/whiteash.png
new file mode 100644
index 0000000..aa60569
--- /dev/null
+++ b/tools/subrast/files/textures/whiteash.png
Binary files differ
diff --git a/tools/subrast/main.cpp b/tools/subrast/main.cpp
new file mode 100644
index 0000000..5dbde8a
--- /dev/null
+++ b/tools/subrast/main.cpp
@@ -0,0 +1,354 @@
+#include <rw.h>
+#include <skeleton.h>
+#include <assert.h>
+
+#include "subrast.h"
+
+rw::V3d zero = { 0.0f, 0.0f, 0.0f };
+rw::EngineOpenParams engineOpenParams;
+float FOV = 70.0f;
+
+rw::RGBA ForegroundColor = { 200, 200, 200, 255 };
+rw::RGBA BackgroundColor = { 64, 64, 64, 0 };
+rw::RGBA BorderColor = { 128, 128, 128, 0 };
+
+rw::Clump *Clump = nil;
+rw::Light *MainLight = nil;
+rw::Light *AmbientLight = nil;
+
+rw::World *World;
+rw::Charset *Charset;
+
+const char *SubCameraCaption[4] = {
+ "Perspective view",
+ "Parallel view: Z-axis",
+ "Parallel view: X-axis",
+ "Parallel view: Y-axis"
+};
+
+rw::V3d Xaxis = { 1.0f, 0.0, 0.0f };
+rw::V3d Yaxis = { 0.0f, 1.0, 0.0f };
+rw::V3d Zaxis = { 0.0f, 0.0, 1.0f };
+
+rw::World*
+CreateWorld(void)
+{
+ rw::BBox bb;
+
+ bb.inf.x = bb.inf.y = bb.inf.z = -100.0f;
+ bb.sup.x = bb.sup.y = bb.sup.z = 100.0f;
+
+ return rw::World::create(&bb);
+}
+
+rw::Light*
+CreateAmbientLight(rw::World *world)
+{
+ rw::Light *light = rw::Light::create(rw::Light::AMBIENT);
+ assert(light);
+ World->addLight(light);
+ return light;
+}
+
+rw::Light*
+CreateMainLight(rw::World *world)
+{
+ rw::Light *light = rw::Light::create(rw::Light::DIRECTIONAL);
+ assert(light);
+ rw::Frame *frame = rw::Frame::create();
+ assert(frame);
+ frame->rotate(&Xaxis, 30.0f, rw::COMBINEREPLACE);
+ frame->rotate(&Yaxis, 30.0f, rw::COMBINEPOSTCONCAT);
+ light->setFrame(frame);
+ World->addLight(light);
+ return light;
+}
+
+rw::Clump*
+CreateClump(rw::World *world)
+{
+ rw::Clump *clump;
+ rw::StreamFile in;
+
+ rw::Image::setSearchPath("files/textures/");
+ const char *filename = "files/clump.dff";
+ if(in.open(filename, "rb") == NULL){
+ printf("couldn't open file\n");
+ return nil;
+ }
+ if(!rw::findChunk(&in, rw::ID_CLUMP, NULL, NULL))
+ return nil;
+ clump = rw::Clump::streamRead(&in);
+ in.close();
+ if(clump == nil)
+ return nil;
+
+ rw::Frame *frame = clump->getFrame();
+ frame->rotate(&Xaxis, -120.0f, rw::COMBINEREPLACE);
+ frame->rotate(&Yaxis, 45.0f, rw::COMBINEPOSTCONCAT);
+ World->addClump(clump);
+ return clump;
+}
+
+void
+RotateClump(float xAngle, float yAngle)
+{
+ rw::Matrix *cameraMatrix = &Camera->getFrame()->matrix;
+ rw::Frame *frame = Clump->getFrame();
+ rw::V3d pos = frame->matrix.pos;
+
+ pos = rw::scale(pos, -1.0f);
+ frame->translate(&pos, rw::COMBINEPOSTCONCAT);
+
+ frame->rotate(&cameraMatrix->up, xAngle, rw::COMBINEPOSTCONCAT);
+ frame->rotate(&cameraMatrix->right, yAngle, rw::COMBINEPOSTCONCAT);
+
+ pos = rw::scale(pos, -1.0f);
+ frame->translate(&pos, rw::COMBINEPOSTCONCAT);
+}
+
+void
+Initialize(void)
+{
+ sk::globals.windowtitle = "Sub-raster example";
+ sk::globals.width = 1280;
+ sk::globals.height = 800;
+ sk::globals.quit = 0;
+}
+
+bool
+Initialize3D(void)
+{
+ if(!sk::InitRW())
+ return false;
+
+ Charset = rw::Charset::create(&ForegroundColor, &BackgroundColor);
+
+ World = CreateWorld();
+
+ AmbientLight = CreateAmbientLight(World);
+ MainLight = CreateMainLight(World);
+ Clump = CreateClump(World);
+ if (Clump == nil)
+ return false;
+
+ CreateCameras(World);
+ UpdateSubRasters(Camera, sk::globals.width, sk::globals.height);
+
+ rw::SetRenderState(rw::CULLMODE, rw::CULLBACK);
+ rw::SetRenderState(rw::ZTESTENABLE, 1);
+ rw::SetRenderState(rw::ZWRITEENABLE, 1);
+
+ ImGui_ImplRW_Init();
+ ImGui::StyleColorsClassic();
+
+ return true;
+}
+
+void
+Terminate3D(void)
+{
+ DestroyCameras(World);
+
+ if(AmbientLight){
+ World->removeLight(AmbientLight);
+ AmbientLight->destroy();
+ AmbientLight = nil;
+ }
+
+ if(MainLight){
+ World->removeLight(MainLight);
+ rw::Frame *frame = MainLight->getFrame();
+ MainLight->setFrame(nil);
+ frame->destroy();
+ MainLight->destroy();
+ MainLight = nil;
+ }
+
+ if(Clump){
+ World->removeClump(Clump);
+ Clump->destroy();
+ Clump = nil;
+ }
+
+ if(World){
+ World->destroy();
+ World = nil;
+ }
+
+ if(Charset){
+ Charset->destroy();
+ Charset = nil;
+ }
+
+ sk::TerminateRW();
+}
+
+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
+DisplayOnScreenInfo(void)
+{
+ for(int i = 0; i < 4; i++){
+ rw::Raster *scr = SubCameras[i]->frameBuffer;
+
+ rw::int32 scrw = scr->width;
+ rw::int32 scrh = scr->height;
+
+ rw::int32 captionWidth = strlen(SubCameraCaption[i])*Charset->desc.width;
+
+ if(captionWidth < scrw && scrh > Charset->desc.height*2){
+ rw::int32 x = scr->offsetX + (scrw - captionWidth)/2;
+ rw::int32 y = scr->offsetY + Charset->desc.height;
+ Charset->print(SubCameraCaption[i], x, y, 0);
+ }
+ }
+}
+
+rw::RGBA BackgroundColors[] = {
+ { 64, 64, 64, 0 },
+ { 128, 0, 0, 0 },
+ { 0, 128, 0, 0 },
+ { 0, 0, 128, 0 },
+};
+void
+Render(float timeDelta)
+{
+ Camera->clear(&BorderColor, rw::Camera::CLEARIMAGE|rw::Camera::CLEARZ);
+
+ for(int i = 0; i < 4; i++){
+ SubCameras[i]->clear(&BackgroundColor, rw::Camera::CLEARIMAGE|rw::Camera::CLEARZ);
+// SubCameras[i]->clear(&BackgroundColors[i], rw::Camera::CLEARIMAGE|rw::Camera::CLEARZ);
+ SubCameras[i]->beginUpdate();
+ World->render();
+ SubCameras[i]->endUpdate();
+ }
+
+ Camera->beginUpdate();
+ DisplayOnScreenInfo();
+ Camera->endUpdate();
+
+ Camera->showRaster(0);
+}
+
+void
+Idle(float timeDelta)
+{
+ Render(timeDelta);
+}
+
+int MouseX, MouseY;
+int MouseDeltaX, MouseDeltaY;
+int MouseButtons;
+
+bool Rotating;
+
+void
+KeyUp(int key)
+{
+}
+
+void
+KeyDown(int key)
+{
+ switch(key){
+ case sk::KEY_ESC:
+ sk::globals.quit = 1;
+ break;
+ }
+}
+
+void
+MouseBtn(sk::MouseState *mouse)
+{
+ MouseButtons = mouse->buttons;
+ Rotating = !!(MouseButtons&1);
+}
+
+void
+MouseMove(sk::MouseState *mouse)
+{
+ MouseDeltaX = mouse->posx - MouseX;
+ MouseDeltaY = mouse->posy - MouseY;
+ MouseX = mouse->posx;
+ MouseY = mouse->posy;
+ if(Rotating)
+ RotateClump(-MouseDeltaX, MouseDeltaY);
+}
+
+sk::EventStatus
+AppEventHandler(sk::Event e, void *param)
+{
+ using namespace sk;
+ Rect *r;
+ MouseState *ms;
+
+ ImGuiEventHandler(e, param);
+ ImGuiIO &io = ImGui::GetIO();
+
+ switch(e){
+ case INITIALIZE:
+ Initialize();
+ return EVENTPROCESSED;
+ case RWINITIALIZE:
+ return Initialize3D() ? EVENTPROCESSED : EVENTERROR;
+ case RWTERMINATE:
+ Terminate3D();
+ return EVENTPROCESSED;
+ case PLUGINATTACH:
+ return attachPlugins() ? EVENTPROCESSED : EVENTERROR;
+ case KEYDOWN:
+ KeyDown(*(int*)param);
+ return EVENTPROCESSED;
+ case KEYUP:
+ KeyUp(*(int*)param);
+ return EVENTPROCESSED;
+ case MOUSEBTN:
+ if(!io.WantCaptureMouse){
+ ms = (MouseState*)param;
+ MouseBtn(ms);
+ }else
+ MouseButtons = 0;
+ return EVENTPROCESSED;
+ case MOUSEMOVE:
+ MouseMove((MouseState*)param);
+ 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){
+ sk::CameraSize(::Camera, r);
+ ::Camera->setFOV(FOV, (float)sk::globals.width/sk::globals.height);
+
+ UpdateSubRasters(::Camera, r->w, r->h);
+ }
+ break;
+ case IDLE:
+ Idle(*(float*)param);
+ return EVENTPROCESSED;
+ }
+ return sk::EVENTNOTPROCESSED;
+}
diff --git a/tools/subrast/subrast.cpp b/tools/subrast/subrast.cpp
new file mode 100644
index 0000000..f91298f
--- /dev/null
+++ b/tools/subrast/subrast.cpp
@@ -0,0 +1,141 @@
+#include <rw.h>
+#include <skeleton.h>
+
+#include "subrast.h"
+
+rw::Camera *Camera;
+rw::Camera *SubCameras[4];
+
+void
+CameraSetViewWindow(rw::Camera *camera, float width, float height, float vw)
+{
+ rw::V2d viewWindow;
+
+ // TODO: aspect ratio when fullscreen
+ if(width > height){
+ viewWindow.x = vw;
+ viewWindow.y = vw / (width/height);
+ }else{
+ viewWindow.x = vw / (height/width);
+ viewWindow.y = vw;
+ }
+
+ camera->setViewWindow(&viewWindow);
+}
+
+void
+UpdateSubRasters(rw::Camera *mainCamera, rw::int32 mainWidth, rw::int32 mainHeight)
+{
+ rw::Rect rect[4];
+ float width, height, border;
+
+ border = mainHeight*0.05f;
+
+ width = (mainWidth - border*3.0f) / 2.0f;
+ height = (mainHeight - border*3.0f) / 2.0f;
+
+ // top left
+ rect[0].x = border;
+ rect[0].y = border;
+ rect[0].w = width;
+ rect[0].h = height;
+
+ // top right
+ rect[1].x = border*2 + width;
+ rect[1].y = border;
+ rect[1].w = width;
+ rect[1].h = height;
+
+ // bottom left
+ rect[2].x = border;
+ rect[2].y = border*2 + height;
+ rect[2].w = width;
+ rect[2].h = height;
+
+ // bottom left
+ rect[3].x = border*2 + width;
+ rect[3].y = border*2 + height;
+ rect[3].w = width;
+ rect[3].h = height;
+
+ CameraSetViewWindow(SubCameras[0], width, height, 0.5f);
+ for(int i = 1; i < 4; i++)
+ CameraSetViewWindow(SubCameras[i], width, height, 0.5f + 0.4f);
+
+ for(int i = 0; i < 4; i++){
+ SubCameras[i]->frameBuffer->subRaster(mainCamera->frameBuffer, &rect[i]);
+ SubCameras[i]->zBuffer->subRaster(mainCamera->zBuffer, &rect[i]);
+ }
+}
+
+void
+PositionSubCameras(void)
+{
+ rw::Frame *frame;
+ rw::V3d pos;
+ const float dist = 2.5f;
+
+ // perspective
+ pos.x = pos.y = 0.0f;
+ pos.z = -4.0f;
+ frame = SubCameras[0]->getFrame();
+ frame->translate(&pos, rw::COMBINEREPLACE);
+
+ // look along z
+ pos.x = pos.y = 0.0f;
+ pos.z = -dist;
+ frame = SubCameras[1]->getFrame();
+ frame->translate(&pos, rw::COMBINEREPLACE);
+
+ // look along x
+ pos.x = -dist;
+ pos.y = pos.z = 0.0f;
+ frame = SubCameras[2]->getFrame();
+ frame->rotate(&Yaxis, 90.0f, rw::COMBINEREPLACE);
+ frame->translate(&pos, rw::COMBINEPOSTCONCAT);
+
+ // look along y
+ pos.x = pos.z = 0.0f;
+ pos.y = -dist;
+ frame = SubCameras[3]->getFrame();
+ frame->rotate(&Xaxis, -90.0f, rw::COMBINEREPLACE);
+ frame->translate(&pos, rw::COMBINEPOSTCONCAT);
+}
+
+void
+CreateCameras(rw::World *world)
+{
+ Camera = sk::CameraCreate(sk::globals.width, sk::globals.height, 1);
+ assert(Camera);
+
+ for(int i = 0; i < 4; i++){
+ SubCameras[i] = sk::CameraCreate(0, 0, 1);
+ assert(SubCameras[i]);
+
+ SubCameras[i]->setNearPlane(0.1f);
+ SubCameras[i]->setFarPlane(30.0f);
+
+ world->addCamera(SubCameras[i]);
+
+ if(i > 0)
+ SubCameras[i]->setProjection(rw::Camera::PARALLEL);
+ }
+
+ PositionSubCameras();
+}
+
+void
+DestroyCameras(rw::World *world)
+{
+ if(Camera){
+ sk::CameraDestroy(Camera);
+ Camera = nil;
+ }
+
+ for(int i = 0; i < 4; i++)
+ if(SubCameras[i]){
+ world->removeCamera(SubCameras[i]);
+ sk::CameraDestroy(SubCameras[i]);
+ SubCameras[i] = nil;
+ }
+}
diff --git a/tools/subrast/subrast.h b/tools/subrast/subrast.h
new file mode 100644
index 0000000..b7c8537
--- /dev/null
+++ b/tools/subrast/subrast.h
@@ -0,0 +1,10 @@
+extern rw::Camera *Camera;
+extern rw::Camera *SubCameras[4];
+
+void CreateCameras(rw::World *world);
+void DestroyCameras(rw::World *world);
+void UpdateSubRasters(rw::Camera *mainCamera, rw::int32 mainWidth, rw::int32 mainHeight);
+
+extern rw::V3d Xaxis;
+extern rw::V3d Yaxis;
+extern rw::V3d Zaxis;