summaryrefslogtreecommitdiff
path: root/tools/hopalong/main.cpp
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/hopalong/main.cpp
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/hopalong/main.cpp')
-rw-r--r--tools/hopalong/main.cpp469
1 files changed, 469 insertions, 0 deletions
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;
+}