diff --git a/.gitignore b/.gitignore index 27e98c9..e1064b3 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,13 @@ PSX.Dev-README.md *.a .cache/ -.vscode/ +.vscode/* +!.vscode/launch.json +!.vscode/tasks.json .editorconfig .clang-format compile_commands.json +LICENSEE.DAT +*.iso +system.cnf +iso.xml \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..6edd986 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,29 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug", + "type": "gdb", + "request": "attach", + "target": "localhost:3333", + "remote": true, + "cwd": "${workspaceRoot}", + "valuesFormatting": "parseText", + "stopAtConnect": true, + "gdbpath": "gdb-multiarch", + "windows": { + "gdbpath": "gdb-multiarch.exe" + }, + "osx": { + "gdbpath": "gdb" + }, + "executable": "${workspaceRoot}/${workspaceRootFolderName}.elf", + "autorun": [ + "monitor reset shellhalt", + "load ${workspaceRootFolderName}.elf", + "tbreak main", + "continue" + ] + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..b46ac12 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,37 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Build Debug", + "type": "shell", + "command": "make BUILD=Debug", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [ + "$gcc" + ] + }, + { + "label": "Build Release", + "type": "shell", + "command": "make", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [ + "$gcc" + ] + }, + { + "label": "Clean", + "type": "shell", + "command": "make clean", + "group": { + "kind": "build" + } + } + ] +} \ No newline at end of file diff --git a/Makefile b/Makefile index 181d8a4..fdfcb64 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,7 @@ src/main.cpp \ src/renderer.cpp \ src/splashpack.cpp \ src/camera.cpp \ +src/gtemath.cpp \ output.o include third_party/nugget/psyqo/psyqo.mk diff --git a/output.bin b/output.bin index b3f9207..e80ce2c 100644 Binary files a/output.bin and b/output.bin differ diff --git a/src/camera.cpp b/src/camera.cpp index 5570b32..5649944 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -6,37 +6,20 @@ #include psxsplash::Camera::Camera() { + // Load identity m_rotationMatrix = psyqo::SoftMath::generateRotationMatrix33(0, psyqo::SoftMath::Axis::X, m_trig); } -void psxsplash::Camera::moveX(psyqo::FixedPoint<12> x) { m_rotation.x += -x; } +void psxsplash::Camera::moveX(psyqo::FixedPoint<12> x) { m_position.x += -x; } -void psxsplash::Camera::moveY(psyqo::FixedPoint<12> y) { m_rotation.y += -y; } +void psxsplash::Camera::moveY(psyqo::FixedPoint<12> y) { m_position.y += -y; } -void psxsplash::Camera::moveZ(psyqo::FixedPoint<12> z) { m_rotation.z += -z; } +void psxsplash::Camera::moveZ(psyqo::FixedPoint<12> z) { m_position.z += -z; } void psxsplash::Camera::setPosition(psyqo::FixedPoint<12> x, psyqo::FixedPoint<12> y, psyqo::FixedPoint<12> z) { - m_rotation.x = -x; - m_rotation.y = -y; - m_rotation.z = -z; -} - -void psxsplash::Camera::rotateX(psyqo::Angle x) { - auto rot = psyqo::SoftMath::generateRotationMatrix33(-x, psyqo::SoftMath::Axis::X, m_trig); - - psyqo::SoftMath::multiplyMatrix33(m_rotationMatrix, rot, &m_rotationMatrix); -} - -void psxsplash::Camera::rotateY(psyqo::Angle y) { - auto rot = psyqo::SoftMath::generateRotationMatrix33(-y, psyqo::SoftMath::Axis::Y, m_trig); - - psyqo::SoftMath::multiplyMatrix33(m_rotationMatrix, rot, &m_rotationMatrix); -} - -void psxsplash::Camera::rotateZ(psyqo::Angle z) { - auto rot = psyqo::SoftMath::generateRotationMatrix33(-z, psyqo::SoftMath::Axis::Y, m_trig); - - psyqo::SoftMath::multiplyMatrix33(m_rotationMatrix, rot, &m_rotationMatrix); + m_position.x = x; + m_position.y = y; + m_position.z = z; } void psxsplash::Camera::setRotation(psyqo::Angle x, psyqo::Angle y, psyqo::Angle z) { @@ -44,9 +27,10 @@ void psxsplash::Camera::setRotation(psyqo::Angle x, psyqo::Angle y, psyqo::Angle auto rotY = psyqo::SoftMath::generateRotationMatrix33(y, psyqo::SoftMath::Axis::Y, m_trig); auto rotZ = psyqo::SoftMath::generateRotationMatrix33(z, psyqo::SoftMath::Axis::Z, m_trig); + // XYZ multiplication order (matches C#) psyqo::SoftMath::multiplyMatrix33(rotY, rotX, &rotY); - psyqo::SoftMath::multiplyMatrix33(rotY, rotZ, &rotY); + m_rotationMatrix = rotY; } diff --git a/src/camera.hh b/src/camera.hh index 5c98b28..0d404df 100644 --- a/src/camera.hh +++ b/src/camera.hh @@ -1,7 +1,5 @@ #pragma once -#include - #include #include #include @@ -17,12 +15,7 @@ class Camera { void moveZ(psyqo::FixedPoint<12> y); void setPosition(psyqo::FixedPoint<12> x, psyqo::FixedPoint<12> y, psyqo::FixedPoint<12> z); - - psyqo::Vec3& getPosition() { return m_rotation; } - - void rotateX(psyqo::Angle x); - void rotateY(psyqo::Angle y); - void rotateZ(psyqo::Angle z); + psyqo::Vec3& getPosition() { return m_position; } void setRotation(psyqo::Angle x, psyqo::Angle y, psyqo::Angle z); psyqo::Matrix33& getRotation(); @@ -30,6 +23,6 @@ class Camera { private: psyqo::Matrix33 m_rotationMatrix; psyqo::Trig<> m_trig; - psyqo::Vec3 m_rotation; + psyqo::Vec3 m_position; }; } // namespace psxsplash \ No newline at end of file diff --git a/src/gameobject.hh b/src/gameobject.hh index 9be7679..12ad5a9 100644 --- a/src/gameobject.hh +++ b/src/gameobject.hh @@ -10,15 +10,13 @@ namespace psxsplash { class GameObject final { public: - psyqo::Vec3 position; // 12 bytes - psyqo::Matrix33 rotation; // 36 bytes - uint16_t polyCount; // 2 bytes - psyqo::PrimPieces::TPageAttr texture; // 2 bytes - uint16_t clutX, clutY; - uint16_t clut[256]; - union { // 4 bytes + psyqo::Vec3 position; + psyqo::Matrix33 rotation; + union { Tri *polygons; uint32_t polygonsOffset; }; + int polyCount; }; +static_assert(sizeof(GameObject) == 56, "GameObject is not 56 bytes"); } // namespace psxsplash \ No newline at end of file diff --git a/src/gtemath.cpp b/src/gtemath.cpp new file mode 100644 index 0000000..a782f03 --- /dev/null +++ b/src/gtemath.cpp @@ -0,0 +1,37 @@ +#include "gtemath.hh" + +#include +#include + +using namespace psyqo::GTE; + +void psxsplash::matrixMultiplyGTE(const psyqo::Matrix33 &matA, const psyqo::Matrix33 &matB, psyqo::Matrix33 *result) { + writeSafe(matA); + + psyqo::Vec3 t; + + psyqo::GTE::writeSafe(psyqo::Vec3{matB.vs[0].x, matB.vs[1].x, matB.vs[2].x}); + + psyqo::GTE::Kernels::mvmva(); + + t = psyqo::GTE::readSafe(); + result->vs[0].x = t.x; + result->vs[1].x = t.y; + result->vs[2].x = t.z; + + psyqo::GTE::writeSafe(psyqo::Vec3{matB.vs[0].y, matB.vs[1].y, matB.vs[2].y}); + + psyqo::GTE::Kernels::mvmva(); + t = psyqo::GTE::readSafe(); + result->vs[0].y = t.x; + result->vs[1].y = t.y; + result->vs[2].y = t.z; + + psyqo::GTE::writeSafe(psyqo::Vec3{matB.vs[0].z, matB.vs[1].z, matB.vs[2].z}); + + psyqo::GTE::Kernels::mvmva(); + t = psyqo::GTE::readSafe(); + result->vs[0].z = t.x; + result->vs[1].z = t.y; + result->vs[2].z = t.z; +} \ No newline at end of file diff --git a/src/gtemath.hh b/src/gtemath.hh new file mode 100644 index 0000000..d5bec9d --- /dev/null +++ b/src/gtemath.hh @@ -0,0 +1,5 @@ +#include + +namespace psxsplash { +void matrixMultiplyGTE(const psyqo::Matrix33 &matA, const psyqo::Matrix33 &matB, psyqo::Matrix33 *result); +}; diff --git a/src/main.cpp b/src/main.cpp index 0ef43c8..c7d43ca 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -15,6 +15,7 @@ #include "renderer.hh" #include "splashpack.hh" +// Data from the splashpack extern uint8_t _binary_output_bin_start[]; namespace { @@ -58,6 +59,8 @@ void PSXSplash::prepare() { .set(psyqo::GPU::ColorMode::C15BITS) .set(psyqo::GPU::Interlace::PROGRESSIVE); gpu().initialize(config); + + // Initialize the Renderer singleton psxsplash::Renderer::init(gpu()); } @@ -75,68 +78,71 @@ void MainScene::start(StartReason reason) { void MainScene::frame() { uint32_t beginFrame = gpu().now(); auto currentFrameCounter = gpu().getFrameCount(); - auto frameDiff = currentFrameCounter - mainScene.m_lastFrameCounter; - if (frameDiff == 0) { + auto deltaTime = currentFrameCounter - mainScene.m_lastFrameCounter; + + // Unlike the torus example, this DOES happen... + if (deltaTime == 0) { return; } + mainScene.m_lastFrameCounter = currentFrameCounter; auto& input = psxSplash.m_input; if (input.isButtonPressed(psyqo::AdvancedPad::Pad::Pad1a, psyqo::AdvancedPad::Right)) { - m_mainCamera.moveX((m_trig.cos(camRotY) * moveSpeed)); - m_mainCamera.moveZ(-(m_trig.sin(camRotY) * moveSpeed)); + m_mainCamera.moveX((m_trig.cos(camRotY) * moveSpeed * deltaTime)); + m_mainCamera.moveZ(-(m_trig.sin(camRotY) * moveSpeed * deltaTime)); } if (input.isButtonPressed(psyqo::AdvancedPad::Pad::Pad1a, psyqo::AdvancedPad::Left)) { - m_mainCamera.moveX(-(m_trig.cos(camRotY) * moveSpeed)); - m_mainCamera.moveZ((m_trig.sin(camRotY) * moveSpeed)); + m_mainCamera.moveX(-(m_trig.cos(camRotY) * moveSpeed * deltaTime)); + m_mainCamera.moveZ((m_trig.sin(camRotY) * moveSpeed * deltaTime)); } if (input.isButtonPressed(psyqo::AdvancedPad::Pad::Pad1a, psyqo::AdvancedPad::Up)) { - m_mainCamera.moveX((m_trig.sin(camRotY) * m_trig.cos(camRotX)) * moveSpeed); + m_mainCamera.moveX((m_trig.sin(camRotY) * m_trig.cos(camRotX)) * moveSpeed * deltaTime); m_mainCamera.moveY(-(m_trig.sin(camRotX) * moveSpeed)); - m_mainCamera.moveZ((m_trig.cos(camRotY) * m_trig.cos(camRotX)) * moveSpeed); + m_mainCamera.moveZ((m_trig.cos(camRotY) * m_trig.cos(camRotX)) * moveSpeed * deltaTime); } if (input.isButtonPressed(psyqo::AdvancedPad::Pad::Pad1a, psyqo::AdvancedPad::Down)) { - m_mainCamera.moveX(-((m_trig.sin(camRotY) * m_trig.cos(camRotX)) * moveSpeed)); - m_mainCamera.moveY((m_trig.sin(camRotX) * moveSpeed)); - m_mainCamera.moveZ(-((m_trig.cos(camRotY) * m_trig.cos(camRotX)) * moveSpeed)); + m_mainCamera.moveX(-((m_trig.sin(camRotY) * m_trig.cos(camRotX)) * moveSpeed * deltaTime)); + m_mainCamera.moveY((m_trig.sin(camRotX) * moveSpeed * deltaTime)); + m_mainCamera.moveZ(-((m_trig.cos(camRotY) * m_trig.cos(camRotX)) * moveSpeed * deltaTime)); } if (input.isButtonPressed(psyqo::AdvancedPad::Pad::Pad1a, psyqo::AdvancedPad::R1)) { - m_mainCamera.moveY(-moveSpeed); + m_mainCamera.moveY(-moveSpeed * deltaTime); } if (input.isButtonPressed(psyqo::AdvancedPad::Pad::Pad1a, psyqo::AdvancedPad::L1)) { - m_mainCamera.moveY(moveSpeed); + m_mainCamera.moveY(moveSpeed * deltaTime); } if (input.isButtonPressed(psyqo::AdvancedPad::Pad::Pad1a, psyqo::AdvancedPad::Cross)) { - camRotX -= rotSpeed; + camRotX -= rotSpeed * deltaTime; m_mainCamera.setRotation(camRotX, camRotY, camRotZ); } if (input.isButtonPressed(psyqo::AdvancedPad::Pad::Pad1a, psyqo::AdvancedPad::Triangle)) { - camRotX += rotSpeed; + camRotX += rotSpeed * deltaTime; m_mainCamera.setRotation(camRotX, camRotY, camRotZ); } if (input.isButtonPressed(psyqo::AdvancedPad::Pad::Pad1a, psyqo::AdvancedPad::Circle)) { - camRotY += rotSpeed; + camRotY += rotSpeed * deltaTime; m_mainCamera.setRotation(camRotX, camRotY, camRotZ); } if (input.isButtonPressed(psyqo::AdvancedPad::Pad::Pad1a, psyqo::AdvancedPad::Square)) { - camRotY -= rotSpeed; + camRotY -= rotSpeed * deltaTime; m_mainCamera.setRotation(camRotX, camRotY, camRotZ); } psxsplash::Renderer::getInstance().render(m_objects); psxSplash.m_font.chainprintf(gpu(), {{.x = 2, .y = 2}}, {{.r = 0xff, .g = 0xff, .b = 0xff}}, "FPS: %i", - gpu().getRefreshRate() / frameDiff); + gpu().getRefreshRate() / deltaTime); gpu().pumpCallbacks(); uint32_t endFrame = gpu().now(); uint32_t spent = endFrame - beginFrame; diff --git a/src/mesh.hh b/src/mesh.hh index 76b3c54..c4d3c7b 100644 --- a/src/mesh.hh +++ b/src/mesh.hh @@ -1,17 +1,25 @@ #pragma once -#include "psyqo/gte-registers.hh" -#include "psyqo/primitives/common.hh" +#include +#include namespace psxsplash { -class Tri final { - public: - psyqo::GTE::PackedVec3 v0, v1, v2; - psyqo::GTE::PackedVec3 normal; - psyqo::PrimPieces::UVCoords uvA, uvB; - psyqo::PrimPieces::UVCoordsPadded uvC; - psyqo::Color colorA, colorB, colorC; -}; - + class Tri final { + public: + psyqo::GTE::PackedVec3 v0, v1, v2; + psyqo::GTE::PackedVec3 normal; + + psyqo::Color colorA, colorB, colorC; + + psyqo::PrimPieces::UVCoords uvA, uvB; + psyqo::PrimPieces::UVCoordsPadded uvC; + + psyqo::PrimPieces::TPageAttr tpage; + uint16_t clutX; + uint16_t clutY; + uint16_t padding; + }; + static_assert(sizeof(Tri) == 52, "Tri is not 52 bytes"); + } // namespace psxsplash diff --git a/src/renderer.cpp b/src/renderer.cpp index e9b6234..6baa0b5 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -7,29 +7,35 @@ #include #include #include +#include #include #include +#include +#include #include +#include "gtemath.hh" + using namespace psyqo::fixed_point_literals; using namespace psyqo::trig_literals; +using namespace psyqo::GTE; psxsplash::Renderer *psxsplash::Renderer::instance = nullptr; void psxsplash::Renderer::init(psyqo::GPU &gpuInstance) { psyqo::Kernel::assert(instance == nullptr, "A second intialization of Renderer was tried"); - psyqo::GTE::clear(); - psyqo::GTE::clear(); - psyqo::GTE::clear(); + clear(); + clear(); + clear(); - psyqo::GTE::write(psyqo::FixedPoint<16>(160.0).raw()); - psyqo::GTE::write(psyqo::FixedPoint<16>(120.0).raw()); + write(psyqo::FixedPoint<16>(160.0).raw()); + write(psyqo::FixedPoint<16>(120.0).raw()); - psyqo::GTE::write(190); + write(120); - psyqo::GTE::write(ORDERING_TABLE_SIZE / 3); - psyqo::GTE::write(ORDERING_TABLE_SIZE / 4); + write(ORDERING_TABLE_SIZE / 3); + write(ORDERING_TABLE_SIZE / 4); if (!instance) { instance = new Renderer(gpuInstance); @@ -47,98 +53,167 @@ void psxsplash::Renderer::render(eastl::vector &objects) { auto &clear = m_clear[parity]; auto &balloc = m_ballocs[parity]; - eastl::array projected; - - m_gpu.getNextClear(clear.primitive, m_clearcolor); - m_gpu.chain(clear); - balloc.reset(); - + eastl::array projected; for (auto &obj : objects) { + psyqo::Vec3 cameraPosition, objectPosition; + psyqo::Matrix33 finalMatrix; + + ::clear(); + ::clear(); + ::clear(); + + // Rotate the camera Translation vector by the camera rotation + writeSafe(m_currentCamera->getRotation()); + writeSafe(m_currentCamera->getPosition()); + + Kernels::mvmva(); + cameraPosition = readSafe(); + + // Rotate the object Translation vector by the camera rotation + writeSafe(obj->position); + Kernels::mvmva(); + objectPosition = readSafe(); + + objectPosition.x += cameraPosition.x; + objectPosition.y += cameraPosition.y; + objectPosition.z += cameraPosition.z; + + // Combine object and camera rotations + matrixMultiplyGTE(m_currentCamera->getRotation(), obj->rotation, &finalMatrix); + + psyqo::GTE::writeSafe(objectPosition); + psyqo::GTE::writeSafe(finalMatrix); + for (int i = 0; i < obj->polyCount; i++) { Tri &tri = obj->polygons[i]; psyqo::Vec3 result; - psyqo::GTE::writeUnsafe(obj->rotation); - psyqo::GTE::write(obj->position.x.raw() + - m_currentCamera->getPosition().x.raw()); - psyqo::GTE::write(obj->position.y.raw() + - m_currentCamera->getPosition().y.raw()); - psyqo::GTE::write(obj->position.z.raw() + - m_currentCamera->getPosition().z.raw()); + writeSafe(tri.v0); + writeSafe(tri.v1); + writeSafe(tri.v2); - psyqo::GTE::writeSafe(tri.v0); - psyqo::GTE::writeSafe(tri.v1); - psyqo::GTE::writeSafe(tri.v2); - - psyqo::GTE::Kernels::mvmva(); - result = psyqo::GTE::readSafe(); - psyqo::GTE::writeSafe(result); - - psyqo::GTE::Kernels::mvmva(); - result = psyqo::GTE::readSafe(); - psyqo::GTE::writeSafe(result); - - psyqo::GTE::Kernels::mvmva(); - result = psyqo::GTE::readSafe(); - psyqo::GTE::writeSafe(result); - - psyqo::GTE::writeUnsafe(m_currentCamera->getRotation()); - psyqo::GTE::clear(); - psyqo::GTE::clear(); - psyqo::GTE::clear(); - - psyqo::GTE::Kernels::rtpt(); - psyqo::GTE::Kernels::nclip(); + Kernels::rtpt(); + Kernels::nclip(); int32_t mac0 = 0; - psyqo::GTE::read(reinterpret_cast(&mac0)); + read(reinterpret_cast(&mac0)); if (mac0 <= 0) continue; int32_t zIndex = 0; uint32_t sz0, sz1, sz2; - psyqo::GTE::read(&sz0); - psyqo::GTE::read(&sz1); - psyqo::GTE::read(&sz2); + read(&sz0); + read(&sz1); + read(&sz2); zIndex = eastl::max(eastl::max(sz0, sz1), sz2); - - // psyqo::GTE::read(reinterpret_cast(&zIndex)); - if (zIndex < 0 || zIndex >= ORDERING_TABLE_SIZE) continue; - psyqo::GTE::read(&projected[0].packed); - psyqo::GTE::read(&projected[1].packed); - psyqo::GTE::read(&projected[2].packed); + read(&projected[0].packed); + read(&projected[1].packed); + read(&projected[2].packed); - auto &prim = balloc.allocateFragment(); - - psyqo::PrimPieces::ClutIndex clut(obj->clutX, obj->clutY); - - prim.primitive.pointA = projected[0]; - prim.primitive.pointB = projected[1]; - prim.primitive.pointC = projected[2]; - prim.primitive.uvA = tri.uvA; - prim.primitive.uvB = tri.uvB; - prim.primitive.uvC = tri.uvC; - prim.primitive.tpage = obj->texture; - prim.primitive.clutIndex = clut; - prim.primitive.setColorA(tri.colorA); - prim.primitive.setColorB(tri.colorB); - prim.primitive.setColorC(tri.colorC); - - prim.primitive.setOpaque(); - - ot.insert(prim, zIndex); + iterativeSubdivideAndRender(tri, projected, zIndex, 3); } } - + m_gpu.getNextClear(clear.primitive, m_clearcolor); + m_gpu.chain(clear); m_gpu.chain(ot); } +static inline psyqo::Color averageColor(const psyqo::Color &c1, const psyqo::Color &c2) { + uint8_t r = (c1.r + c2.r) >> 1; + uint8_t g = (c1.g + c2.g) >> 1; + uint8_t b = (c1.b + c2.b) >> 1; + psyqo::Color c; + + c.r = r; + c.g = g; + c.b = b; + + return c; +} + + +// Temporary subdivision code. I'm told this is terrible. +void psxsplash::Renderer::iterativeSubdivideAndRender(const Tri &initialTri, + const eastl::array &initialProj, int zIndex, + int maxIterations) { + struct Subdiv { + Tri tri; + eastl::array proj; + int iterations; + }; + + // Reserve space knowing the max subdivisions (for maxIterations=3, max elements are small) + eastl::vector stack; + stack.reserve(16); + stack.push_back({initialTri, initialProj, maxIterations}); + + while (!stack.empty()) { + Subdiv s = stack.back(); + stack.pop_back(); + + uint16_t minX = eastl::min({s.proj[0].x, s.proj[1].x, s.proj[2].x}); + uint16_t maxX = eastl::max({s.proj[0].x, s.proj[1].x, s.proj[2].x}); + uint16_t minY = eastl::min({s.proj[0].y, s.proj[1].y, s.proj[2].y}); + uint16_t maxY = eastl::max({s.proj[0].y, s.proj[1].y, s.proj[2].y}); + uint16_t width = maxX - minX; + uint16_t height = maxY - minY; + + // Base case: small enough or no iterations left. + if (s.iterations == 0 || (width < 2048 && height < 1024)) { + auto &balloc = m_ballocs[m_gpu.getParity()]; + auto &prim = balloc.allocateFragment(); + + prim.primitive.pointA = s.proj[0]; + prim.primitive.pointB = s.proj[1]; + prim.primitive.pointC = s.proj[2]; + prim.primitive.uvA = s.tri.uvA; + prim.primitive.uvB = s.tri.uvB; + prim.primitive.uvC = s.tri.uvC; + prim.primitive.tpage = s.tri.tpage; + psyqo::PrimPieces::ClutIndex clut(s.tri.clutX, s.tri.clutY); + prim.primitive.clutIndex = clut; + prim.primitive.setColorA(s.tri.colorA); + prim.primitive.setColorB(s.tri.colorB); + prim.primitive.setColorC(s.tri.colorC); + prim.primitive.setOpaque(); + + m_ots[m_gpu.getParity()].insert(prim, zIndex); + continue; + } + + // Compute midpoint between projected[0] and projected[1]. + psyqo::Vertex mid; + mid.x = (s.proj[0].x + s.proj[1].x) >> 1; + mid.y = (s.proj[0].y + s.proj[1].y) >> 1; + + // Interpolate UV and color. + psyqo::PrimPieces::UVCoords newUV; + newUV.u = (s.tri.uvA.u + s.tri.uvB.u) / 2; + newUV.v = (s.tri.uvA.v + s.tri.uvB.v) / 2; + psyqo::Color newColor = averageColor(s.tri.colorA, s.tri.colorB); + + // Prepare new projected vertices. + eastl::array projA = {s.proj[0], mid, s.proj[2]}; + eastl::array projB = {mid, s.proj[1], s.proj[2]}; + + // Construct new Tris + Tri triA = s.tri; + triA.uvB = newUV; + triA.colorB = newColor; + + Tri triB = s.tri; + triB.uvA = newUV; + triB.colorA = newColor; + + // Push new subdivisions on stack. + stack.push_back({triA, projA, s.iterations - 1}); + stack.push_back({triB, projB, s.iterations - 1}); + } +} + void psxsplash::Renderer::vramUpload(const uint16_t *imageData, int16_t posX, int16_t posY, int16_t width, int16_t height) { psyqo::Rect uploadRect{.a = {.x = posX, .y = posY}, .b = {width, height}}; diff --git a/src/renderer.hh b/src/renderer.hh index 4e94425..1a9e4ce 100644 --- a/src/renderer.hh +++ b/src/renderer.hh @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -10,6 +11,7 @@ #include #include #include +#include #include #include "camera.hh" @@ -22,14 +24,16 @@ class Renderer final { Renderer(const Renderer&) = delete; Renderer& operator=(const Renderer&) = delete; - static constexpr size_t ORDERING_TABLE_SIZE = 4096 * 16; - static constexpr size_t BUMP_ALLOCATOR_SIZE = 8192 * 8; + static constexpr size_t ORDERING_TABLE_SIZE = 2048 * 3; + static constexpr size_t BUMP_ALLOCATOR_SIZE = 8096 * 16; static void init(psyqo::GPU& gpuInstance); void setCamera(Camera& camera); void render(eastl::vector& objects); + void iterativeSubdivideAndRender(const Tri& initialTri, const eastl::array& initialProj, + int zIndex, int maxIterations); void vramUpload(const uint16_t* imageData, int16_t posX, int16_t posY, int16_t width, int16_t height); static Renderer& getInstance() { @@ -50,8 +54,9 @@ class Renderer final { psyqo::OrderingTable m_ots[2]; psyqo::Fragments::SimpleFragment m_clear[2]; - psyqo::Color m_clearcolor = {.r = 63, .g = 63, .b = 63}; psyqo::BumpAllocator m_ballocs[2]; + + psyqo::Color m_clearcolor = {.r = 63, .g = 63, .b = 100}; }; } // namespace psxsplash \ No newline at end of file diff --git a/src/splashpack.cpp b/src/splashpack.cpp index d336ddc..1c18532 100644 --- a/src/splashpack.cpp +++ b/src/splashpack.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include "gameobject.hh" @@ -23,25 +22,7 @@ eastl::vector LoadSplashpack(uint8_t *data) { for (uint16_t i = 0; i < header->gameObjectCount; i++) { psxsplash::GameObject *go = reinterpret_cast(curentPointer); - - int16_t width = 0; - switch ((psyqo::Prim::TPageAttr::ColorMode)((std::bit_cast(go->texture) & 0x0180) >> 7)) { - case psyqo::Prim::TPageAttr::ColorMode::Tex4Bits: - width = 16; - break; - case psyqo::Prim::TPageAttr::ColorMode::Tex8Bits: - width = 256; - break; - default: - width = -1; - } - - if (width > 0) { - psxsplash::Renderer::getInstance().vramUpload(go->clut, go->clutX * 16, go->clutY, width, 1); - } - go->polygons = reinterpret_cast(data + go->polygonsOffset); - gameObjects.push_back(go); curentPointer += sizeof(psxsplash::GameObject); } @@ -49,12 +30,18 @@ eastl::vector LoadSplashpack(uint8_t *data) { for (uint16_t i = 0; i < header->textureAtlasCount; i++) { psxsplash::SPLASHPACKTextureAtlas *atlas = reinterpret_cast(curentPointer); - uint8_t *offsetData = data + atlas->polygonsOffset; // Ensure correct byte offset - uint16_t *castedData = reinterpret_cast(offsetData); // Safe cast + uint8_t *offsetData = data + atlas->polygonsOffset; + uint16_t *castedData = reinterpret_cast(offsetData); psxsplash::Renderer::getInstance().vramUpload(castedData, atlas->x, atlas->y, atlas->width, atlas->height); curentPointer += sizeof(psxsplash::SPLASHPACKTextureAtlas); } + for (uint16_t i = 0; i < header->clutCount; i++) { + psxsplash::SPLASHPACKClut *clut = reinterpret_cast(curentPointer); + psxsplash::Renderer::getInstance().vramUpload(clut->clut, clut->clutPackingX * 16, clut->clutPackingY, clut->length, 1); + curentPointer += sizeof(psxsplash::SPLASHPACKClut); + } + return gameObjects; } diff --git a/src/splashpack.hh b/src/splashpack.hh index 63a2977..2237e52 100644 --- a/src/splashpack.hh +++ b/src/splashpack.hh @@ -13,6 +13,8 @@ struct SPLASHPACKFileHeader { uint16_t version; uint16_t gameObjectCount; uint16_t textureAtlasCount; + uint16_t clutCount; + uint16_t pad; }; struct SPLASHPACKTextureAtlas { @@ -21,6 +23,15 @@ struct SPLASHPACKTextureAtlas { uint16_t x, y; }; +struct SPLASHPACKClut { + uint16_t clut[256]; + + uint16_t clutPackingX; + uint16_t clutPackingY; + uint16_t length; + uint16_t pad; +}; + eastl::vector LoadSplashpack(uint8_t *data); }; // namespace psxsplash \ No newline at end of file diff --git a/src/texture.hh b/src/texture.hh deleted file mode 100644 index d4f7fc0..0000000 --- a/src/texture.hh +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include -#include - -namespace psxsplash { -class Texture final { - public: - psyqo::PrimPieces::TPageAttr m_tpage; - uint8_t m_width, m_height; -}; - -} // namespace psxsplash \ No newline at end of file