├── P3DBenchmark ├── P3DBenchmark.sav ├── source │ ├── recip.cpp │ ├── fixeddiv.s │ └── main.iwram.cpp ├── P3DBenchmark.elf ├── P3DBenchmark.gba ├── Performance.xlsx ├── include │ └── model.h ├── P3DBenchmark.pro └── Makefile ├── Potato3dGBAExample ├── source │ ├── bspmodel.cpp │ ├── object3d.cpp │ ├── recip.cpp │ ├── render.iwram.cpp │ ├── model.h │ ├── fixeddiv.s │ └── main.cpp ├── test.bmp ├── Potato3dGBAExample.elf ├── Potato3dGBAExample.gba ├── include │ └── model.h ├── Potato3dGBAExample.pro └── Makefile ├── P3DFps.xlsx ├── Potato3dExample2 ├── source │ ├── recip.redir.cpp │ ├── bspmodel.redir.iwram.cpp │ ├── worldmodel.cpp │ ├── main.cpp │ ├── model.cpp │ ├── setup.cpp │ ├── fixeddiv.iwram.s │ ├── collision.cpp │ ├── camera.cpp │ ├── videosystem.cpp │ └── mainloop.iwram.cpp ├── include │ ├── model.h │ ├── setup.h │ ├── common.h │ ├── collision.h │ ├── worldmodel.h │ ├── camera.h │ ├── mainloop.h │ └── videosystem.h ├── P3DExample2.pro └── Makefile ├── Potato3dExample ├── CodeProphet.lib ├── main.cpp ├── mainwindow.h ├── mainwindow2.h ├── Potato3dExample.pro ├── mainwindow.cpp └── mainwindow2.cpp ├── potato3d.h ├── Config.h ├── P3DObj2Bsp ├── resources.qrc ├── bspmodelexport.h ├── Config.h ├── P3DObj2Bsp.pro ├── .gitignore ├── objloader.h ├── main.cpp ├── bspbuilder.h └── bspmodelexport.cpp ├── 3dmaths ├── recip.h ├── f3dmath.h ├── divide.h ├── plane.h ├── v2.h ├── v4.h ├── aabb.h ├── v3.h ├── utils.h └── fp.h ├── README.md ├── ConfigInternal.h ├── ConfigUser.h ├── TextureCache.h ├── object3d.h ├── BspModelDefs.h ├── Pixel.h ├── Qt Debug Helper └── personaltypes.py ├── .gitignore ├── bspmodel.cpp ├── bspmodel.h ├── RenderTarget.h ├── RenderCommon.h ├── PixelShaderGBA8.h ├── PixelShaderDefault.h ├── object3d.cpp └── RenderDevice.h /P3DBenchmark/P3DBenchmark.sav: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /P3DBenchmark/source/recip.cpp: -------------------------------------------------------------------------------- 1 | #include "../../3dmaths/recip.cpp" 2 | -------------------------------------------------------------------------------- /Potato3dGBAExample/source/bspmodel.cpp: -------------------------------------------------------------------------------- 1 | #include "../../bspmodel.cpp" 2 | -------------------------------------------------------------------------------- /Potato3dGBAExample/source/object3d.cpp: -------------------------------------------------------------------------------- 1 | #include "../../object3d.cpp" 2 | -------------------------------------------------------------------------------- /P3DFps.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doomhack/Potato3d/HEAD/P3DFps.xlsx -------------------------------------------------------------------------------- /Potato3dExample2/source/recip.redir.cpp: -------------------------------------------------------------------------------- 1 | #include "../../3dmaths/recip.cpp" 2 | -------------------------------------------------------------------------------- /Potato3dGBAExample/source/recip.cpp: -------------------------------------------------------------------------------- 1 | #include "../../3dmaths/recip.cpp" 2 | -------------------------------------------------------------------------------- /Potato3dExample2/source/bspmodel.redir.iwram.cpp: -------------------------------------------------------------------------------- 1 | //#include "common.h" 2 | #include "../../bspmodel.cpp" 3 | -------------------------------------------------------------------------------- /Potato3dGBAExample/test.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doomhack/Potato3d/HEAD/Potato3dGBAExample/test.bmp -------------------------------------------------------------------------------- /P3DBenchmark/P3DBenchmark.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doomhack/Potato3d/HEAD/P3DBenchmark/P3DBenchmark.elf -------------------------------------------------------------------------------- /P3DBenchmark/P3DBenchmark.gba: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doomhack/Potato3d/HEAD/P3DBenchmark/P3DBenchmark.gba -------------------------------------------------------------------------------- /P3DBenchmark/Performance.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doomhack/Potato3d/HEAD/P3DBenchmark/Performance.xlsx -------------------------------------------------------------------------------- /Potato3dExample/CodeProphet.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doomhack/Potato3d/HEAD/Potato3dExample/CodeProphet.lib -------------------------------------------------------------------------------- /Potato3dGBAExample/source/render.iwram.cpp: -------------------------------------------------------------------------------- 1 | #include "../../RenderTarget.cpp" 2 | #include "../../RenderDevice.cpp" 3 | -------------------------------------------------------------------------------- /potato3d.h: -------------------------------------------------------------------------------- 1 | #ifndef POTATO3D 2 | #define POTATO3D 3 | 4 | #include "Config.h" 5 | #include "object3d.h" 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /Potato3dGBAExample/Potato3dGBAExample.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doomhack/Potato3d/HEAD/Potato3dGBAExample/Potato3dGBAExample.elf -------------------------------------------------------------------------------- /Potato3dGBAExample/Potato3dGBAExample.gba: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doomhack/Potato3d/HEAD/Potato3dGBAExample/Potato3dGBAExample.gba -------------------------------------------------------------------------------- /Config.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_H 2 | #define CONFIG_H 3 | 4 | #include "ConfigUser.h" 5 | #include "ConfigInternal.h" 6 | 7 | #endif // CONFIG_H 8 | -------------------------------------------------------------------------------- /P3DObj2Bsp/resources.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | nQuantCpp.exe 4 | 5 | 6 | -------------------------------------------------------------------------------- /P3DBenchmark/include/model.h: -------------------------------------------------------------------------------- 1 | #ifndef MODEL_H 2 | #define MODEL_H 3 | 4 | const extern unsigned char modeldata[]; 5 | 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /Potato3dExample2/include/model.h: -------------------------------------------------------------------------------- 1 | #ifndef MODEL_H 2 | #define MODEL_H 3 | 4 | const extern unsigned char modeldata[]; 5 | 6 | #endif // MODEL_H 7 | -------------------------------------------------------------------------------- /Potato3dGBAExample/include/model.h: -------------------------------------------------------------------------------- 1 | #ifndef MODEL_H 2 | #define MODEL_H 3 | 4 | const extern unsigned char modeldata[]; 5 | 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /Potato3dGBAExample/source/model.h: -------------------------------------------------------------------------------- 1 | #ifndef MODEL_H 2 | #define MODEL_H 3 | 4 | const extern unsigned char modeldata[]; 5 | 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /Potato3dExample2/include/setup.h: -------------------------------------------------------------------------------- 1 | #ifndef SETUP_H 2 | #define SETUP_H 3 | 4 | class Setup 5 | { 6 | public: 7 | Setup(); 8 | 9 | void DoSetup(); 10 | }; 11 | 12 | #endif // SETUP_H 13 | -------------------------------------------------------------------------------- /3dmaths/recip.h: -------------------------------------------------------------------------------- 1 | #ifndef RECIP_H 2 | #define RECIP_H 3 | 4 | namespace P3D 5 | { 6 | extern const int reciprocalTable[]; 7 | extern const unsigned char shiftTable[]; 8 | }; 9 | 10 | #endif // RECIP_H 11 | -------------------------------------------------------------------------------- /Potato3dExample2/source/worldmodel.cpp: -------------------------------------------------------------------------------- 1 | #include "../include/worldmodel.h" 2 | 3 | WorldModel::WorldModel() 4 | { 5 | 6 | } 7 | 8 | const P3D::BspModel* WorldModel::GetModel() const 9 | { 10 | return model; 11 | } 12 | -------------------------------------------------------------------------------- /Potato3dExample/main.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow2.h" 2 | #include "mainwindow.h" 3 | 4 | 5 | #include 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | QApplication a(argc, argv); 10 | MainWindow w; 11 | w.show(); 12 | return a.exec(); 13 | } 14 | 15 | -------------------------------------------------------------------------------- /Potato3dExample2/source/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../include/mainloop.h" 2 | #include "../include/setup.h" 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | Setup setup; 7 | MainLoop* loop = new MainLoop; 8 | 9 | setup.DoSetup(); 10 | loop->Run(); 11 | 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /3dmaths/f3dmath.h: -------------------------------------------------------------------------------- 1 | #ifndef P3DMATH_H 2 | #define P3DMATH_H 3 | 4 | #include 5 | 6 | 7 | #include "fp.h" 8 | #include "v4.h" 9 | #include "v3.h" 10 | #include "v2.h" 11 | #include "m4.h" 12 | #include "plane.h" 13 | #include "aabb.h" 14 | #include "utils.h" 15 | 16 | 17 | #endif // P3DMATH_H 18 | -------------------------------------------------------------------------------- /Potato3dExample2/include/common.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_H 2 | #define COMMON_H 3 | 4 | #include 5 | #ifdef __arm__ 6 | #define GBA 7 | #endif 8 | 9 | typedef enum Keys : unsigned int 10 | { 11 | KeyLeft = 1, 12 | KeyRight = 2, 13 | KeyUp = 4, 14 | KeyDown = 8 15 | } Keys; 16 | 17 | 18 | #endif // COMMON_H 19 | -------------------------------------------------------------------------------- /Potato3dExample2/source/model.cpp: -------------------------------------------------------------------------------- 1 | //#include "C:/Users/Zak/Downloads/GE_Temple/temple.cpp" 2 | //#include "C:/Users/Zak/Downloads/Dam/dam.cpp" 3 | //#include "C:/Users/Zak/Documents/GitProjects/Potato3d/Potato3dExample/models/Streets/Streets.cpp" 4 | #include "C:/Users/Zak/Downloads/Villa/villa.cpp" 5 | //#include "C:/Users/Zak/Downloads/DDHQ2/ddhq2.cpp" 6 | -------------------------------------------------------------------------------- /Potato3dExample2/include/collision.h: -------------------------------------------------------------------------------- 1 | #ifndef COLLISION_H 2 | #define COLLISION_H 3 | 4 | #include "../include/common.h" 5 | #include "../../Config.h" 6 | #include "../../3dmaths/f3dmath.h" 7 | #include "../../bspmodel.h" 8 | 9 | class Collision 10 | { 11 | public: 12 | Collision(); 13 | bool CheckCollision(const P3D::BspModelTriangle* tri, const P3D::V3& point, const P3D::fp radius, P3D::V3& resolutionVector); 14 | }; 15 | 16 | #endif // COLLISION_H 17 | -------------------------------------------------------------------------------- /Potato3dExample2/include/worldmodel.h: -------------------------------------------------------------------------------- 1 | #ifndef WORLDMODEL_H 2 | #define WORLDMODEL_H 3 | 4 | #include "../include/common.h" 5 | #include "../../Config.h" 6 | #include "../../3dmaths/f3dmath.h" 7 | #include "../../bspmodel.h" 8 | #include "model.h" 9 | 10 | 11 | class WorldModel 12 | { 13 | public: 14 | explicit WorldModel(); 15 | 16 | const P3D::BspModel* GetModel() const; 17 | private: 18 | const P3D::BspModel* model = (P3D::BspModel*)&modeldata; 19 | }; 20 | 21 | #endif // WORLDMODEL_H 22 | -------------------------------------------------------------------------------- /P3DBenchmark/P3DBenchmark.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | CONFIG += console c++20 3 | CONFIG -= app_bundle 4 | CONFIG += force_debug_info 5 | QT += core gui 6 | 7 | 8 | 9 | INCLUDEPATH += "include" 10 | 11 | INCLUDEPATH += "C:\devkitPro\libgba\include" 12 | 13 | SOURCES += \ 14 | source/main.iwram.cpp \ 15 | source/recip.cpp 16 | 17 | 18 | 19 | #QMAKE_CXXFLAGS += /GL 20 | #QMAKE_CFLAGS += /GL 21 | 22 | #QMAKE_LFLAGS += /LTCG 23 | 24 | DISTFILES += \ 25 | Makefile 26 | 27 | -------------------------------------------------------------------------------- /Potato3dExample2/source/setup.cpp: -------------------------------------------------------------------------------- 1 | #include "../include/common.h" 2 | #include "../include/setup.h" 3 | 4 | 5 | #ifdef GBA 6 | #include 7 | #include 8 | #include 9 | 10 | #define REG_WAITCNT *((vu16 *)(0x4000204)) 11 | #endif 12 | 13 | Setup::Setup() 14 | { 15 | 16 | } 17 | 18 | void Setup::DoSetup() 19 | { 20 | #ifdef GBA 21 | irqInit(); 22 | 23 | //Set gamepak wait states and prefetch. 24 | REG_WAITCNT = 0x46DA; 25 | 26 | SetMode(MODE_4 | BG2_ENABLE | BIT(5)); 27 | #else 28 | 29 | #endif 30 | } 31 | -------------------------------------------------------------------------------- /P3DObj2Bsp/bspmodelexport.h: -------------------------------------------------------------------------------- 1 | #ifndef BSPMODELEXPORT_H 2 | #define BSPMODELEXPORT_H 3 | 4 | #include 5 | #include 6 | 7 | #include "bspbuilder.h" 8 | #include "config.h" 9 | #include "../BspModelDefs.h" 10 | 11 | 12 | namespace Obj2Bsp 13 | { 14 | class BspModelExport 15 | { 16 | public: 17 | BspModelExport(); 18 | 19 | QByteArray ExportBSPModel(Obj2Bsp::BspNode* root, Model3d* model); 20 | 21 | private: 22 | void TraverseNodesRecursive(BspNode* n, QList& nodeList); 23 | 24 | }; 25 | 26 | } 27 | 28 | #endif // BSPMODELEXPORT_H 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Potato3d 2 | 3 | A simple, fast 3d renderer for stuff with slow processors. 4 | 5 | 6 | Still very much a work in progress. 7 | 8 | 9 | Currently uses BSP to sort and cull polygons. 10 | 11 | Very little division in the hot path. I've been able to use a reciprocal table to get rid of most division. 12 | 13 | All math execept for one time set-up stuff is fixed point. Can also use float or doubles by just changing a #define. 14 | 15 | 16 | Renders textures perspective-correctish. Perspective correct tex corodinates are computed every 16px and linear interpolated from there. 17 | 18 | 19 | Rendering is front-to-back with no Z-buffer and zero overdraw. But the span buffering is still a performance hog. 20 | 21 | -------------------------------------------------------------------------------- /P3DObj2Bsp/Config.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_H 2 | #define CONFIG_H 3 | 4 | #include 5 | #include 6 | 7 | #include "../3dmaths/f3dmath.h" 8 | 9 | namespace Obj2Bsp 10 | { 11 | inline constexpr int TEX_SIZE = 64; 12 | inline constexpr int TEX_MAX_TILE = 8; 13 | 14 | inline constexpr QImage::Format textureFormat = QImage::Format_Indexed8; 15 | 16 | 17 | inline constexpr int LIGHT_LEVELS = 8; 18 | inline constexpr int FOG_LEVELS = 16; 19 | 20 | inline constexpr int FOG_COLOR = 0x799ED7; 21 | 22 | inline constexpr const char QUANT_ALGO[] = "PNN"; //PNN, DIV, NEU, WU 23 | } 24 | 25 | namespace P3D 26 | { 27 | typedef quint8 pixel; 28 | 29 | //typedef double fp; 30 | typedef P3D::FP16 fp; 31 | } 32 | 33 | #endif // CONFIG_H 34 | -------------------------------------------------------------------------------- /P3DObj2Bsp/P3DObj2Bsp.pro: -------------------------------------------------------------------------------- 1 | QT = core gui 2 | 3 | CONFIG += c++20 cmdline 4 | 5 | # You can make your code fail to compile if it uses deprecated APIs. 6 | # In order to do so, uncomment the following line. 7 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 8 | 9 | SOURCES += \ 10 | bspbuilder.cpp \ 11 | bspmodelexport.cpp \ 12 | main.cpp \ 13 | objloader.cpp 14 | 15 | # Default rules for deployment. 16 | qnx: target.path = /tmp/$${TARGET}/bin 17 | else: unix:!android: target.path = /opt/$${TARGET}/bin 18 | !isEmpty(target.path): INSTALLS += target 19 | 20 | HEADERS += \ 21 | ../BspModelDefs.h \ 22 | bspbuilder.h \ 23 | bspmodelexport.h \ 24 | config.h \ 25 | objloader.h 26 | 27 | RESOURCES += \ 28 | resources.qrc 29 | -------------------------------------------------------------------------------- /Potato3dExample2/source/fixeddiv.iwram.s: -------------------------------------------------------------------------------- 1 | .arm 2 | .align 2 3 | 4 | .global udiv64_arm 5 | .type udiv64_arm, %function 6 | 7 | udiv64_arm: 8 | 9 | /* 10 | Tweaked version of 64/32 division found in 11 | section 7.3.1.3 of 12 | ARM System Developer’s Guide 13 | Designing and Optimizing System Software 14 | 15 | ISBN: 1-55860-874-5 16 | 17 | r0 = numerator high, return quotient 18 | r1 = numerator low 19 | r2 = denominator 20 | r3 = scratch 21 | */ 22 | 23 | cmp r0, r2 24 | bcs .overflow_32 25 | rsb r2, r2, #0 26 | adds r3, r1, r1 27 | adcs r1, r2, r0, LSL#1 28 | subcc r1, r1, r2 29 | 30 | .rept 31 31 | adcs r3, r3, r3 32 | adcs r1, r2, r1, LSL#1 33 | subcc r1,r1, r2 34 | .endr 35 | 36 | adcs r0, r3, r3 37 | bx lr 38 | 39 | .overflow_32: 40 | mov r0, #-1 41 | bx lr 42 | -------------------------------------------------------------------------------- /Potato3dExample2/include/camera.h: -------------------------------------------------------------------------------- 1 | #ifndef CAMERA_H 2 | #define CAMERA_H 3 | 4 | #include "../include/common.h" 5 | #include "../../Config.h" 6 | #include "../../3dmaths/f3dmath.h" 7 | 8 | class Camera 9 | { 10 | public: 11 | Camera(); 12 | 13 | const P3D::V3& GetPosition() const; 14 | const P3D::V3 GetEyePosition() const; 15 | const P3D::V3& GetAngle() const; 16 | void MovePosition(const P3D::V3& delta); 17 | void SetPosition(const P3D::V3& pos); 18 | 19 | void HandleInput(unsigned int keyState, P3D::fp gravity_velocity); 20 | 21 | private: 22 | P3D::V3 position = P3D::V3(0,50,0); 23 | P3D::V3 angle = P3D::V3(0,0,0); 24 | const P3D::V3 eyeOffset = P3D::V3(0,100,0); 25 | }; 26 | 27 | #endif // CAMERA_H 28 | -------------------------------------------------------------------------------- /P3DBenchmark/source/fixeddiv.s: -------------------------------------------------------------------------------- 1 | .section .iwram 2 | .arm 3 | .align 4 | 5 | .global udiv64_arm 6 | 7 | udiv64_arm: 8 | 9 | /* 10 | Tweaked version of 64/32 division found in 11 | section 7.3.1.3 of 12 | ARM System Developer’s Guide 13 | Designing and Optimizing System Software 14 | 15 | ISBN: 1-55860-874-5 16 | 17 | r0 = numerator high, return quotient 18 | r1 = numerator low 19 | r2 = denominator 20 | r3 = scratch 21 | */ 22 | 23 | cmp r0, r2 24 | bcs .overflow_32 25 | rsb r2, r2, #0 26 | adds r3, r1, r1 27 | adcs r1, r2, r0, LSL#1 28 | subcc r1, r1, r2 29 | 30 | .rept 31 31 | adcs r3, r3, r3 32 | adcs r1, r2, r1, LSL#1 33 | subcc r1,r1, r2 34 | .endr 35 | 36 | adcs r0, r3, r3 37 | bx lr 38 | 39 | .overflow_32: 40 | mov r0, #-1 41 | bx lr 42 | -------------------------------------------------------------------------------- /Potato3dGBAExample/source/fixeddiv.s: -------------------------------------------------------------------------------- 1 | .section .iwram 2 | .arm 3 | .align 4 | 5 | .global udiv64_arm 6 | 7 | udiv64_arm: 8 | 9 | /* 10 | Tweaked version of 64/32 division found in 11 | section 7.3.1.3 of 12 | ARM System Developer’s Guide 13 | Designing and Optimizing System Software 14 | 15 | ISBN: 1-55860-874-5 16 | 17 | r0 = numerator high, return quotient 18 | r1 = numerator low 19 | r2 = denominator 20 | r3 = scratch 21 | */ 22 | 23 | cmp r0, r2 24 | bcs .overflow_32 25 | rsb r2, r2, #0 26 | adds r3, r1, r1 27 | adcs r1, r2, r0, LSL#1 28 | subcc r1, r1, r2 29 | 30 | .rept 31 31 | adcs r3, r3, r3 32 | adcs r1, r2, r1, LSL#1 33 | subcc r1,r1, r2 34 | .endr 35 | 36 | adcs r0, r3, r3 37 | bx lr 38 | 39 | .overflow_32: 40 | mov r0, #-1 41 | bx lr 42 | -------------------------------------------------------------------------------- /3dmaths/divide.h: -------------------------------------------------------------------------------- 1 | #ifndef DIVIDE_H 2 | #define DIVIDE_H 3 | 4 | #ifdef __arm__ 5 | extern "C" constexpr unsigned int udiv64_arm (unsigned int a, unsigned int b, unsigned int c); 6 | #endif 7 | 8 | template 9 | constexpr inline T FixedDiv(const T a, const T b, int fracbits) 10 | { 11 | return (a * (1 << fracbits)) / b; 12 | } 13 | 14 | template<> 15 | constexpr inline int FixedDiv(int a, int b, int fracbits) 16 | { 17 | #ifndef __arm__ 18 | long long int tmp = ((long long int)a << fracbits) / b; 19 | 20 | return (int)tmp; 21 | #else 22 | 23 | int sign = (a^b) < 0; /* different signs */ 24 | 25 | a = a<0 ? -a:a; 26 | b = b<0 ? -b:b; 27 | 28 | unsigned int l = (a << fracbits); 29 | unsigned int h = (a >> fracbits); 30 | 31 | int q = udiv64_arm (h,l,b); 32 | if (sign) 33 | q = -q; 34 | 35 | return q; 36 | #endif 37 | 38 | } 39 | 40 | #endif // DIVIDE_H 41 | -------------------------------------------------------------------------------- /Potato3dGBAExample/Potato3dGBAExample.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | CONFIG += console c++14 3 | CONFIG -= app_bundle 4 | CONFIG -= qt 5 | 6 | 7 | INCLUDEPATH += "include" 8 | 9 | INCLUDEPATH += "C:\devkitPro\libgba\include" 10 | 11 | SOURCES += \ 12 | source/bspmodel.cpp \ 13 | source/main.cpp \ 14 | source/model.cpp \ 15 | source/object3d.cpp \ 16 | source/recip.cpp \ 17 | source/render.iwram.cpp 18 | 19 | 20 | HEADERS += \ 21 | ../3dmaths/divide.h \ 22 | ../3dmaths/f3dmath.h \ 23 | ../3dmaths/fp.h \ 24 | ../3dmaths/m4.h \ 25 | ../3dmaths/utils.h \ 26 | ../3dmaths/v2.h \ 27 | ../3dmaths/v3.h \ 28 | ../3dmaths/v4.h \ 29 | ../common.h \ 30 | ../object3d.h \ 31 | ../potato3d.h \ 32 | ../render.h \ 33 | ../rtypes.h \ 34 | include/common.h \ 35 | include/model.h 36 | 37 | DISTFILES += \ 38 | Makefile \ 39 | source/fixeddiv.s 40 | 41 | 42 | -------------------------------------------------------------------------------- /ConfigInternal.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIGINTERNAL_H 2 | #define CONFIGINTERNAL_H 3 | 4 | namespace P3D 5 | { 6 | inline constexpr int constexpr_log2(int v) { return v ? 1 + constexpr_log2(v >> 1) : -1; } 7 | 8 | inline constexpr int TEX_SHIFT = constexpr_log2(TEX_SIZE); 9 | 10 | inline constexpr int TEX_MASK = (TEX_SIZE-1); 11 | 12 | inline constexpr int TEX_SIZE_PIXELS = (TEX_SIZE * TEX_SIZE); 13 | 14 | inline constexpr int TEX_SIZE_BYTES = (TEX_SIZE_PIXELS * sizeof(pixel)); 15 | 16 | 17 | inline constexpr int SUBDIVIDE_SPAN_SHIFT = constexpr_log2(SUBDIVIDE_SPAN_LEN); 18 | 19 | inline constexpr int FOG_SHIFT = constexpr_log2(FOG_LEVELS); 20 | inline constexpr int LIGHT_SHIFT = constexpr_log2(LIGHT_LEVELS); 21 | inline constexpr fp FOG_MAX = fp(1) - std::numeric_limits().epsilon(); 22 | inline constexpr fp LIGHT_MAX = fp(1) - std::numeric_limits().epsilon(); 23 | 24 | } 25 | 26 | #endif // CONFIGINTERNAL_H 27 | -------------------------------------------------------------------------------- /Potato3dExample2/source/collision.cpp: -------------------------------------------------------------------------------- 1 | #include "../include/collision.h" 2 | 3 | Collision::Collision() 4 | { 5 | 6 | } 7 | 8 | bool Collision::CheckCollision(const P3D::BspModelTriangle* tri, const P3D::V3& point, const P3D::fp radius, P3D::V3& resolutionVector) 9 | { 10 | P3D::fp distance = tri->normal_plane.DistanceToPoint(point); 11 | 12 | //No collision 13 | if(distance < 0 || distance >= radius) 14 | return false; 15 | 16 | P3D::fp margin = -P3D::fp(P3D::pASR(radius, 4)); 17 | 18 | if(tri->edge_plane_0_1.DistanceToPoint(point) < margin) 19 | return false; 20 | 21 | if(tri->edge_plane_1_2.DistanceToPoint(point) < margin) 22 | return false; 23 | 24 | if(tri->edge_plane_2_0.DistanceToPoint(point) < margin) 25 | return false; 26 | 27 | //Move in direction of normal. 28 | P3D::fp penetrationDepth = radius - distance; 29 | 30 | resolutionVector = tri->normal_plane.Normal() * (penetrationDepth + P3D::fp(0.05)); 31 | 32 | return true; 33 | } 34 | -------------------------------------------------------------------------------- /P3DObj2Bsp/.gitignore: -------------------------------------------------------------------------------- 1 | # This file is used to ignore files which are generated 2 | # ---------------------------------------------------------------------------- 3 | 4 | *~ 5 | *.autosave 6 | *.a 7 | *.core 8 | *.moc 9 | *.o 10 | *.obj 11 | *.orig 12 | *.rej 13 | *.so 14 | *.so.* 15 | *_pch.h.cpp 16 | *_resource.rc 17 | *.qm 18 | .#* 19 | *.*# 20 | core 21 | !core/ 22 | tags 23 | .DS_Store 24 | .directory 25 | *.debug 26 | Makefile* 27 | *.prl 28 | *.app 29 | moc_*.cpp 30 | ui_*.h 31 | qrc_*.cpp 32 | Thumbs.db 33 | *.res 34 | *.rc 35 | /.qmake.cache 36 | /.qmake.stash 37 | 38 | # qtcreator generated files 39 | *.pro.user* 40 | CMakeLists.txt.user* 41 | 42 | # xemacs temporary files 43 | *.flc 44 | 45 | # Vim temporary files 46 | .*.swp 47 | 48 | # Visual Studio generated files 49 | *.ib_pdb_index 50 | *.idb 51 | *.ilk 52 | *.pdb 53 | *.sln 54 | *.suo 55 | *.vcproj 56 | *vcproj.*.*.user 57 | *.ncb 58 | *.sdf 59 | *.opensdf 60 | *.vcxproj 61 | *vcxproj.* 62 | 63 | # MinGW generated files 64 | *.Debug 65 | *.Release 66 | 67 | # Python byte code 68 | *.pyc 69 | 70 | # Binaries 71 | # -------- 72 | *.dll 73 | *.exe 74 | 75 | -------------------------------------------------------------------------------- /3dmaths/plane.h: -------------------------------------------------------------------------------- 1 | #ifndef PLANE_H 2 | #define PLANE_H 3 | 4 | #include "v3.h" 5 | 6 | namespace P3D 7 | { 8 | typedef enum FrustrumPlanes : unsigned int 9 | { 10 | Left = 0, 11 | Right = 1, 12 | Top = 2, 13 | Bottom = 3, 14 | Far = 4, 15 | Near = 5, 16 | } FrustrumPlanes; 17 | 18 | template class Plane 19 | { 20 | public: 21 | constexpr Plane() {} 22 | constexpr Plane(V3 normal, T distance) : n(normal), d(distance) {} 23 | 24 | constexpr void Normalise() 25 | { 26 | T length = n.Length(); 27 | n = n.Normalised(); 28 | d /= length; 29 | } 30 | 31 | constexpr T DistanceToPoint(const V3& v) const 32 | { 33 | return n.DotProduct(v) + d; 34 | } 35 | 36 | constexpr bool TriangleIsFrontside(const V3& v1, const V3& v2, const V3& v3) const 37 | { 38 | if ((DistanceToPoint(v1) < 0) && (DistanceToPoint(v2) < 0) && (DistanceToPoint(v3) < 0)) 39 | return false; 40 | 41 | return true; 42 | } 43 | 44 | constexpr V3 Normal() const {return n;} 45 | constexpr T Distance() const {return d;} 46 | 47 | private: 48 | V3 n; 49 | T d; 50 | }; 51 | } 52 | 53 | #endif // PLANE_H 54 | 55 | -------------------------------------------------------------------------------- /Potato3dExample2/P3DExample2.pro: -------------------------------------------------------------------------------- 1 | QT += core gui widgets 2 | 3 | CONFIG += c++20 4 | CONFIG += force_debug_info 5 | 6 | # You can make your code fail to compile if it uses deprecated APIs. 7 | # In order to do so, uncomment the following line. 8 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 9 | 10 | SOURCES += \ 11 | source/bspmodel.redir.iwram.cpp \ 12 | source/camera.cpp \ 13 | source/collision.cpp \ 14 | source/mainloop.iwram.cpp \ 15 | source/model.cpp \ 16 | source/main.cpp \ 17 | source/recip.redir.cpp \ 18 | source/setup.cpp \ 19 | source/videosystem.cpp \ 20 | source/worldmodel.cpp 21 | 22 | 23 | 24 | HEADERS += \ 25 | include/camera.h \ 26 | include/common.h \ 27 | include/mainloop.h \ 28 | include/model.h \ 29 | include/setup.h \ 30 | include/videosystem.h \ 31 | include/worldmodel.h \ 32 | include/collision.h 33 | 34 | 35 | # Default rules for deployment. 36 | qnx: target.path = /tmp/$${TARGET}/bin 37 | else: unix:!android: target.path = /opt/$${TARGET}/bin 38 | !isEmpty(target.path): INSTALLS += target 39 | 40 | DISTFILES += \ 41 | Makefile \ 42 | source/fixeddiv.iwram.s 43 | -------------------------------------------------------------------------------- /Potato3dExample/mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | #include 6 | 7 | #include "../potato3d.h" 8 | 9 | class MainWindow : public QMainWindow 10 | { 11 | Q_OBJECT 12 | 13 | public: 14 | MainWindow(QWidget *parent = nullptr); 15 | ~MainWindow(); 16 | 17 | protected: 18 | void paintEvent(QPaintEvent *event) override; 19 | 20 | void keyPressEvent(QKeyEvent *event) override; 21 | 22 | private: 23 | 24 | QElapsedTimer fpsTimer; 25 | QElapsedTimer renderTimer; 26 | 27 | 28 | P3D::Object3d* object3d; 29 | 30 | QImage frameBufferImage; 31 | 32 | //static const int screenWidth = 1920; 33 | //static const int screenHeight = 1080; 34 | 35 | //static const int screenWidth = 1280; 36 | //static const int screenHeight = 1024; 37 | 38 | //static const int screenWidth = 640; 39 | //static const int screenHeight = 512; 40 | 41 | //static const int screenWidth = 160; 42 | //static const int screenHeight = 128; 43 | 44 | static const int screenWidth = 240; 45 | static const int screenHeight = 160; 46 | 47 | //static const int screenWidth = 256; 48 | //static const int screenHeight = 256; 49 | }; 50 | #endif // MAINWINDOW_H 51 | -------------------------------------------------------------------------------- /Potato3dExample/mainwindow2.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW2_H 2 | #define MAINWINDOW2_H 3 | 4 | #include 5 | #include 6 | 7 | #include "../RenderDevice.h" 8 | #include "../RenderTarget.h" 9 | 10 | class MainWindow2 : public QMainWindow 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | MainWindow2(QWidget *parent = nullptr); 16 | ~MainWindow2(); 17 | 18 | protected: 19 | void paintEvent(QPaintEvent *event) override; 20 | 21 | void keyPressEvent(QKeyEvent *event) override; 22 | 23 | private: 24 | 25 | QElapsedTimer fpsTimer; 26 | QElapsedTimer renderTimer; 27 | 28 | QImage frameBufferImage; 29 | 30 | P3D::RenderTarget* render_target = nullptr; 31 | P3D::RenderDevice* render_device = nullptr; 32 | 33 | //static const int screenWidth = 1280; 34 | //static const int screenHeight = 1024; 35 | 36 | //static const int screenWidth = 640; 37 | //static const int screenHeight = 512; 38 | 39 | //static const int screenWidth = 160; 40 | //static const int screenHeight = 128; 41 | 42 | //static const int screenWidth = 240; 43 | //static const int screenHeight = 160; 44 | 45 | static const int screenWidth = 256; 46 | static const int screenHeight = 256; 47 | }; 48 | 49 | #endif // MAINWINDOW2_H 50 | -------------------------------------------------------------------------------- /ConfigUser.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIGUSER_H 2 | #define CONFIGUSER_H 3 | 4 | #include "3dmaths/f3dmath.h" 5 | #include "Pixel.h" 6 | 7 | namespace P3D 8 | { 9 | #ifndef __arm__ 10 | #define RENDER_STATS 11 | #endif 12 | 13 | //Type of a texture and framebuffer pixel. 14 | 15 | typedef uint8_t pixel; 16 | typedef Pixel<3,2,3, pixel> pixelType; 17 | 18 | inline constexpr int LIGHT_LEVELS = 8; 19 | inline constexpr int FOG_LEVELS = 16; 20 | 21 | 22 | //Width & height of a texture in pixels. 23 | inline constexpr int TEX_SIZE = 64; 24 | 25 | //Maximum UV tiling. Increasing this will reduce bits available for perspective correct texture mapper. 26 | inline constexpr int TEX_MAX_TILE = 8; 27 | 28 | inline constexpr int CLIP_GUARD_BAND_SHIFT = 1; 29 | 30 | 31 | //#define USE_FLOAT 32 | #ifdef USE_FLOAT 33 | typedef double fp; 34 | #else 35 | typedef FP16 fp; 36 | #endif 37 | 38 | typedef fp z_val; 39 | 40 | inline constexpr int SUBDIVIDE_SPAN_LEN = 16; 41 | //inline constexpr fp SUBDIVIDE_Z_THREASHOLD = fp(5); 42 | inline constexpr fp SUBDIVIDE_Z_THREASHOLD = fp(2); 43 | 44 | //#define no_inline __attribute__((noinline)) 45 | #define no_inline 46 | 47 | } 48 | 49 | #endif // CONFIGUSER_H 50 | -------------------------------------------------------------------------------- /TextureCache.h: -------------------------------------------------------------------------------- 1 | #ifndef TEXTURECACHE_H 2 | #define TEXTURECACHE_H 3 | 4 | #include "Config.h" 5 | 6 | namespace P3D 7 | { 8 | class TextureCacheBase 9 | { 10 | public: 11 | TextureCacheBase() = default; 12 | virtual ~TextureCacheBase() = default; 13 | 14 | virtual void AddTexture(const pixel* texture, const signed char importance = 0) = 0; 15 | virtual void RemoveTexture(const pixel* texture) = 0; 16 | virtual const pixel* GetTexture(const pixel* texture) const = 0; 17 | virtual void ClearTextureCache() = 0; 18 | }; 19 | 20 | class TextureCacheDefault final : public TextureCacheBase 21 | { 22 | public: 23 | 24 | TextureCacheDefault() = default; 25 | ~TextureCacheDefault() = default; 26 | 27 | void AddTexture(const pixel* texture, const signed char importance = 0) 28 | { 29 | (void)texture; 30 | (void)importance; 31 | } 32 | 33 | void RemoveTexture(const pixel* texture) 34 | { 35 | (void)texture; 36 | } 37 | 38 | const pixel* GetTexture(const pixel* texture) const 39 | { 40 | return texture; 41 | } 42 | 43 | virtual void ClearTextureCache() {} 44 | }; 45 | 46 | }; 47 | 48 | #endif // TEXTURECACHE_H 49 | -------------------------------------------------------------------------------- /Potato3dExample2/include/mainloop.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINLOOP_H 2 | #define MAINLOOP_H 3 | 4 | #include "../include/common.h" 5 | #include "../../Config.h" 6 | #include "../../3dmaths/f3dmath.h" 7 | 8 | #include "../../RenderDevice.h" 9 | #include "../../PixelShaderGBA8.h" 10 | 11 | #include "../include/videosystem.h" 12 | #include "../include/camera.h" 13 | #include "../include/worldmodel.h" 14 | #include "../include/collision.h" 15 | 16 | class MainLoop 17 | { 18 | public: 19 | MainLoop(); 20 | 21 | void Run(); 22 | 23 | private: 24 | 25 | void UpdateFrustrumBB(); 26 | void RenderModel(); 27 | bool FrustrumTestTriangle(const P3D::BspModelTriangle* tri) const; 28 | void ResolveCollisions(); 29 | void RunTimeslots(); 30 | 31 | static constexpr P3D::fp zNear = 10; 32 | static constexpr P3D::fp zFar = 1500; 33 | static constexpr P3D::fp vFov = 60; 34 | static constexpr P3D::fp hFov = 90; 35 | 36 | static constexpr unsigned int frameTicks = 50; 37 | 38 | P3D::fp gravity_velocity = 0; 39 | 40 | 41 | P3D::V3 frustrumPoints[4]; //Top left and bottom-right frustrum points. 42 | 43 | P3D::Plane frustrumPlanes[6]; 44 | 45 | P3D::AABB viewFrustrumBB; 46 | 47 | P3D::RenderDevice renderDev; 48 | 49 | Camera camera; 50 | WorldModel model; 51 | VideoSystem vid; 52 | Collision collision; 53 | 54 | 55 | unsigned short keyState = 0; 56 | 57 | std::vector triBuffer; 58 | }; 59 | 60 | #endif // MAINLOOP_H 61 | -------------------------------------------------------------------------------- /3dmaths/v2.h: -------------------------------------------------------------------------------- 1 | #ifndef V2_H 2 | #define V2_H 3 | 4 | #include "fp.h" 5 | 6 | namespace P3D 7 | { 8 | template class V2 9 | { 10 | public: 11 | T x; 12 | T y; 13 | 14 | V2(T x, T y) : x(x), y(y) {} 15 | V2() {} 16 | ~V2() {} 17 | 18 | constexpr V2 operator+(const V2& r) const 19 | { 20 | V2 v(x,y); 21 | return v+=r; 22 | } 23 | 24 | constexpr V2 operator-(const V2& r) const 25 | { 26 | V2 v(x,y); 27 | return v-=r; 28 | } 29 | 30 | const V2 operator*(const V2& r) const 31 | { 32 | V2 v(x,y); 33 | return v*=r; 34 | } 35 | 36 | constexpr V2 operator*(const T& r) const 37 | { 38 | V2 v(x,y); 39 | 40 | v.x *= r; 41 | v.y *= r; 42 | 43 | return v; 44 | } 45 | 46 | constexpr V2& operator+=(const V2& r) 47 | { 48 | x += r.x; 49 | y += r.y; 50 | return *this; 51 | } 52 | 53 | constexpr V2& operator-=(const V2& r) 54 | { 55 | x -= r.x; 56 | y -= r.y; 57 | return *this; 58 | } 59 | 60 | constexpr V2& operator*=(const V2& r) 61 | { 62 | x *= r.x; 63 | y *= r.y; 64 | return *this; 65 | } 66 | }; 67 | 68 | typedef V2 V2F; 69 | typedef V2 V2D; 70 | typedef V2 V2FP; 71 | 72 | } 73 | #endif // V2_H 74 | 75 | -------------------------------------------------------------------------------- /Potato3dExample2/source/camera.cpp: -------------------------------------------------------------------------------- 1 | #include "../include/camera.h" 2 | 3 | Camera::Camera() 4 | { 5 | position = P3D::V3(-2511,1050,-3108); 6 | //position = P3D::V3(-230,39,6152); 7 | 8 | //position = P3D::V3(8508,13,8563); 9 | //angle = P3D::V3(0,-236,0); 10 | } 11 | 12 | const P3D::V3& Camera::GetPosition() const 13 | { 14 | return position; 15 | } 16 | 17 | const P3D::V3 Camera::GetEyePosition() const 18 | { 19 | return (position + eyeOffset); 20 | } 21 | 22 | const P3D::V3& Camera::GetAngle() const 23 | { 24 | return angle; 25 | } 26 | 27 | void Camera::HandleInput(unsigned int keyState, P3D::fp gravity_velocity) 28 | { 29 | if(keyState & KeyLeft) 30 | angle.y += 5; 31 | 32 | if(keyState & KeyRight) 33 | angle.y -= 5; 34 | 35 | if(keyState & KeyUp) 36 | { 37 | P3D::V3 camAngle = angle; 38 | 39 | float angleYRad = P3D::pD2R(camAngle.y); 40 | 41 | P3D::V3 d((float)-(std::sin(angleYRad) *20), 0, (float)-(std::cos(angleYRad) *20)); 42 | 43 | position += d; 44 | } 45 | 46 | if(keyState & KeyDown) 47 | { 48 | P3D::V3 camAngle = angle; 49 | 50 | float angleYRad = P3D::pD2R(camAngle.y); 51 | 52 | P3D::V3 d((float)-(std::sin(angleYRad) *20), 0, (float)-(std::cos(angleYRad) *20)); 53 | 54 | position -= d; 55 | } 56 | 57 | position.y -= gravity_velocity; 58 | 59 | } 60 | 61 | void Camera::MovePosition(const P3D::V3& delta) 62 | { 63 | position += delta; 64 | } 65 | 66 | void Camera::SetPosition(const P3D::V3& pos) 67 | { 68 | position = pos; 69 | } 70 | -------------------------------------------------------------------------------- /object3d.h: -------------------------------------------------------------------------------- 1 | #ifndef OBJECT3D_H 2 | #define OBJECT3D_H 3 | 4 | #include 5 | #include 6 | 7 | #include "Config.h" 8 | #include "bspmodel.h" 9 | 10 | #include "RenderDevice.h" 11 | 12 | namespace P3D 13 | { 14 | class Object3d 15 | { 16 | public: 17 | Object3d(); 18 | 19 | bool Setup(unsigned int screenWidth, unsigned int screenHeight, fp hFov = 54, fp zNear = 5, fp zFar = 1024, pixel *framebuffer = nullptr); 20 | 21 | V3& CameraPos(); 22 | V3& CameraAngle(); 23 | 24 | void DoCollisions(); 25 | bool CheckCollision(const BspModelTriangle* tri, V3& point, const fp radius); 26 | 27 | 28 | void RenderScene(); 29 | 30 | void SetModel(const BspModel* model); 31 | 32 | void SetBackgroundColor(pixel color); 33 | 34 | void SetFrameBuffer(pixel* buffer); 35 | 36 | #ifdef RENDER_STATS 37 | const RenderStats& GetRenderStats(); 38 | #endif 39 | 40 | bool update_frustrum_bb = true; 41 | bool do_collisions = true; 42 | fp light_level = 1.0; 43 | 44 | private: 45 | 46 | void RenderBsp(); 47 | 48 | void UpdateFrustrumAABB(); 49 | 50 | RenderDevice* render_device = nullptr; 51 | 52 | RenderTarget* render_target = nullptr; 53 | 54 | const BspModel* model; 55 | 56 | AABB viewFrustrumBB; 57 | 58 | V3 eyePos; 59 | //V3 cameraPos = V3(0,50,0); 60 | V3 cameraPos = V3(8508,13,8563); 61 | V3 cameraAngle = V3(0,-236,0); 62 | pixel backgroundColor = 0x799ED7; 63 | 64 | V3 frustrumPoints[4]; //Top left and bottom-right frustrum points. 65 | 66 | Plane frustrumPlanes[6]; 67 | }; 68 | } 69 | 70 | #endif // OBJECT3D_H 71 | -------------------------------------------------------------------------------- /BspModelDefs.h: -------------------------------------------------------------------------------- 1 | #include "3dmaths/f3dmath.h" 2 | 3 | namespace P3D 4 | { 5 | class Vertex3d 6 | { 7 | public: 8 | V3 pos; 9 | unsigned int vertex_id = no_vx_id; 10 | V2 uv; 11 | 12 | static const unsigned int no_vx_id = -1; 13 | }; 14 | 15 | class Triangle3d 16 | { 17 | public: 18 | Vertex3d verts[3]; 19 | }; 20 | 21 | class Texture 22 | { 23 | public: 24 | const pixel* pixels; 25 | unsigned short width; 26 | unsigned short height; 27 | bool alpha; 28 | }; 29 | 30 | typedef struct BspModelHeader 31 | { 32 | unsigned int texture_count; 33 | unsigned int texture_offset; //Offset in bytes from BspModel* 34 | 35 | unsigned int triangle_count; 36 | unsigned int triangle_offset; //Bytes from BspModel* 37 | 38 | unsigned int node_count; 39 | unsigned int node_offset; 40 | 41 | unsigned int texture_pixels_offset; //Bytes from BspModel* 42 | 43 | unsigned int texture_palette_offset; //Bytes from BspModel* 44 | 45 | unsigned int fog_lightmap_offset; //Bytes from BspModel* 46 | 47 | } BspModelHeader; 48 | 49 | typedef struct BspModelTriangle 50 | { 51 | Triangle3d tri; 52 | AABB tri_bb; 53 | 54 | Plane normal_plane; 55 | Plane edge_plane_0_1; 56 | Plane edge_plane_1_2; 57 | Plane edge_plane_2_0; 58 | 59 | int texture; 60 | pixel color; 61 | } BspModelTriangle; 62 | 63 | typedef struct TriIndexList 64 | { 65 | unsigned short offset; 66 | unsigned short count; 67 | } TriIndexList; 68 | 69 | typedef struct BspNodeTexture 70 | { 71 | unsigned int texture_pixels_offset; //Pixels 72 | unsigned short width; 73 | unsigned short height; 74 | bool alpha; 75 | } BspNodeTexture; 76 | 77 | typedef struct BspModelNode 78 | { 79 | Plane plane; 80 | AABB node_bb; 81 | AABB child_bb; 82 | unsigned int parent_node; 83 | unsigned int front_node; 84 | unsigned int back_node; 85 | TriIndexList front_tris; 86 | TriIndexList back_tris; 87 | } BspModelNode; 88 | } 89 | -------------------------------------------------------------------------------- /Potato3dExample/Potato3dExample.pro: -------------------------------------------------------------------------------- 1 | QT += core gui widgets 2 | 3 | CONFIG += c++20 4 | CONFIG += force_debug_info 5 | 6 | 7 | # The following define makes your compiler emit warnings if you use 8 | # any Qt feature that has been marked deprecated (the exact warnings 9 | # depend on your compiler). Please consult the documentation of the 10 | # deprecated API in order to know how to port your code away from it. 11 | DEFINES += QT_DEPRECATED_WARNINGS 12 | DEFINES += _USE_MATH_DEFINES 13 | 14 | # You can also make your code fail to compile if it uses deprecated APIs. 15 | # In order to do so, uncomment the following line. 16 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 17 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 18 | 19 | SOURCES += \ 20 | ../3dmaths/recip.cpp \ 21 | ../bspmodel.cpp \ 22 | ../object3d.cpp \ 23 | main.cpp \ 24 | mainwindow.cpp \ 25 | mainwindow2.cpp 26 | 27 | HEADERS += \ 28 | ../3dmaths/divide.h \ 29 | ../3dmaths/f3dmath.h \ 30 | ../3dmaths/fp.h \ 31 | ../3dmaths/m4.h \ 32 | ../3dmaths/plane.h \ 33 | ../3dmaths/recip.h \ 34 | ../3dmaths/utils.h \ 35 | ../3dmaths/v2.h \ 36 | ../3dmaths/v3.h \ 37 | ../3dmaths/v4.h \ 38 | ../3dmaths/aabb.h \ 39 | ../BspModelDefs.h \ 40 | ../Config.h \ 41 | ../ConfigInternal.h \ 42 | ../ConfigUser.h \ 43 | ../PixelShaderDefault.h \ 44 | ../PixelShaderGBA8.h \ 45 | ../RenderCommon.h \ 46 | ../RenderDevice.h \ 47 | ../RenderTarget.h \ 48 | ../RenderTriangle.h \ 49 | ../TextureCache.h \ 50 | ../Pixel.h \ 51 | ../bspmodel.h \ 52 | ../object3d.h \ 53 | ../potato3d.h \ 54 | mainwindow.h \ 55 | mainwindow2.h \ 56 | models/model.h 57 | 58 | # Default rules for deployment. 59 | qnx: target.path = /tmp/$${TARGET}/bin 60 | else: unix:!android: target.path = /opt/$${TARGET}/bin 61 | !isEmpty(target.path): INSTALLS += target 62 | 63 | RESOURCES += \ 64 | Resources.qrc 65 | 66 | #LIBS += $$PWD/codeprophet.lib 67 | #QMAKE_CXXFLAGS += /GH /Gh 68 | #QMAKE_CFLAGS += /GH /Gh 69 | 70 | QMAKE_CXXFLAGS += /GL 71 | QMAKE_CFLAGS += /GL 72 | 73 | QMAKE_LFLAGS += /LTCG 74 | -------------------------------------------------------------------------------- /P3DObj2Bsp/objloader.h: -------------------------------------------------------------------------------- 1 | #ifndef OBJLOADER_H 2 | #define OBJLOADER_H 3 | 4 | #include 5 | #include 6 | 7 | #include "config.h" 8 | 9 | namespace Obj2Bsp 10 | { 11 | 12 | class Vertex3d 13 | { 14 | public: 15 | QVector3D pos; 16 | unsigned int vertex_id = no_vx_id; 17 | QVector2D uv; 18 | 19 | static const unsigned int no_vx_id = -1; 20 | }; 21 | 22 | class Triangle3d 23 | { 24 | public: 25 | Vertex3d verts[3]; 26 | }; 27 | 28 | class Texture 29 | { 30 | public: 31 | QByteArray pixels; 32 | unsigned short width; 33 | unsigned short height; 34 | bool alpha; 35 | }; 36 | 37 | class Mesh3d 38 | { 39 | public: 40 | QRgb color = 0; 41 | Texture* texture = nullptr; 42 | 43 | QList tris; 44 | }; 45 | 46 | class Model3d 47 | { 48 | public: 49 | QVector3D pos = QVector3D(0,0,0); 50 | QList mesh; 51 | unsigned int vertex_id_count = 0; 52 | unsigned int colormap[256]; 53 | unsigned char foglightmap[256][16][16]; 54 | }; 55 | 56 | 57 | class ObjLoader 58 | { 59 | public: 60 | ObjLoader(); 61 | bool LoadObjFile(QString filePath); 62 | 63 | Model3d* GetModel(); 64 | 65 | private: 66 | 67 | void ParseGeometry(QString objText, QMap& textures, QMap& textureColors); 68 | 69 | void ParseMaterials(QString mtlText, QDir texturePath, QMap& textures, QMap& textureColors); 70 | QImage GetMegaTexture(QMap& textures); 71 | 72 | QImage QuantizeMegaTexture(QImage megaTexture, QByteArray& fogLightMap); 73 | 74 | QImage nQuantCppImage(QImage imageIn); 75 | 76 | QImage GetImageWithFogAndLightmap(QImage imageIn); 77 | 78 | QColor blendColor(QColor color1, QColor color2, double frac); 79 | 80 | QColor rgbToLab(const QColor &color); 81 | double colorDistanceCIEDE2000(const QColor &lab1, const QColor &lab2); 82 | 83 | double colorDiff(QColor e1, QColor e2); 84 | 85 | QByteArray GenerateFogAndLightTables(QImage imageIn); 86 | 87 | void CopyTextureDataToTextures(QImage megaTexture, QMap& textures); 88 | 89 | Model3d* model = nullptr; 90 | 91 | }; 92 | } 93 | 94 | #endif // OBJLOADER_H 95 | -------------------------------------------------------------------------------- /Pixel.h: -------------------------------------------------------------------------------- 1 | #ifndef PIXEL_H 2 | #define PIXEL_H 3 | 4 | namespace P3D 5 | { 6 | template class Pixel 7 | { 8 | public: 9 | 10 | constexpr Pixel(const Pixel& r) : p(r.p) {} 11 | constexpr Pixel(const TStorageType v) : p(v) {} 12 | 13 | constexpr operator TStorageType() const {return p;} 14 | 15 | constexpr Pixel(const unsigned int r, const unsigned int g, const unsigned int b) 16 | { 17 | Set(r, g, b); 18 | } 19 | 20 | constexpr unsigned int R() const 21 | { 22 | return (p & rmask()) >> rshift(); 23 | } 24 | 25 | constexpr unsigned int G() const 26 | { 27 | return (p & gmask()) >> gshift(); 28 | } 29 | 30 | constexpr unsigned int B() const 31 | { 32 | return (p & bmask()) >> bshift(); 33 | } 34 | 35 | constexpr void Set(unsigned int r, unsigned int g, unsigned int b) 36 | { 37 | p = ( 38 | ((r << rshift()) & rmask()) | 39 | ((g << gshift()) & gmask()) | 40 | ((b << bshift()) & bmask()) 41 | ); 42 | } 43 | 44 | private: 45 | 46 | constexpr unsigned int rmask() const 47 | { 48 | return ((1 << RBits)-1) << rshift(); 49 | } 50 | 51 | constexpr unsigned int gmask() const 52 | { 53 | return ((1 << GBits)-1) << gshift(); 54 | } 55 | 56 | constexpr unsigned int bmask() const 57 | { 58 | return ((1 << BBits)-1) << bshift(); 59 | } 60 | 61 | constexpr unsigned int rshift() const 62 | { 63 | return 0; 64 | } 65 | 66 | constexpr unsigned int gshift() const 67 | { 68 | return RBits; 69 | } 70 | 71 | constexpr unsigned int bshift() const 72 | { 73 | return (GBits + RBits); 74 | } 75 | 76 | TStorageType p; 77 | }; 78 | 79 | typedef Pixel<8,8,8,unsigned int> RGB888; //24bit color 80 | typedef Pixel<5,6,5,unsigned short> RGB565; //16bit color 81 | typedef Pixel<5,5,5,unsigned short> RGB555; //15bit color 82 | } 83 | #endif // PIXEL_H 84 | -------------------------------------------------------------------------------- /Qt Debug Helper/personaltypes.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 The Qt Company Ltd. 2 | # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 3 | 4 | # This is a place to add your own dumpers for testing purposes. 5 | # Any contents here will be picked up by GDB, LLDB, and CDB based 6 | # debugging in Qt Creator automatically. 7 | 8 | # NOTE: This file will get overwritten when updating Qt Creator. 9 | # 10 | # To add dumpers that don't get overwritten, copy this file here 11 | # to a safe location outside the Qt Creator installation and 12 | # make this location known to Qt Creator using the Debugger > 13 | # Locals & Expressions > Extra Debugging Helpers setting. 14 | 15 | # Example to display a simple type 16 | # template struct MapNode 17 | # { 18 | # U key; 19 | # V data; 20 | # } 21 | # 22 | # def qdump__MapNode(d, value): 23 | # d.putValue("This is the value column contents") 24 | # d.putExpandable() 25 | # if d.isExpanded(): 26 | # with Children(d): 27 | # # Compact simple case. 28 | # d.putSubItem("key", value["key"]) 29 | # # Same effect, with more customization possibilities. 30 | # with SubItem(d, "data") 31 | # d.putItem("data", value["data"]) 32 | 33 | # Check http://doc.qt.io/qtcreator/creator-debugging-helpers.html 34 | # for more details or look at qttypes.py, stdtypes.py, boosttypes.py 35 | # for more complex examples. 36 | 37 | from dumper import Children, SubItem, UnnamedSubItem, DumperBase 38 | from utils import DisplayFormat, TypeCode 39 | 40 | ######################## Your code below ####################### 41 | 42 | def qdump__P3D__FP(d, value): 43 | # Get the template parameter (fracbits) 44 | innerType = value.type 45 | fracbits = innerType.templateArgument(0) 46 | 47 | # Get the private member 'n' 48 | n = value["n"].integer() 49 | 50 | # Calculate the floating point value 51 | divisor = 1 << fracbits 52 | fp_value = float(n) / float(divisor) 53 | 54 | # Display format: show both the floating point value and raw integer 55 | d.putValue("%.6f (raw: %d)" % (fp_value, n)) 56 | d.putPlainChildren(value) 57 | 58 | # Add child items for detailed view 59 | d.putNumChild(2) 60 | if d.isExpanded(): 61 | with Children(d): 62 | d.putSubItem("floating_value", fp_value) 63 | d.putSubItem("raw_value", n) 64 | d.putSubItem("fracbits", fracbits) -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | build-Potato3dExample-Desktop_Qt_5_14_2_MinGW_32_bit-Debug/ 3 | 4 | build-Potato3dExample-Desktop_Qt_5_14_2_MSVC2017_64bit-Debug/ 5 | 6 | build-Potato3dExample-Desktop_Qt_5_14_2_MSVC2017_64bit-Release/ 7 | 8 | build-Potato3dExample-Desktop_Qt_5_14_2_MSVC2017_32bit-Release/ 9 | 10 | *.user 11 | 12 | Potato3dGBAExample/build/ 13 | 14 | Potato3dGBAExample/Potato3dGBAExample.sav 15 | 16 | 17 | build-Potato3dGBAExample-Desktop_Qt_5_14_2_MSVC2017_64bit-Release/ 18 | 19 | Potato3dExample/models/GE_Facility/ 20 | 21 | Potato3dExample/models/model.cpp 22 | 23 | build-Potato3dExample-Desktop_Qt_5_14_2_MSVC2017_32bit-Debug/ 24 | 25 | Potato3dExample/models/ 26 | 27 | ~$P3DFps.xlsx 28 | 29 | build-Potato3dExample-Desktop_Qt_5_15_1_MSVC2019_64bit-Release/ 30 | 31 | build-Potato3dExample-Desktop_Qt_5_15_1_MSVC2019_64bit-Debug/ 32 | 33 | build-Potato3dExample-Desktop_Qt_5_15_1_MSVC2019_32bit-Release/ 34 | 35 | build-Potato3dExample-Desktop_Qt_5_15_2_MSVC2019_32bit-Debug/ 36 | 37 | build-Potato3dExample-Desktop_Qt_5_15_2_MSVC2019_32bit-Release/ 38 | 39 | build-Potato3dExample-Desktop_Qt_5_15_2_MSVC2019_64bit-Debug/ 40 | 41 | build-Potato3dExample-Desktop_Qt_5_15_2_MinGW_32_bit-Debug/ 42 | 43 | build-Potato3dExample-Desktop_Qt_6_3_0_MSVC2019_64bit-Debug/ 44 | 45 | build-Potato3dExample-Desktop_Qt_6_4_0_MSVC2019_64bit-Debug/ 46 | 47 | build-Potato3dExample-Desktop_Qt_6_4_0_MSVC2019_64bit-Release/ 48 | 49 | Potato3dGBAExample/.qtc_clangd/ 50 | 51 | build-P3DBenchmark-Desktop_Qt_6_4_2_MinGW_64_bit-Debug/ 52 | 53 | build-P3DBenchmark-Desktop_Qt_6_4_2_MSVC2019_64bit-Debug/ 54 | 55 | build-Potato3dGBAExample-Desktop_Qt_6_4_2_MinGW_64_bit-Debug/ 56 | 57 | build-Potato3dGBAExample-Desktop_Qt_6_4_2_MinGW_64_bit-GBA/ 58 | 59 | build-Potato3dGBAExample-Desktop_Qt_6_4_2_MSVC2019_64bit-Debug/ 60 | 61 | P3DBenchmark/.qtc_clangd/ 62 | 63 | P3DBenchmark/build/ 64 | 65 | build-P3DBenchmark-Desktop_Qt_6_4_2_MinGW_64_bit-GBA/ 66 | 67 | build-P3DBenchmark-Desktop_Qt_6_4_2_MinGW_64_bit-Release/ 68 | 69 | build-P3DBenchmark-Desktop_Qt_6_4_2_MSVC2019_64bit-Release/ 70 | 71 | build-Potato3dExample-Desktop_Qt_6_4_2_MinGW_64_bit-Debug/ 72 | 73 | P3DBenchmark/~$Performance.xlsx 74 | 75 | build-Potato3dExample-Desktop_Qt_6_4_2_MinGW_64_bit-Release/ 76 | 77 | build-Potato3dExample-Desktop_Qt_6_4_2_MSVC2019_64bit-Debug/ 78 | 79 | Potato3dExample/build/ 80 | 81 | build-Potato3dExample-Desktop_Qt_6_4_2_MSVC2019_64bit-Release/ 82 | 83 | P3DObj2Bsp/build/ 84 | 85 | Potato3dExample2/build/ 86 | 87 | Potato3dExample2/.qtc_clangd/ 88 | -------------------------------------------------------------------------------- /3dmaths/v4.h: -------------------------------------------------------------------------------- 1 | #ifndef V4_H 2 | #define V4_H 3 | 4 | #include "fp.h" 5 | #include "v3.h" 6 | #include "utils.h" 7 | 8 | namespace P3D 9 | { 10 | 11 | template class V4 12 | { 13 | public: 14 | T x; 15 | T y; 16 | T z; 17 | T w; 18 | 19 | V4(T x, T y, T z, T w) : x(x), y(y), z(z), w(w) {} 20 | V4() {} 21 | ~V4() {} 22 | 23 | constexpr V4 operator+(const V4& r) const 24 | { 25 | V4 v(x,y,z,w); 26 | return v+=r; 27 | } 28 | 29 | constexpr V4 operator-(const V4& r) const 30 | { 31 | V4 v(x,y,z,w); 32 | return v-=r; 33 | } 34 | 35 | constexpr V4 operator*(const V4& r) const 36 | { 37 | V4 v(x,y,z,w); 38 | return v*=r; 39 | } 40 | 41 | constexpr V4 operator*(const T& r) const 42 | { 43 | V4 v(x,y,z,w); 44 | 45 | v.x *= r; 46 | v.y *= r; 47 | v.z *= r; 48 | v.w *= r; 49 | 50 | return v; 51 | } 52 | 53 | constexpr V4& operator+=(const V4& r) 54 | { 55 | x += r.x; 56 | y += r.y; 57 | z += r.z; 58 | w += r.w; 59 | 60 | return *this; 61 | } 62 | 63 | constexpr V4& operator-=(const V4& r) 64 | { 65 | x -= r.x; 66 | y -= r.y; 67 | z -= r.z; 68 | w -= r.w; 69 | 70 | return *this; 71 | } 72 | 73 | constexpr V4& operator*=(const V4& r) 74 | { 75 | x *= r.x; 76 | y *= r.y; 77 | z *= r.z; 78 | w *= r.w; 79 | 80 | return *this; 81 | } 82 | 83 | constexpr V3 CrossProduct(const V4& r) const 84 | { 85 | V3 v; 86 | 87 | v.x = ((y * r.z) - (z * r.y)); 88 | v.y = ((z * r.x) - (x * r.z)); 89 | v.z = ((x * r.y) - (y * r.x)); 90 | 91 | return v; 92 | } 93 | 94 | constexpr void ToScreenSpace() 95 | { 96 | if (w != T(1)) 97 | { 98 | x = x / w; 99 | y = y / w; 100 | z = z / w; 101 | } 102 | } 103 | }; 104 | 105 | typedef V4 V4F; 106 | typedef V4 V4D; 107 | typedef V4 V4FP; 108 | 109 | } 110 | 111 | #endif // V4_H 112 | -------------------------------------------------------------------------------- /P3DObj2Bsp/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "objloader.h" 3 | #include "bspbuilder.h" 4 | #include "bspmodelexport.h" 5 | 6 | void SaveBytesAsCFile(QByteArray bytes, QString file); 7 | 8 | int main(int argc, char *argv[]) 9 | { 10 | QCoreApplication a(argc, argv); 11 | 12 | QImageReader::setAllocationLimit(4096); 13 | 14 | Obj2Bsp::ObjLoader loader; 15 | 16 | //QString objPath = "C:\\Users\\Zak\\Downloads\\GE_temple\\temple.obj"; 17 | //QString objPath = "C:\\Users\\Zak\\Downloads\\DDHQ\\ddhq.obj"; 18 | //QString objPath = "C:\\Users\\Zak\\Downloads\\DDHQ2\\ddhq2.obj"; 19 | 20 | QString objPath = "C:\\Users\\Zak\\Downloads\\Villa\\villa.obj"; 21 | //QString objPath = "C:\\Users\\Zak\\Downloads\\Dam\\dam.obj"; 22 | //QString objPath = "C:\\Users\\Zak\\Downloads\\Temple\\temple.obj"; 23 | //QString objPath = "C:\\Users\\Zak\\Documents\\GitProjects\\Potato3d\\Potato3dExample\\models\\Streets\\Streets.obj"; 24 | //QString objPath = "C:\\Users\\Zak\\Documents\\GitProjects\\Potato3d\\Potato3dExample\\models\\temple.obj"; 25 | //QString objPath = "C:\\Users\\Zak\\Documents\\GitProjects\\Potato3d\\Potato3dExample\\models\\hf\\hf2.obj"; 26 | //QString objPath = "C:\\Users\\Zak\\Documents\\GitProjects\\Potato3d\\Potato3dExample\\models\\d2\\driver2_small.obj"; 27 | 28 | bool result = loader.LoadObjFile(objPath); 29 | 30 | if(!result) 31 | return 0; 32 | 33 | Obj2Bsp::BspBuilder bspBuilder; 34 | 35 | Obj2Bsp::BspNode* root = bspBuilder.BuildBSPTree(loader.GetModel()); 36 | 37 | Obj2Bsp::BspModelExport bspExport; 38 | 39 | QByteArray bspData = bspExport.ExportBSPModel(root, loader.GetModel()); 40 | 41 | QDir workDir = QDir(QFileInfo(objPath).absolutePath()); 42 | QString baseName = QFileInfo(objPath).fileName().chopped(3); 43 | 44 | QFile bspFile(workDir.filePath(baseName + "bsp")); 45 | bspFile.open(QFile::Truncate | QFile::ReadWrite); 46 | bspFile.write(bspData); 47 | bspFile.close(); 48 | 49 | SaveBytesAsCFile(bspData, workDir.filePath(baseName + "cpp")); 50 | } 51 | 52 | void SaveBytesAsCFile(QByteArray bytes, QString file) 53 | { 54 | QFile f(file); 55 | 56 | if(!f.open(QIODevice::Truncate | QIODevice::ReadWrite)) 57 | return; 58 | 59 | QString decl = QString("const extern unsigned char modeldata[%1UL] = {\n").arg(bytes.size()); 60 | 61 | f.write(decl.toLatin1()); 62 | 63 | for(int i = 0; i < bytes.size(); i++) 64 | { 65 | QString element = QString("0x%1,").arg((quint8)bytes.at(i),2, 16, QChar('0')); 66 | 67 | if(( (i+1) % 40) == 0) 68 | element += "\n"; 69 | 70 | f.write(element.toLatin1()); 71 | } 72 | 73 | QString close = QString("\n};"); 74 | f.write(close.toLatin1()); 75 | 76 | f.close(); 77 | } 78 | -------------------------------------------------------------------------------- /Potato3dExample2/include/videosystem.h: -------------------------------------------------------------------------------- 1 | #ifndef VIDEOSYSTEM_H 2 | #define VIDEOSYSTEM_H 3 | 4 | #include "../include/common.h" 5 | 6 | #include "../../RenderTarget.h" 7 | 8 | #ifndef GBA 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | class GameWindow : public QWidget 16 | { 17 | public: 18 | explicit GameWindow(unsigned short* keyState) : QWidget() 19 | { 20 | this->keyState = keyState; 21 | } 22 | 23 | void SetBackbuffer(QImage* image) { backBuffer = image; } 24 | 25 | protected: 26 | void paintEvent(QPaintEvent *event) override 27 | { 28 | Q_UNUSED(event) 29 | 30 | if(!backBuffer) 31 | return; 32 | 33 | QPainter p(this); 34 | 35 | p.drawImage(this->rect(), *backBuffer, backBuffer->rect()); 36 | } 37 | 38 | void closeEvent(QCloseEvent *event) override 39 | { 40 | Q_UNUSED(event) 41 | 42 | exit(0); 43 | } 44 | 45 | void keyPressEvent(QKeyEvent *event) override 46 | { 47 | if(event->key() == Qt::Key_Up) 48 | *keyState |= KeyUp; 49 | 50 | if(event->key() == Qt::Key_Down) 51 | *keyState |= KeyDown; 52 | 53 | if(event->key() == Qt::Key_Left) 54 | *keyState |= KeyLeft; 55 | 56 | if(event->key() == Qt::Key_Right) 57 | *keyState |= KeyRight; 58 | } 59 | 60 | void keyReleaseEvent(QKeyEvent *event) override 61 | { 62 | if(event->key() == Qt::Key_Up) 63 | *keyState &= ~KeyUp; 64 | 65 | if(event->key() == Qt::Key_Down) 66 | *keyState &= ~KeyDown; 67 | 68 | if(event->key() == Qt::Key_Left) 69 | *keyState &= ~KeyLeft; 70 | 71 | if(event->key() == Qt::Key_Right) 72 | *keyState &= ~KeyRight; 73 | } 74 | 75 | private: 76 | QImage* backBuffer = nullptr; 77 | 78 | unsigned short* keyState = nullptr; 79 | }; 80 | #endif 81 | 82 | 83 | class VideoSystem 84 | { 85 | public: 86 | explicit VideoSystem(); 87 | 88 | void Setup(unsigned short *keyState); 89 | const P3D::RenderTarget *GetBackBuffer(); 90 | void PageFlip(); 91 | void SetPalette(const unsigned int pal[]); 92 | 93 | void UpdateKeys(); 94 | unsigned int GetTime(); 95 | 96 | private: 97 | P3D::RenderTarget* buffers[2] = {nullptr}; 98 | int currentBuffer = 1; 99 | 100 | const int width = 240; 101 | const int height = 160; 102 | 103 | unsigned short* keyState = nullptr; 104 | 105 | #ifndef GBA 106 | QImage *image[2]; 107 | QApplication* application = nullptr; 108 | GameWindow* window = nullptr; 109 | 110 | QElapsedTimer timer; 111 | #endif 112 | 113 | }; 114 | 115 | #endif // VIDEOSYSTEM_H 116 | -------------------------------------------------------------------------------- /3dmaths/aabb.h: -------------------------------------------------------------------------------- 1 | #ifndef AABB_H 2 | #define AABB_H 3 | 4 | #include "v3.h" 5 | #include "utils.h" 6 | 7 | namespace P3D 8 | { 9 | 10 | template class AABB 11 | { 12 | public: 13 | explicit constexpr AABB() 14 | { 15 | x1 = y1 = z1 = std::numeric_limits::max(); 16 | x2 = y2 = z2 = std::numeric_limits::lowest(); 17 | } 18 | 19 | explicit constexpr AABB(const V3& point) 20 | { 21 | AddPoint(point); 22 | } 23 | 24 | explicit constexpr AABB(const V3& point, T size) 25 | { 26 | x1 = point.x - pASR(size,1); 27 | x2 = point.x + pASR(size,1); 28 | 29 | y1 = point.y - pASR(size,1); 30 | y2 = point.y + pASR(size,1); 31 | 32 | z1 = point.z - pASR(size,1); 33 | z2 = point.z + pASR(size,1); 34 | } 35 | 36 | explicit constexpr AABB(T xmin, T xmax, T ymin, T ymax, T zmin, T zmax) 37 | { 38 | x1 = xmin, x2 = xmax, y1 = ymin, y2 = ymax, z1 = zmin, z2 = zmax; 39 | } 40 | 41 | constexpr void AddPoint(const V3& point) 42 | { 43 | x1 = pMin(x1, point.x); 44 | x2 = pMax(x2, point.x); 45 | 46 | y1 = pMin(y1, point.y); 47 | y2 = pMax(y2, point.y); 48 | 49 | z1 = pMin(z1, point.z); 50 | z2 = pMax(z2, point.z); 51 | } 52 | 53 | constexpr void AddTriangle(const V3& v1, const V3& v2, const V3& v3) 54 | { 55 | AddPoint(v1); 56 | AddPoint(v2); 57 | AddPoint(v3); 58 | } 59 | 60 | constexpr void AddAABB(const AABB& other) 61 | { 62 | x1 = pMin(x1, other.x1); 63 | x2 = pMax(x2, other.x2); 64 | 65 | y1 = pMin(y1, other.y1); 66 | y2 = pMax(y2, other.y2); 67 | 68 | z1 = pMin(z1, other.z1); 69 | z2 = pMax(z2, other.z2); 70 | } 71 | 72 | constexpr bool Intersect(const V3& point) const 73 | { 74 | if(x1 > point.x) 75 | return false; 76 | 77 | if(x2 < point.x) 78 | return false; 79 | 80 | if(y1 > point.y) 81 | return false; 82 | 83 | if(y2 < point.y) 84 | return false; 85 | 86 | if(z1 > point.z) 87 | return false; 88 | 89 | if(z2 < point.z) 90 | return false; 91 | 92 | return true; 93 | } 94 | 95 | constexpr bool Intersect(const AABB& other) const 96 | { 97 | // X-axis 98 | if (x1 > other.x2 || x2 < other.x1) 99 | return false; 100 | 101 | // Z-axis 102 | if (z1 > other.z2 || z2 < other.z1) 103 | return false; 104 | 105 | // Y-axis 106 | if (y1 > other.y2 || y2 < other.y1) 107 | return false; 108 | 109 | return true; 110 | } 111 | 112 | private: 113 | 114 | T x1; 115 | T x2; 116 | 117 | T y1; 118 | T y2; 119 | 120 | T z1; 121 | T z2; 122 | }; 123 | 124 | } 125 | 126 | #endif // AABB_H 127 | -------------------------------------------------------------------------------- /Potato3dGBAExample/source/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "../../potato3d.h" 6 | 7 | #include "model.h" 8 | 9 | #define DCNT_PAGE 0x0010 10 | 11 | #define VID_PAGE1 VRAM 12 | #define VID_PAGE2 0x600A000 13 | 14 | #define TM_FREQ_1024 0x0003 15 | #define TM_ENABLE 0x0080 16 | #define TM_CASCADE 0x0004 17 | #define TM_FREQ_1024 0x0003 18 | #define TM_FREQ_256 0x0002 19 | 20 | #define REG_WAITCNT *((vu16 *)(0x4000204)) 21 | 22 | unsigned char* I_GetBackBuffer() 23 | { 24 | if(REG_DISPCNT & DCNT_PAGE) 25 | return (unsigned char*)VID_PAGE1; 26 | 27 | return (unsigned char*)VID_PAGE2; 28 | } 29 | 30 | void I_FinishUpdate_e32() 31 | { 32 | REG_DISPCNT ^= DCNT_PAGE; 33 | } 34 | 35 | P3D::Object3d* obj3d = nullptr; 36 | 37 | void PollKeys() 38 | { 39 | scanKeys(); 40 | 41 | u16 key_down = keysHeld(); 42 | 43 | if(key_down) 44 | { 45 | if(key_down & KEY_LEFT) 46 | { 47 | obj3d->CameraAngle().y += 4; 48 | } 49 | 50 | if(key_down & KEY_RIGHT) 51 | { 52 | obj3d->CameraAngle().y -= 4; 53 | } 54 | 55 | if(key_down & KEY_UP) 56 | { 57 | P3D::V3 camAngle = obj3d->CameraAngle(); 58 | 59 | P3D::fp angleYRad = P3D::pD2R(camAngle.y); 60 | 61 | P3D::V3 d(-(std::sin(angleYRad.f()) *25), 0, -(std::cos(angleYRad.f()) *25)); 62 | 63 | obj3d->CameraPos() += d; 64 | } 65 | 66 | if(key_down & KEY_DOWN) 67 | { 68 | P3D::V3 camAngle = obj3d->CameraAngle(); 69 | 70 | P3D::fp angleYRad = P3D::pD2R(camAngle.y); 71 | 72 | P3D::V3 d(-(std::sin(angleYRad.f()) *25), 0, -(std::cos(angleYRad.f()) *25)); 73 | 74 | obj3d->CameraPos() -= d; 75 | } 76 | 77 | if(key_down & KEY_L) 78 | { 79 | obj3d->CameraPos().y -= 10; 80 | } 81 | 82 | if(key_down & KEY_R) 83 | { 84 | obj3d->CameraPos().y += 10; 85 | } 86 | } 87 | } 88 | 89 | int main() 90 | { 91 | irqInit(); 92 | 93 | //Set gamepak wait states and prefetch. 94 | REG_WAITCNT = 0x46DA; 95 | 96 | SetMode(MODE_4 | BG2_ENABLE | BIT(5)); 97 | 98 | obj3d = new P3D::Object3d(); 99 | 100 | obj3d->Setup(240, 160, 60, 25, 2500, I_GetBackBuffer()); 101 | 102 | 103 | const P3D::BspModel* runway = (const P3D::BspModel*)modeldata; 104 | obj3d->SetModel(runway); 105 | 106 | unsigned short* pal_ram = (unsigned short*)0x5000000; 107 | 108 | for(int i = 0; i< 256; i++) 109 | { 110 | unsigned int c = runway->GetColorMapColor(i); 111 | 112 | unsigned int r = (c >> 16) & 0xff; 113 | unsigned int g = (c >> 8) & 0xff; 114 | unsigned int b = (c) & 0xff; 115 | 116 | pal_ram[i] = RGB8(r, g, b); 117 | } 118 | 119 | 120 | obj3d->SetBackgroundColor(10); 121 | 122 | while(true) 123 | { 124 | PollKeys(); 125 | 126 | obj3d->RenderScene(); 127 | I_FinishUpdate_e32(); 128 | 129 | obj3d->SetFrameBuffer(I_GetBackBuffer()); 130 | } 131 | 132 | } 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /bspmodel.cpp: -------------------------------------------------------------------------------- 1 | #include "Config.h" 2 | #include 3 | #include "bspmodel.h" 4 | 5 | namespace P3D 6 | { 7 | Stack BspModel::stack; 8 | List BspModel::node_list; 9 | 10 | void BspModel::Sort(const V3& p, const AABB& frustrum, std::vector &out, bool backface_cull) const 11 | { 12 | out.clear(); 13 | node_list.Clear(); 14 | 15 | SortBackToFront(p, frustrum); 16 | OutputTris(frustrum, out, backface_cull); 17 | } 18 | 19 | constexpr unsigned int BACK_BIT = 1 << 31; 20 | constexpr unsigned int POST_BIT = 1 << 30; 21 | 22 | constexpr unsigned int NODE_MASK = ~(BACK_BIT | POST_BIT); 23 | 24 | void BspModel::SortBackToFront(const V3& p, const AABB& frustrum) const 25 | { 26 | stack.Push(0); 27 | 28 | while(!stack.Empty()) 29 | { 30 | const unsigned int item = stack.Pop(); 31 | 32 | const BspModelNode* n = GetNode(item & NODE_MASK); 33 | 34 | if (!frustrum.Intersect(n->child_bb)) 35 | continue; 36 | 37 | if (item & POST_BIT) 38 | { 39 | if (frustrum.Intersect(n->node_bb)) 40 | { 41 | if (item & BACK_BIT) 42 | node_list.Add(item & NODE_MASK); 43 | else 44 | node_list.Add((item & NODE_MASK) | BACK_BIT); 45 | } 46 | } 47 | else 48 | { 49 | if(Distance(n->plane, p) >= 0) 50 | { 51 | if (n->front_node) 52 | stack.Push(n->front_node); 53 | 54 | stack.Push(item | POST_BIT); 55 | 56 | if (n->back_node) 57 | stack.Push(n->back_node); 58 | } 59 | else 60 | { 61 | if (n->back_node) 62 | stack.Push(n->back_node); 63 | 64 | stack.Push(item | POST_BIT | BACK_BIT); 65 | 66 | if (n->front_node) 67 | stack.Push(n->front_node); 68 | } 69 | } 70 | } 71 | } 72 | 73 | void BspModel::OutputTris(const AABB& frustrum, std::vector &out, bool backface_cull) const 74 | { 75 | for(unsigned int i = 0; i < node_list.Size(); i++) 76 | { 77 | const unsigned int node = node_list.At(i); 78 | 79 | const BspModelNode* n = GetNode(node & NODE_MASK); 80 | 81 | const TriIndexList* front = (node & BACK_BIT) ? &n->back_tris : &n->front_tris; 82 | 83 | for(unsigned int i = 0; i < front->count; i++) 84 | { 85 | const BspModelTriangle* tri = GetTriangle(front->offset + i); 86 | 87 | if(frustrum.Intersect(tri->tri_bb)) 88 | out.push_back(tri); 89 | } 90 | 91 | if(!backface_cull) 92 | { 93 | const TriIndexList* back = (node & BACK_BIT) ? &n->front_tris : &n->back_tris; 94 | 95 | for(unsigned int i = 0; i < back->count; i++) 96 | { 97 | const BspModelTriangle* tri = GetTriangle(back->offset + i); 98 | 99 | if(frustrum.Intersect(tri->tri_bb)) 100 | out.push_back(tri); 101 | } 102 | } 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /bspmodel.h: -------------------------------------------------------------------------------- 1 | #ifndef BSPMODEL_H 2 | #define BSPMODEL_H 3 | 4 | #include 5 | #include 6 | #include "BspModelDefs.h" 7 | 8 | namespace P3D 9 | { 10 | //Bare minimum monday stack. 11 | template class Stack 12 | { 13 | public: 14 | Stack(unsigned int size = 2048) { pos = mem = (T*)new char[sizeof(T) * size]; } 15 | ~Stack() { delete[] mem; } 16 | void Push(T item) { *pos = item; pos++; } 17 | T Pop() { pos--; return *pos; } 18 | bool Empty() const { return pos == mem; } 19 | void Clear() { pos = mem; } 20 | 21 | private: 22 | T* mem; T* pos; 23 | }; 24 | 25 | //Fuck it friday list. 26 | template class List 27 | { 28 | public: 29 | List(unsigned int size = 2048) { pos = mem = (T*)new char[sizeof(T) * size]; } 30 | ~List() { delete[] mem; } 31 | void Add(T item) { *pos = item; pos++; } 32 | T At(unsigned int index) const { return mem[index]; } 33 | unsigned int Size() const { return pos - mem; } 34 | void Clear() { pos = mem; } 35 | 36 | private: 37 | T* mem; T* pos; 38 | }; 39 | 40 | 41 | class BspModel 42 | { 43 | public: 44 | BspModelHeader header; 45 | 46 | void Sort(const V3& p, const AABB& frustrum, std::vector &out, bool backface_cull) const; 47 | 48 | const BspNodeTexture* GetTexture(int n) const 49 | { 50 | if(n == -1) 51 | return nullptr; 52 | 53 | return &((const BspNodeTexture*)(GetBasePtr() + header.texture_offset))[n]; 54 | } 55 | 56 | const pixel* GetTexturePixels(unsigned int n) const 57 | { 58 | return &((const pixel*)(GetBasePtr() + header.texture_pixels_offset))[n]; 59 | } 60 | 61 | unsigned int GetColorMapColor(unsigned int n) const 62 | { 63 | return GetColorMap()[n]; 64 | } 65 | 66 | const unsigned int* GetColorMap() const 67 | { 68 | return ((const unsigned int*)(GetBasePtr() + header.texture_palette_offset)); 69 | } 70 | 71 | const unsigned char* GetFogLightMap() const 72 | { 73 | return ((const unsigned char*)(GetBasePtr() + header.fog_lightmap_offset)); 74 | } 75 | 76 | private: 77 | 78 | static Stack stack; 79 | static List node_list; 80 | 81 | void OutputTris(const AABB &frustrum, std::vector &out, bool backface_cull) const; 82 | void SortBackToFront(const V3 &p, const AABB& frustrum) const; 83 | 84 | const unsigned char* GetBasePtr() const 85 | { 86 | return (const unsigned char*)&header; 87 | } 88 | 89 | fp Distance(const Plane& plane, const V3& pos) const 90 | { 91 | fp dot = plane.Normal().DotProduct(pos); 92 | 93 | return dot - plane.Distance(); 94 | } 95 | 96 | const BspModelTriangle* GetTriangle(unsigned int n) const 97 | { 98 | return &((const BspModelTriangle*)(GetBasePtr() + header.triangle_offset))[n]; 99 | } 100 | 101 | const BspModelNode* GetNode(unsigned int n) const 102 | { 103 | return &((const BspModelNode*)(GetBasePtr() + header.node_offset))[n]; 104 | } 105 | 106 | unsigned int NodeIndex(const BspModelNode* node) const 107 | { 108 | return node - GetNode(0); 109 | } 110 | }; 111 | } 112 | #endif // BSPMODEL_H 113 | -------------------------------------------------------------------------------- /RenderTarget.h: -------------------------------------------------------------------------------- 1 | #ifndef RENDERTARGET_H 2 | #define RENDERTARGET_H 3 | 4 | #include "Config.h" 5 | 6 | namespace P3D 7 | { 8 | class RenderTarget 9 | { 10 | public: 11 | explicit RenderTarget(unsigned int width, unsigned int height, pixel* buffer = nullptr, unsigned int y_pitch = 0) 12 | { 13 | AttachColorBuffer(width, height, buffer, y_pitch); 14 | } 15 | 16 | RenderTarget(const RenderTarget&) = delete; 17 | RenderTarget& operator=(const RenderTarget&) = delete; 18 | RenderTarget(RenderTarget&&) = delete; 19 | RenderTarget& operator=(RenderTarget&&) = delete; 20 | 21 | ~RenderTarget() 22 | { 23 | RemoveColorBuffer(); 24 | RemoveZBuffer(); 25 | } 26 | 27 | bool AttachColorBuffer(unsigned int width, unsigned int height, pixel* buffer = nullptr, unsigned int y_pitch = 0) 28 | { 29 | RemoveColorBuffer(); 30 | 31 | if(!width || !height) 32 | return false; 33 | 34 | if(y_pitch == 0) 35 | y_pitch = width; 36 | 37 | if(y_pitch < width) 38 | return false; 39 | 40 | color_buffer_y_pitch = y_pitch; 41 | this->width = width; 42 | this->height = height; 43 | 44 | if(buffer) 45 | { 46 | color_buffer = buffer; 47 | } 48 | else 49 | { 50 | color_buffer = new pixel[width * height]; 51 | owned_color_buffer = true; 52 | } 53 | 54 | return true; 55 | } 56 | 57 | bool AttachZBuffer(z_val* buffer = nullptr, unsigned int y_pitch = 0) 58 | { 59 | RemoveZBuffer(); 60 | 61 | if(!width || !height) 62 | return false; 63 | 64 | if(y_pitch == 0) 65 | y_pitch = width; 66 | 67 | if(y_pitch < width) 68 | return false; 69 | 70 | if(buffer) 71 | { 72 | z_buffer = buffer; 73 | z_buffer_y_pitch = y_pitch; 74 | } 75 | else 76 | { 77 | z_buffer = new z_val[width * height]; 78 | z_buffer_y_pitch = width; 79 | owned_z_buffer = true; 80 | } 81 | 82 | return true; 83 | } 84 | 85 | void RemoveZBuffer() 86 | { 87 | if(owned_z_buffer) 88 | delete[] z_buffer; 89 | 90 | z_buffer = nullptr; 91 | z_buffer_y_pitch = 0; 92 | } 93 | 94 | unsigned int GetWidth() const {return width;} 95 | unsigned int GetHeight() const {return height;} 96 | unsigned int GetColorBufferYPitch() const {return color_buffer_y_pitch;} 97 | pixel* GetColorBuffer() const {return color_buffer;} 98 | 99 | z_val* GetZBuffer() const {return z_buffer;} 100 | unsigned int GetZBufferYPitch() const {return z_buffer_y_pitch;} 101 | private: 102 | 103 | void RemoveColorBuffer() 104 | { 105 | if(owned_color_buffer) 106 | delete[] color_buffer; 107 | 108 | color_buffer = nullptr; 109 | owned_color_buffer = false; 110 | } 111 | 112 | pixel* color_buffer = nullptr; 113 | unsigned int width = 0; 114 | unsigned int height = 0; 115 | unsigned int color_buffer_y_pitch = 0; 116 | 117 | z_val* z_buffer = nullptr; 118 | unsigned int z_buffer_y_pitch = 0; 119 | 120 | bool owned_color_buffer = false; 121 | bool owned_z_buffer = false; 122 | }; 123 | }; 124 | #endif // RENDERTARGET_H 125 | -------------------------------------------------------------------------------- /3dmaths/v3.h: -------------------------------------------------------------------------------- 1 | #ifndef V3_H 2 | #define V3_H 3 | 4 | #include 5 | #include "fp.h" 6 | 7 | namespace P3D 8 | { 9 | 10 | template class V3 11 | { 12 | public: 13 | T x; 14 | T y; 15 | T z; 16 | 17 | V3(T x, T y, T z) : x(x), y(y), z(z) {} 18 | V3() {} 19 | ~V3() {} 20 | 21 | constexpr bool operator==(const V3& r) 22 | { 23 | return ((x == r.x) && (y == r.y) && (z == r.z)); 24 | } 25 | 26 | constexpr V3 operator+(const V3& r) const 27 | { 28 | V3 v(x,y,z); 29 | return v+=r; 30 | } 31 | 32 | constexpr V3 operator-(const V3& r) const 33 | { 34 | V3 v(x,y,z); 35 | return v-=r; 36 | } 37 | 38 | constexpr V3 operator*(const V3& r) const 39 | { 40 | V3 v(x,y,z); 41 | return v*=r; 42 | } 43 | 44 | constexpr V3 operator*(const T& r) const 45 | { 46 | V3 v(x,y,z); 47 | return v*=r; 48 | } 49 | 50 | V3& operator+=(const V3& r) 51 | { 52 | x += r.x; 53 | y += r.y; 54 | z += r.z; 55 | 56 | return *this; 57 | } 58 | 59 | V3& operator-=(const V3& r) 60 | { 61 | x -= r.x; 62 | y -= r.y; 63 | z -= r.z; 64 | 65 | return *this; 66 | } 67 | 68 | V3& operator*=(const V3& r) 69 | { 70 | x *= r.x; 71 | y *= r.y; 72 | z *= r.z; 73 | 74 | return *this; 75 | } 76 | 77 | V3& operator*=(const T& r) 78 | { 79 | x *= r; 80 | y *= r; 81 | z *= r; 82 | 83 | return *this; 84 | } 85 | 86 | V3 CrossProduct(const V3& r) const 87 | { 88 | V3 v; 89 | 90 | v.x = (float)(((double)y * (double)r.z) - ((double)z * (double)r.y)); 91 | v.y = (float)(((double)z * (double)r.x) - ((double)x * (double)r.z)); 92 | v.z = (float)(((double)x * (double)r.y) - ((double)y * (double)r.x)); 93 | 94 | return v; 95 | } 96 | 97 | V3 CrossProductNormalised(const V3& r) const 98 | { 99 | V3 v; 100 | 101 | double xd = ((double)y * (double)r.z) - ((double)z * (double)r.y); 102 | double yd = ((double)z * (double)r.x) - ((double)x * (double)r.z); 103 | double zd = ((double)x * (double)r.y) - ((double)y * (double)r.x); 104 | 105 | double len = xd * xd + 106 | yd * yd + 107 | zd * zd; 108 | 109 | len = std::sqrt(len); 110 | 111 | v.x = (float)(xd / len); 112 | v.y = (float)(yd / len); 113 | v.z = (float)(zd / len); 114 | 115 | return v; 116 | } 117 | 118 | constexpr V3 Normalised() const 119 | { 120 | V3 v; 121 | 122 | T len = Length(); 123 | 124 | v.x = (float)((float)x / len); 125 | v.y = (float)((float)y / len); 126 | v.z = (float)((float)z / len); 127 | 128 | return v; 129 | } 130 | 131 | constexpr T CrossProductZ(const V3& r) const 132 | { 133 | return ((x * r.y) - (y * r.x)); 134 | } 135 | 136 | constexpr T DotProduct(const V3& v2) const 137 | { 138 | return x * v2.x + y * v2.y + z * v2.z; 139 | } 140 | 141 | constexpr T Length() const 142 | { 143 | return std::sqrt((x * x) + (y * y) + (z * z)); 144 | } 145 | }; 146 | 147 | typedef V3 V3F; 148 | typedef V3 V3D; 149 | typedef V3 V3FP; 150 | 151 | } 152 | 153 | #endif // V3_H 154 | -------------------------------------------------------------------------------- /Potato3dExample2/source/videosystem.cpp: -------------------------------------------------------------------------------- 1 | #include "../include/videosystem.h" 2 | 3 | #ifdef GBA 4 | #include 5 | #include 6 | #include 7 | 8 | #define DCNT_PAGE 0x0010 9 | #define VID_PAGE1 VRAM 10 | #define VID_PAGE2 0x600A000 11 | 12 | #define TM_FREQ_1024 0x0003 13 | #define TM_ENABLE 0x0080 14 | #define TM_CASCADE 0x0004 15 | #define TM_FREQ_1024 0x0003 16 | #define TM_FREQ_256 0x0002 17 | 18 | #define REG_WAITCNT *((vu16 *)(0x4000204)) 19 | #endif 20 | 21 | VideoSystem::VideoSystem() 22 | { 23 | #ifndef GBA 24 | timer.start(); 25 | #endif 26 | 27 | } 28 | 29 | void VideoSystem::Setup(unsigned short* keyState) 30 | { 31 | #ifdef GBA 32 | SetMode(MODE_4 | BG2_ENABLE | BIT(5)); 33 | 34 | REG_TM2CNT_L= 65535-66; // 66 = 1ms 35 | REG_TM2CNT_H = TM_FREQ_256 | TM_ENABLE; // we're using the 256 cycle timer 36 | 37 | // cascade into tm3 38 | REG_TM3CNT_H = TM_CASCADE | TM_ENABLE; 39 | 40 | buffers[0] = new P3D::RenderTarget(width, height, (P3D::pixel*)VID_PAGE1); 41 | buffers[1] = new P3D::RenderTarget(width, height, (P3D::pixel*)VID_PAGE2); 42 | 43 | this->keyState = keyState; 44 | #else 45 | image[0] = new QImage(width, height, QImage::Format_Indexed8); 46 | image[1] = new QImage(width, height, QImage::Format_Indexed8); 47 | 48 | image[0]->setColorCount(256); 49 | image[1]->setColorCount(256); 50 | 51 | buffers[0] = new P3D::RenderTarget(width, height, image[0]->scanLine(0)); 52 | buffers[1] = new P3D::RenderTarget(width, height, image[1]->scanLine(0)); 53 | 54 | int z = 0; 55 | application = new QApplication (z, nullptr); 56 | 57 | window = new GameWindow(keyState); 58 | window->SetBackbuffer(image[0]); 59 | 60 | window->setAttribute(Qt::WA_PaintOnScreen); 61 | window->resize(width * 2, height * 2); 62 | window->show(); 63 | #endif 64 | } 65 | 66 | const P3D::RenderTarget* VideoSystem::GetBackBuffer() 67 | { 68 | return buffers[currentBuffer]; 69 | } 70 | 71 | void VideoSystem::PageFlip() 72 | { 73 | #ifdef GBA 74 | REG_DISPCNT ^= DCNT_PAGE; 75 | #else 76 | window->SetBackbuffer(image[currentBuffer]); 77 | window->repaint(); 78 | application->processEvents(); 79 | #endif 80 | 81 | currentBuffer = 1 - currentBuffer; 82 | } 83 | 84 | void VideoSystem::SetPalette(const unsigned int pal[256]) 85 | { 86 | #ifdef GBA 87 | 88 | unsigned short* pal_ram = (unsigned short*)0x5000000; 89 | 90 | for(int i = 0; i< 256; i++) 91 | { 92 | unsigned int r = (pal[i] >> 16) & 0xff; 93 | unsigned int g = (pal[i] >> 8) & 0xff; 94 | unsigned int b = (pal[i]) & 0xff; 95 | 96 | pal_ram[i] = RGB8(r, g, b); 97 | } 98 | 99 | #else 100 | 101 | QList colorMap; 102 | 103 | for(int i = 0; i< 256; i++) 104 | { 105 | colorMap.append(QRgb(pal[i])); 106 | } 107 | 108 | image[0]->setColorTable(colorMap); 109 | image[1]->setColorTable(colorMap); 110 | 111 | #endif 112 | } 113 | 114 | void VideoSystem::UpdateKeys() 115 | { 116 | #ifdef GBA 117 | scanKeys(); 118 | 119 | u16 key = keysDown(); 120 | 121 | if(key & KEY_UP) 122 | *keyState |= KeyUp; 123 | else if(key & KEY_DOWN) 124 | *keyState |= KeyDown; 125 | 126 | if(key & KEY_LEFT) 127 | *keyState |= KeyLeft; 128 | else if(key & KEY_RIGHT) 129 | *keyState |= KeyRight; 130 | 131 | key = keysUp(); 132 | 133 | if(key & KEY_UP) 134 | *keyState &= ~KeyUp; 135 | else if(key & KEY_DOWN) 136 | *keyState &= ~KeyDown; 137 | 138 | if(key & KEY_LEFT) 139 | *keyState &= ~KeyLeft; 140 | else if(key & KEY_RIGHT) 141 | *keyState &= ~KeyRight; 142 | 143 | #endif 144 | } 145 | 146 | unsigned int VideoSystem::GetTime() 147 | { 148 | #ifndef GBA 149 | return timer.elapsed(); 150 | #else 151 | static unsigned int lastVal = 0; 152 | static unsigned int highPart = 0; 153 | 154 | unsigned int currentValue = *((volatile unsigned short*)(0x400010C)); 155 | 156 | if(currentValue < lastVal) 157 | highPart++; 158 | 159 | lastVal = currentValue; 160 | 161 | return highPart << 16 | currentValue; 162 | #endif 163 | } 164 | -------------------------------------------------------------------------------- /RenderCommon.h: -------------------------------------------------------------------------------- 1 | #ifndef RENDERCOMMON_H 2 | #define RENDERCOMMON_H 3 | 4 | #include "Config.h" 5 | 6 | namespace P3D 7 | { 8 | enum RenderFlags : unsigned int 9 | { 10 | NoFlags = 0ul, 11 | ZTest = 1ul, 12 | ZWrite = 2ul, 13 | ZBuffer = ZTest | ZWrite, 14 | AlphaTest = 4ul, 15 | FullPerspectiveMapping = 16ul, 16 | SubdividePerspectiveMapping = 32ul, 17 | BackFaceCulling = 64ul, 18 | FrontFaceCulling = 128ul, 19 | Fog = 512ul, 20 | VertexLight = 1024ul, 21 | }; 22 | 23 | typedef enum FogMode : unsigned int 24 | { 25 | FogLinear = 0u, //Linear fog 26 | FogExponential = 1u, //Exponential Fog 27 | FogExponential2 = 2u, //Exponential Squared Fog 28 | } FogMode; 29 | 30 | class Material 31 | { 32 | public: 33 | 34 | Material() {color = 0;}; 35 | 36 | enum MaterialType : unsigned int 37 | { 38 | Color = 0u, 39 | Texture = 1u 40 | }; 41 | 42 | MaterialType type = Color; 43 | 44 | union 45 | { 46 | struct 47 | { 48 | const pixel* pixels; 49 | bool alpha; //If pixel == 0. Don't draw. 50 | }; 51 | struct 52 | { 53 | pixel color; 54 | }; 55 | }; 56 | 57 | static constexpr unsigned int width = TEX_SIZE; 58 | static constexpr unsigned int height = TEX_SIZE; 59 | }; 60 | 61 | class RenderStats 62 | { 63 | public: 64 | unsigned int vertex_transformed; 65 | unsigned int triangles_submitted; 66 | unsigned int triangles_drawn; 67 | unsigned int scanlines_drawn; 68 | unsigned int span_checks; 69 | unsigned int span_count; 70 | unsigned int triangles_clipped; 71 | 72 | void ResetToZero() 73 | { 74 | vertex_transformed = 0; 75 | triangles_submitted = 0; 76 | triangles_drawn = 0; 77 | scanlines_drawn = 0; 78 | span_checks = 0; 79 | span_count = 0; 80 | triangles_clipped = 0; 81 | } 82 | }; 83 | 84 | namespace Internal 85 | { 86 | class RenderTargetViewport 87 | { 88 | public: 89 | pixel* start = nullptr; 90 | unsigned int width = 0; 91 | unsigned int height = 0; 92 | unsigned int y_pitch = 0; 93 | 94 | z_val* z_start = nullptr; 95 | unsigned int z_y_pitch; 96 | }; 97 | 98 | class RenderDeviceNearFarPlanes 99 | { 100 | public: 101 | fp z_near = 0; 102 | fp z_far = 0; 103 | 104 | fp z_ratio_1; // (far) / (far - near) 105 | fp z_ratio_2; // (-far * near) / (far - near) 106 | fp z_ratio_3; // (1/(far-near)) 107 | }; 108 | 109 | class RenderDeviceFogParameters 110 | { 111 | public: 112 | 113 | RenderDeviceFogParameters() {} 114 | 115 | fp fog_start; 116 | fp fog_end; 117 | fp fog_density; 118 | 119 | FogMode mode = FogLinear; 120 | pixel fog_color = 0; 121 | }; 122 | 123 | typedef enum ClipPlane : unsigned int 124 | { 125 | NoClip = 0u, 126 | W_Near = 1u, 127 | X_W_Left = 2u, 128 | X_W_Right = 4u, 129 | Y_W_Top = 8u, 130 | Y_W_Bottom = 16u, 131 | W_Far = 32u, 132 | 133 | } ClipPlane; 134 | 135 | typedef enum ClipOperation : unsigned int 136 | { 137 | Accept = 0u, //No clip required. 138 | Clip = 1u, //Clip required. 139 | Reject = 2u //All out. Reject polygon. 140 | } ClipOperation; 141 | 142 | class Vertex4d 143 | { 144 | public: 145 | V4 pos; 146 | V2 uv; 147 | fp fog_factor; 148 | fp light_factor; 149 | 150 | void toPerspectiveCorrect(const fp scale = fp(1)) 151 | { 152 | pos.w = fp(scale) / pos.w; 153 | uv.x = uv.x * pos.w; 154 | uv.y = uv.y * pos.w; 155 | } 156 | }; 157 | 158 | class TransformedTriangle 159 | { 160 | public: 161 | Vertex4d verts[8]; 162 | }; 163 | }; 164 | }; 165 | 166 | #endif // RENDERCOMMON_H 167 | -------------------------------------------------------------------------------- /PixelShaderGBA8.h: -------------------------------------------------------------------------------- 1 | #ifndef PIXELSHADERGBA8_H 2 | #define PIXELSHADERGBA8_H 3 | 4 | #include "RenderCommon.h" 5 | 6 | namespace P3D 7 | { 8 | template class PixelShaderGBA8 9 | { 10 | public: 11 | 12 | static void DrawScanlinePixelPair(pixel* fb, z_val *zb, const z_val zv1, const z_val zv2, const pixel* texels, const fp u1, const fp v1, const fp u2, const fp v2, const fp f1, const fp f2, const fp l1, const fp l2, const pixel fog_color, const unsigned char* fog_light_map = nullptr) 13 | { 14 | if constexpr (render_flags & ZTest) 15 | { 16 | if(zv1 >= zb[0]) //Reject left? 17 | { 18 | if(zv2 >= zb[1]) //Reject right? 19 | return; //Both Z Reject. 20 | 21 | //Accept right. 22 | DrawScanlinePixelHigh(fb+1, zb+1, zv2, texels, u2, v2, f2, l2, fog_color); 23 | return; 24 | } 25 | else //Accept left. 26 | { 27 | if(zv2 >= zb[1]) //Reject right? 28 | { 29 | DrawScanlinePixelLow(fb, zb, zv1, texels, u1, v1, f1, l1, fog_color); 30 | return; 31 | } 32 | } 33 | } 34 | 35 | const unsigned int tx = (int)u1 & TEX_MASK; 36 | const unsigned int ty = ((int)v1 & TEX_MASK) << TEX_SHIFT; 37 | 38 | const unsigned int tx2 = (int)u2 & TEX_MASK; 39 | const unsigned int ty2 = ((int)v2 & TEX_MASK) << TEX_SHIFT; 40 | 41 | pixel p1 = texels[(ty + tx)], p2 = texels[(ty2 + tx2)]; 42 | 43 | if constexpr(render_flags & (Fog | VertexLight)) 44 | { 45 | p1 = FogLightPixel(p1, f1, l1, fog_light_map); 46 | p2 = FogLightPixel(p2, f2, l2, fog_light_map); 47 | } 48 | 49 | *(unsigned short*)fb = (p1 | (p2 << 8)); 50 | 51 | if constexpr (render_flags & ZWrite) 52 | { 53 | zb[0] = zv1, zb[1] = zv2; 54 | } 55 | } 56 | 57 | static void DrawScanlinePixelHigh(pixel *fb, z_val *zb, const z_val zv, const pixel* texels, const fp u, const fp v, const fp f, const fp l, const pixel, const unsigned char* fog_light_map = nullptr) 58 | { 59 | if constexpr (render_flags & ZTest) 60 | { 61 | if(*zb <= zv) 62 | return; 63 | } 64 | 65 | if constexpr (render_flags & ZWrite) 66 | { 67 | *zb = zv; 68 | } 69 | 70 | const unsigned int tx = (int)u & TEX_MASK; 71 | const unsigned int ty = ((int)v & TEX_MASK) << TEX_SHIFT; 72 | 73 | pixel p1 = texels[(ty + tx)]; 74 | 75 | if constexpr(render_flags & (Fog | VertexLight)) 76 | { 77 | p1 = FogLightPixel(p1, f, l, fog_light_map); 78 | } 79 | 80 | unsigned short* p16 = (unsigned short*)(fb-1); 81 | const pixel* p8 = (pixel*)p16; 82 | 83 | const unsigned short texel = (p1 << 8) | *p8; 84 | 85 | *p16 = texel; 86 | } 87 | 88 | static void DrawScanlinePixelLow(pixel *fb, z_val *zb, const z_val zv, const pixel* texels, const fp u, const fp v, const fp f, const fp l, const pixel, const unsigned char* fog_light_map = nullptr) 89 | { 90 | if constexpr (render_flags & ZTest) 91 | { 92 | if(*zb <= zv) 93 | return; 94 | } 95 | 96 | if constexpr (render_flags & ZWrite) 97 | { 98 | *zb = zv; 99 | } 100 | 101 | const unsigned int tx = (int)u & TEX_MASK; 102 | const unsigned int ty = ((int)v & TEX_MASK) << TEX_SHIFT; 103 | 104 | pixel p1 = texels[(ty + tx)]; 105 | 106 | if constexpr(render_flags & (Fog | VertexLight)) 107 | { 108 | p1 = FogLightPixel(p1, f, l, fog_light_map); 109 | } 110 | 111 | unsigned short* p16 = (unsigned short*)(fb); 112 | const pixel* p8 = (pixel*)p16; 113 | 114 | const unsigned short texel = p1 | (p8[1] << 8); 115 | 116 | *p16 = texel; 117 | } 118 | 119 | static constexpr pixel FogLightPixel(pixel src_color, fp fog_frac, fp light_frac, const unsigned char* fog_light_map) 120 | { 121 | unsigned int light = 0, fog = 0; 122 | 123 | if constexpr(render_flags & VertexLight) 124 | { 125 | light = pASL(pClamp(fp(0), light_frac, LIGHT_MAX), LIGHT_SHIFT); 126 | } 127 | 128 | if constexpr(render_flags & Fog) 129 | { 130 | fog = pASL(pClamp(fp(0), fog_frac, FOG_MAX), FOG_SHIFT); 131 | } 132 | 133 | const unsigned int texel = src_color; 134 | 135 | return fog_light_map[FogLightIndex(texel, fog, light)]; 136 | } 137 | 138 | static constexpr unsigned int FogLightIndex(const unsigned int color, const unsigned int fog, const unsigned int light) 139 | { 140 | return (light * (FOG_LEVELS * 256)) + (fog * 256) + color; 141 | } 142 | }; 143 | }; 144 | #endif // PIXELSHADERGBA8_H 145 | -------------------------------------------------------------------------------- /PixelShaderDefault.h: -------------------------------------------------------------------------------- 1 | #ifndef PIXELSHADERDEFAULT_H 2 | #define PIXELSHADERDEFAULT_H 3 | 4 | #include "RenderCommon.h" 5 | #include "RenderTriangle.h" 6 | 7 | namespace P3D 8 | { 9 | using pixel_pair = double_width_t; 10 | 11 | template class PixelShaderDefault 12 | { 13 | public: 14 | 15 | static void DrawScanlinePixelPair(pixel* fb, z_val *zb, const z_val zv1, const z_val zv2, const pixel* texels, const fp u1, const fp v1, const fp u2, const fp v2, const fp f1, const fp f2, const fp l1, const fp l2, const pixel fog_color, const unsigned char* fog_light_map = nullptr) 16 | { 17 | if constexpr (render_flags & ZTest) 18 | { 19 | if(zv1 >= zb[0]) //Reject left? 20 | { 21 | if(zv2 >= zb[1]) //Reject right? 22 | return; //Both Z Reject. 23 | 24 | //Accept right. 25 | DrawScanlinePixel(fb+1, zb+1, zv2, texels, u2, v2, f2, l2, fog_color, fog_light_map); 26 | return; 27 | } 28 | else //Accept left. 29 | { 30 | if(zv2 >= zb[1]) //Reject right? 31 | { 32 | DrawScanlinePixel(fb, zb, zv1, texels, u1, v1, f1, l1, fog_color, fog_light_map); 33 | return; 34 | } 35 | } 36 | } 37 | 38 | if constexpr (render_flags & ZWrite) 39 | { 40 | zb[0] = zv1, zb[1] = zv2; 41 | } 42 | 43 | const unsigned int tx = (unsigned int)u1 & TEX_MASK; 44 | const unsigned int ty = ((unsigned int)v1 & TEX_MASK) << TEX_SHIFT; 45 | 46 | const unsigned int tx2 = (unsigned int)u2 & TEX_MASK; 47 | const unsigned int ty2 = ((unsigned int)v2 & TEX_MASK) << TEX_SHIFT; 48 | 49 | pixel p1 = texels[(ty + tx)], p2 = texels[(ty2 + tx2)]; 50 | 51 | if constexpr(render_flags & (Fog | VertexLight)) 52 | { 53 | p1 = FogLightPixel(p1, f1, l1, fog_color, fog_light_map); 54 | p2 = FogLightPixel(p2, f2, l2, fog_color, fog_light_map); 55 | } 56 | 57 | *(pixel_pair*)fb = ( (p1) | ((pixel_pair)p2 << (sizeof(pixel)*8)) ); 58 | } 59 | 60 | static void DrawScanlinePixel(pixel *fb, z_val *zb, const z_val zv, const pixel* texels, const fp u, const fp v, const fp f, const fp l, const pixel fog_color, const unsigned char* fog_light_map = nullptr) 61 | { 62 | if constexpr (render_flags & ZTest) 63 | { 64 | if(*zb <= zv) 65 | return; 66 | } 67 | 68 | if constexpr (render_flags & ZWrite) 69 | { 70 | *zb = zv; 71 | } 72 | 73 | const unsigned int tx = (int)u & TEX_MASK; 74 | const unsigned int ty = ((int)v & TEX_MASK) << TEX_SHIFT; 75 | 76 | pixel p1 = texels[(ty + tx)]; 77 | 78 | if constexpr(render_flags & (Fog | VertexLight)) 79 | { 80 | p1 = FogLightPixel(p1, f, l, fog_color, fog_light_map); 81 | } 82 | 83 | *fb = p1; 84 | } 85 | 86 | static void DrawScanlinePixelHigh(pixel *fb, z_val *zb, const z_val zv, const pixel* texels, const fp u, const fp v, const fp f, const fp l, const pixel fog_color, const unsigned char* fog_light_map = nullptr) 87 | { 88 | DrawScanlinePixel(fb, zb, zv, texels, u, v, f, l, fog_color, fog_light_map); 89 | } 90 | 91 | static void DrawScanlinePixelLow(pixel *fb, z_val *zb, const z_val zv, const pixel* texels, const fp u, const fp v, const fp f, const fp l, const pixel fog_color, const unsigned char* fog_light_map = nullptr) 92 | { 93 | DrawScanlinePixel(fb, zb, zv, texels, u, v, f, l, fog_color, fog_light_map); 94 | } 95 | 96 | 97 | static constexpr pixel BlendPixel(const pixel src_color, const pixel dst_color, const fp f) 98 | { 99 | if constexpr (render_flags & Fog) 100 | { 101 | if(f == 0) 102 | return src_color; 103 | else if(f == 1) 104 | return dst_color; 105 | 106 | const pixelType src(src_color); 107 | const pixelType dst(dst_color); 108 | 109 | const pixelType out = pixelType( 110 | pLerp(fp(src.R()), fp(dst.R()), f), 111 | pLerp(fp(src.G()), fp(dst.G()), f), 112 | pLerp(fp(src.B()), fp(dst.B()), f) 113 | ); 114 | 115 | return out; 116 | } 117 | else 118 | { 119 | return src_color; 120 | } 121 | } 122 | 123 | static constexpr pixel FogLightPixel(pixel src_color, fp fog_frac, fp light_frac, pixel fog_color, const unsigned char* fog_light_map) 124 | { 125 | if constexpr(sizeof(pixel) == 1) 126 | { 127 | //(color×16×16)+(light×16)+fog 128 | 129 | unsigned int light = 0, fog = 0; 130 | 131 | if constexpr(render_flags & VertexLight) 132 | { 133 | light = pASL(light_frac, LIGHT_SHIFT); 134 | } 135 | 136 | if constexpr(render_flags & Fog) 137 | { 138 | fog = pASL(fog_frac, FOG_SHIFT); 139 | } 140 | 141 | const unsigned int texel = src_color; 142 | 143 | return fog_light_map[pASL(texel, FOG_SHIFT + LIGHT_SHIFT) + pASL(light, LIGHT_SHIFT) + fog]; 144 | } 145 | else 146 | { 147 | return BlendPixel(src_color, fog_color, fog_frac); 148 | } 149 | } 150 | }; 151 | 152 | }; 153 | #endif // PIXELSHADERDEFAULT_H 154 | -------------------------------------------------------------------------------- /Potato3dExample/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | 3 | #include 4 | #include 5 | 6 | const QImage::Format format = QImage::Format_Indexed8; 7 | 8 | 9 | MainWindow::MainWindow(QWidget *parent) 10 | : QMainWindow(parent) 11 | { 12 | 13 | this->resize(960, 640); 14 | this->update(); 15 | 16 | fpsTimer.start(); 17 | 18 | frameBufferImage = QImage(screenWidth, screenHeight, format); 19 | 20 | object3d = new P3D::Object3d(); 21 | 22 | //QFile f("C:\\Users\\Zak\\Documents\\GitProjects\\Potato3d\\Potato3dExample\\models\\Streets\\Streets.bsp"); 23 | //QFile f("C:\\Users\\Zak\\Documents\\GitProjects\\Potato3d\\Potato3dExample\\models\\temple.bsp"); 24 | //QFile f("C:\\Users\\Zak\\Documents\\GitProjects\\Potato3d\\Potato3dExample\\models\\hf\\hf2.bsp"); 25 | //QFile f("C:\\Users\\Zak\\Documents\\GitProjects\\Potato3d\\Potato3dExample\\models\\d2\\driver2_small.bsp"); 26 | //QFile f("C:\\Users\\Zak\\Downloads\\Temple\\temple.bsp"); 27 | QFile f("C:\\Users\\Zak\\Downloads\\Dam\\dam.bsp"); 28 | //QFile f("C:\\Users\\Zak\\Downloads\\Villa\\villa.bsp"); 29 | //QFile f("C:\\Users\\Zak\\Downloads\\DDHQ\\ddhq.bsp"); 30 | 31 | 32 | 33 | f.open(QFile::ReadOnly); 34 | QByteArray* bf = new QByteArray(); 35 | *bf = f.readAll(); 36 | f.close(); 37 | 38 | const P3D::BspModel* bspModel = (P3D::BspModel*)bf->constData(); 39 | 40 | object3d->SetModel(bspModel); 41 | 42 | //object3d->Setup(screenWidth, screenHeight, 54, 25, 1500, (P3D::pixel*)frameBufferImage.bits()); 43 | object3d->Setup(screenWidth, screenHeight, 60, 10, 1500, (P3D::pixel*)frameBufferImage.bits()); 44 | 45 | QRgb backgroundColor = 0x799dd6; 46 | P3D::pixel bg = 0; 47 | 48 | if constexpr(sizeof(P3D::pixel) == 1) 49 | { 50 | bg = bspModel->GetFogLightMap()[(P3D::FOG_LEVELS-1)*256]; 51 | } 52 | else 53 | { 54 | bg = backgroundColor; 55 | } 56 | 57 | object3d->SetBackgroundColor(bg); 58 | 59 | for(int i = 0; i < 256; i++) 60 | frameBufferImage.setColor(i, bspModel->GetColorMapColor(i)); 61 | } 62 | 63 | MainWindow::~MainWindow() 64 | { 65 | 66 | } 67 | 68 | void MainWindow::paintEvent(QPaintEvent *event) 69 | { 70 | Q_UNUSED(event) 71 | 72 | static unsigned int frameCount = 0; 73 | static unsigned int currentFps = 0; 74 | 75 | static double aveRtime = 0; 76 | static unsigned int rTime = 0; 77 | 78 | renderTimer.restart(); 79 | 80 | object3d->RenderScene(); 81 | 82 | rTime += renderTimer.elapsed(); 83 | 84 | QPainter p(this); 85 | 86 | p.drawImage(this->rect(), frameBufferImage); 87 | 88 | frameCount++; 89 | 90 | unsigned int elapsed = fpsTimer.elapsed(); 91 | 92 | P3D::RenderStats rs = object3d->GetRenderStats(); 93 | 94 | if(elapsed > 1000) 95 | { 96 | currentFps = qRound((double)frameCount / ((double)elapsed / 1000.0)); 97 | 98 | aveRtime = (double)rTime / (double)frameCount; 99 | 100 | rTime = 0; 101 | frameCount = 0; 102 | fpsTimer.restart(); 103 | } 104 | 105 | p.setPen(Qt::red); 106 | 107 | p.drawText(32,32, QString("FPS: %1").arg(currentFps)); 108 | p.drawText(32,48, QString("Ave Render Time: %1ms").arg(aveRtime)); 109 | 110 | p.drawText(32,64, QString("Triangles submitted: %1").arg(rs.triangles_submitted)); 111 | p.drawText(32,80, QString("Triangles drawn: %1").arg(rs.triangles_drawn)); 112 | p.drawText(32,96, QString("Vertexes transformed: %1").arg(rs.vertex_transformed)); 113 | p.drawText(32,112, QString("Scanlines drawn: %1").arg(rs.scanlines_drawn)); 114 | p.drawText(32,128, QString("Spans checked: %1").arg(rs.span_checks)); 115 | p.drawText(32,144, QString("Spans generated: %1").arg(rs.span_count)); 116 | p.drawText(32,160, QString("Triangles clipped: %1").arg(rs.triangles_clipped)); 117 | 118 | this->update(); 119 | } 120 | 121 | void MainWindow::keyPressEvent(QKeyEvent *event) 122 | { 123 | if(event->key() == Qt::Key_Left) 124 | { 125 | object3d->CameraAngle().y += 2; 126 | } 127 | else if(event->key() == Qt::Key_Right) 128 | { 129 | object3d->CameraAngle().y -= 2; 130 | } 131 | else if(event->key() == Qt::Key_Up) 132 | { 133 | P3D::V3 camAngle = object3d->CameraAngle(); 134 | 135 | float angleYRad = qDegreesToRadians((float)camAngle.y); 136 | 137 | P3D::V3 d((float)-(qSin(angleYRad) *10), 0, (float)-(qCos(angleYRad) *10)); 138 | 139 | object3d->CameraPos() += d; 140 | } 141 | else if(event->key() == Qt::Key_Down) 142 | { 143 | P3D::V3 camAngle = object3d->CameraAngle(); 144 | 145 | float angleYRad = qDegreesToRadians((float)camAngle.y); 146 | 147 | P3D::V3 d((float)-(qSin(angleYRad) *10), 0, (float)-(qCos(angleYRad) *10)); 148 | 149 | object3d->CameraPos() -= d; 150 | } 151 | else if(event->key() == Qt::Key_Z) 152 | { 153 | object3d->CameraAngle().x += 1; 154 | } 155 | else if(event->key() == Qt::Key_X) 156 | { 157 | object3d->CameraAngle().x -= 1; 158 | } 159 | else if(event->key() == Qt::Key_Q) 160 | { 161 | object3d->CameraPos().y += 1; 162 | } 163 | else if(event->key() == Qt::Key_W) 164 | { 165 | object3d->CameraPos().y -= 1; 166 | } 167 | else if(event->key() == Qt::Key_Space) 168 | { 169 | object3d->update_frustrum_bb = !object3d->update_frustrum_bb; 170 | } 171 | else if(event->key() == Qt::Key_Equal) 172 | { 173 | object3d->light_level += P3D::fp(0.05); 174 | } 175 | else if(event->key() == Qt::Key_Minus) 176 | { 177 | object3d->light_level -= P3D::fp(0.05); 178 | } 179 | else if(event->key() == Qt::Key_C) 180 | { 181 | object3d->do_collisions = !object3d->do_collisions; 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /P3DObj2Bsp/bspbuilder.h: -------------------------------------------------------------------------------- 1 | #ifndef BSPBUILDER_H 2 | #define BSPBUILDER_H 3 | 4 | #include 5 | #include 6 | 7 | #include "objloader.h" 8 | #include "config.h" 9 | 10 | namespace Obj2Bsp 11 | { 12 | 13 | class BspAABB 14 | { 15 | public: 16 | float x1,x2; 17 | float y1,y2; 18 | float z1,z2; 19 | 20 | BspAABB() 21 | { 22 | x1 = y1 = z1 = std::numeric_limits::max(); 23 | x2 = y2 = z2 = std::numeric_limits::lowest(); 24 | } 25 | 26 | void AddTriangle(const Triangle3d& tri) 27 | { 28 | for(unsigned int i = 0; i < 3; i++) 29 | { 30 | AddPoint(tri.verts[i].pos); 31 | } 32 | } 33 | 34 | void AddPoint(const QVector3D point) 35 | { 36 | if(point.x() < x1) 37 | x1 = point.x(); 38 | 39 | if(point.x() > x2) 40 | x2 = point.x(); 41 | 42 | if(point.y() < y1) 43 | y1 = point.y(); 44 | 45 | if(point.y() > y2) 46 | y2 = point.y(); 47 | 48 | if(point.z() < z1) 49 | z1 = point.z(); 50 | 51 | if(point.z() > z2) 52 | z2 = point.z(); 53 | } 54 | 55 | void AddAABB(const BspAABB& other) 56 | { 57 | if(other.x1 < x1) 58 | x1 = other.x1; 59 | 60 | if(other.x2 > x2) 61 | x2 = other.x2; 62 | 63 | if(other.y1 < y1) 64 | y1 = other.y1; 65 | 66 | if(other.y2 > y2) 67 | y2 = other.y2; 68 | 69 | if(other.z1 < z1) 70 | z1 = other.z1; 71 | 72 | if(other.z2 > z2) 73 | z2 = other.z2; 74 | } 75 | 76 | bool Intersect(const QVector3D& point) const 77 | { 78 | if(x1 > point.x()) 79 | return false; 80 | 81 | if(x2 < point.x()) 82 | return false; 83 | 84 | if(y1 > point.y()) 85 | return false; 86 | 87 | if(y2 < point.y()) 88 | return false; 89 | 90 | if(z1 > point.z()) 91 | return false; 92 | 93 | if(z2 < point.z()) 94 | return false; 95 | 96 | return true; 97 | } 98 | 99 | bool Intersect(const BspAABB& other) const 100 | { 101 | if(x1 > other.x2) 102 | return false; 103 | 104 | if(x2 < other.x1) 105 | return false; 106 | 107 | if(y1 > other.y2) 108 | return false; 109 | 110 | if(y2 < other.y1) 111 | return false; 112 | 113 | if(z1 > other.z2) 114 | return false; 115 | 116 | if(z2 < other.z1) 117 | return false; 118 | 119 | return true; 120 | } 121 | }; 122 | 123 | typedef struct BspPlane 124 | { 125 | QVector3D normal; 126 | float distance; 127 | } BspPlane; 128 | 129 | class BspTriangle 130 | { 131 | public: 132 | Triangle3d* tri; 133 | BspPlane normal_plane; 134 | BspPlane edge_plane_01; 135 | BspPlane edge_plane_12; 136 | BspPlane edge_plane_20; 137 | const Texture* texture; 138 | P3D::pixel color; 139 | 140 | void ComputePlanes() 141 | { 142 | //Edge vectors 143 | QVector3D side01 = tri->verts[1].pos - tri->verts[0].pos; 144 | QVector3D side02 = tri->verts[2].pos - tri->verts[0].pos; 145 | QVector3D side12 = tri->verts[2].pos - tri->verts[1].pos; 146 | QVector3D side20 = tri->verts[0].pos - tri->verts[2].pos; 147 | 148 | //Compute Normal 149 | normal_plane.normal = QVector3D::crossProduct(side02, side01).normalized(); 150 | normal_plane.distance = -QVector3D::dotProduct(normal_plane.normal, tri->verts[0].pos); 151 | 152 | //Edge 0-1 plane 153 | edge_plane_01.normal = QVector3D::crossProduct(side01, normal_plane.normal).normalized(); 154 | edge_plane_01.distance = -QVector3D::dotProduct(edge_plane_01.normal, tri->verts[0].pos); 155 | 156 | //Edge 1-2 plane 157 | edge_plane_12.normal = QVector3D::crossProduct(side12, normal_plane.normal).normalized(); 158 | edge_plane_12.distance = -QVector3D::dotProduct(edge_plane_12.normal, tri->verts[1].pos); 159 | 160 | //Edge 2-0 plane 161 | edge_plane_20.normal = QVector3D::crossProduct(side20, normal_plane.normal).normalized(); 162 | edge_plane_20.distance = -QVector3D::dotProduct(edge_plane_20.normal, tri->verts[2].pos); 163 | } 164 | }; 165 | 166 | class BspNode 167 | { 168 | public: 169 | BspPlane plane; //Plane that this node splits on. 170 | std::vector back_tris; //Back facing triangles that lie on this plane. 171 | std::vector front_tris; //Triangles that lie on this plane. 172 | BspNode* parent = nullptr; //Parent node. 173 | BspNode* front = nullptr; //Front children. 174 | BspNode* back = nullptr; //Back children. 175 | BspAABB node_bb; //AABB of the triangles in this node. 176 | BspAABB child_node_bb; //AABB of this node + children. 177 | }; 178 | 179 | class BspBuilder 180 | { 181 | public: 182 | BspBuilder(); 183 | BspNode* BuildBSPTree(Model3d* model); 184 | 185 | private: 186 | BspPlane CalculatePlane(const Triangle3d* triangle); 187 | BspNode* BuildTreeRecursive(std::vector& triangles); 188 | BspPlane CheckPlane(std::vector& triangles, unsigned int index, int& front, int& back, int& onplane); 189 | float Distance(const BspPlane& plane, const QVector3D& pos); 190 | int Sign(float i); 191 | void SeperateTriangles(BspPlane& plane, std::vector& triangles, std::vector& front_tris, std::vector& back_tris, std::vector& plane_tris_front, std::vector &plane_tris_back); 192 | float Relation(float a, float b); 193 | Vertex3d LerpVertex(Vertex3d& out, const Vertex3d& vx1, const Vertex3d& vx2, float frac); 194 | 195 | static constexpr float epsilon = 0.25f; 196 | }; 197 | 198 | } 199 | 200 | #endif // BSPBUILDER_H 201 | -------------------------------------------------------------------------------- /P3DBenchmark/source/main.iwram.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | #ifdef __arm__ 7 | #include 8 | #include 9 | #include 10 | #else 11 | #include 12 | #endif 13 | 14 | 15 | #include "../../RenderDevice.h" 16 | #include "../../RenderTarget.h" 17 | #include "../../PixelShaderGBA8.h" 18 | 19 | #define DCNT_PAGE 0x0010 20 | 21 | #define VID_PAGE1 VRAM 22 | #define VID_PAGE2 0x600A000 23 | 24 | #define TM_FREQ_1024 0x0003 25 | #define TM_ENABLE 0x0080 26 | #define TM_CASCADE 0x0004 27 | #define TM_FREQ_1024 0x0003 28 | #define TM_FREQ_256 0x0002 29 | 30 | #define REG_WAITCNT *((vu16 *)(0x4000204)) 31 | 32 | const uint8_t fogLightMap[65536] = {255}; 33 | 34 | #ifndef __arm__ 35 | P3D::pixel frontbuffer[240*160]; 36 | P3D::pixel backbuffer[240*160]; 37 | #endif 38 | 39 | P3D::pixel* I_GetBackBuffer() 40 | { 41 | #ifdef __arm__ 42 | if(REG_DISPCNT & DCNT_PAGE) 43 | return (P3D::pixel*)VID_PAGE1; 44 | 45 | return (P3D::pixel*)VID_PAGE2; 46 | #else 47 | return backbuffer; 48 | #endif 49 | } 50 | 51 | void I_FinishUpdate_e32() 52 | { 53 | #ifdef __arm__ 54 | REG_DISPCNT ^= DCNT_PAGE; 55 | #else 56 | 57 | #endif 58 | } 59 | 60 | inline uint32_t rng32() 61 | { 62 | static uint32_t seed = 1234; 63 | 64 | return seed = (seed*1664525U + 1013904223U); 65 | } 66 | 67 | inline int8_t r8() 68 | { 69 | return (int8_t)rng32(); 70 | } 71 | 72 | inline int16_t r16() 73 | { 74 | return (int16_t)rng32() & 511; 75 | } 76 | 77 | int main() 78 | { 79 | #ifdef __arm__ 80 | 81 | irqInit(); 82 | 83 | //Set gamepak wait states and prefetch. 84 | REG_WAITCNT = 0x46DA; 85 | 86 | SetMode(MODE_4 | BG2_ENABLE | BIT(5)); 87 | 88 | unsigned short* pal_ram = (unsigned short*)0x5000000; 89 | 90 | for(int i = 0; i< 256; i++) 91 | { 92 | pal_ram[i] = RGB8(i, i, i); 93 | } 94 | #endif 95 | 96 | P3D::RenderTarget* render_target = new P3D::RenderTarget(240, 160, I_GetBackBuffer()); 97 | render_target->AttachZBuffer(); 98 | 99 | P3D::RenderDevice* render_device = new P3D::RenderDevice(); 100 | render_device->SetRenderFlags>(); 101 | //render_device->SetRenderFlags>(); 102 | //render_device->SetRenderFlags>(); 103 | //render_device->SetRenderFlags>(); 104 | //render_device->SetRenderFlags>(); 105 | //render_device->SetRenderFlags>(); 106 | //render_device->SetRenderFlags>(); 107 | 108 | 109 | #if 0 110 | render_device->SetFogMode(P3D::FogMode::FogLinear); 111 | render_device->SetFogDepth(500, 1000); 112 | #else 113 | render_device->SetFogMode(P3D::FogMode::FogExponential2); 114 | render_device->SetFogDensity(2); 115 | #endif 116 | 117 | render_device->SetRenderTarget(render_target); 118 | 119 | 120 | 121 | render_device->ClearDepth(1); 122 | render_device->SetFogLightMap(fogLightMap); 123 | 124 | P3D::pixel* tex = new P3D::pixel[P3D::TEX_SIZE*P3D::TEX_SIZE]; 125 | for(int i = 0; i < P3D::TEX_SIZE*P3D::TEX_SIZE; i++) 126 | { 127 | tex[i] = rand(); 128 | } 129 | 130 | P3D::Material m; 131 | #if 1 132 | m.type = P3D::Material::Texture; 133 | m.pixels = tex; 134 | #else 135 | m.type = P3D::Material::Color; 136 | m.color = 100; 137 | #endif 138 | render_device->SetMaterial(m); 139 | 140 | 141 | const P3D::fp aspectRatio = (float)240 / (float)160; 142 | 143 | render_device->SetPerspective(60, aspectRatio, 10, 1000); 144 | 145 | render_device->PushMatrix(); 146 | render_device->LoadIdentity(); 147 | render_device->Translate(P3D::V3(0,0,-200)); 148 | 149 | render_device->BeginFrame(); 150 | 151 | render_device->BeginDraw(); 152 | render_device->ClearColor(0); 153 | 154 | 155 | P3D::V2 uv[3]; 156 | uv[0] = P3D::V2(0,0); 157 | uv[1] = P3D::V2(64,0); 158 | uv[2] = P3D::V2(64,64); 159 | 160 | unsigned int vi[3] = {2,1,0}; 161 | 162 | #ifndef __arm__ 163 | QElapsedTimer t; 164 | t.start(); 165 | #else 166 | REG_TM2CNT_L= 65535-65; // 65 ticks = 1/1000 secs 167 | REG_TM2CNT_H = TM_FREQ_256 | TM_ENABLE; // we're using the 256 cycle timer 168 | 169 | // cascade into tm3 170 | REG_TM3CNT_H = TM_CASCADE | TM_ENABLE; 171 | #endif 172 | 173 | #ifdef __arm__ 174 | const int runs = 1000; 175 | #else 176 | const int runs = 1000000; 177 | #endif 178 | 179 | I_FinishUpdate_e32(); 180 | 181 | unsigned int ll; 182 | 183 | P3D::fp lights[3] = {ll,ll,ll}; 184 | 185 | for(int i = 0; i < runs; i++) 186 | { 187 | P3D::V3 v[3]; 188 | v[0] = P3D::V3(-100 + r8(),100+ r8(),0+ r8()); 189 | v[1] = P3D::V3(100+ r8(),100+ r8(),0+ r8()); 190 | v[2] = P3D::V3(100+ r8(),-100+ r8(),0+ r8()); 191 | 192 | render_device->TransformVertexes(v, 3); 193 | 194 | render_device->DrawTriangle(vi, uv, lights); 195 | 196 | //P3D::fp dx = v[0].x * v[0].y; 197 | //P3D::fp dy = v[0].y * v[0].z; 198 | 199 | //P3D::fp dz = (dx/dy); 200 | //P3D::fp dz = P3D::pApproxDiv(dx, dy); 201 | 202 | //I_GetBackBuffer()[0] = dz.i(); 203 | } 204 | 205 | 206 | 207 | #ifndef __arm__ 208 | uint64_t x = t.elapsed(); 209 | #else 210 | 211 | uint64_t x = REG_TM3CNT_L; 212 | consoleDemoInit(); 213 | 214 | #endif 215 | 216 | render_device->EndDraw(); 217 | 218 | render_device->EndFrame(); 219 | 220 | render_device->PopMatrix(); 221 | render_device->PopMatrix(); 222 | 223 | double s = x / 1000.0; 224 | double p = (double)runs / s; 225 | 226 | printf("%d polys rendered in %f s\n", runs, s); 227 | printf("Polys/second %d\n", (int)p); 228 | 229 | while(true) 230 | { 231 | } 232 | } 233 | 234 | 235 | 236 | -------------------------------------------------------------------------------- /Potato3dGBAExample/Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #--------------------------------------------------------------------------------- 4 | 5 | ifeq ($(strip $(DEVKITARM)),) 6 | $(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") 7 | endif 8 | 9 | include $(DEVKITARM)/gba_rules 10 | 11 | #--------------------------------------------------------------------------------- 12 | # TARGET is the name of the output 13 | # BUILD is the directory where object files & intermediate files will be placed 14 | # SOURCES is a list of directories containing source code 15 | # INCLUDES is a list of directories containing extra header files 16 | # DATA is a list of directories containing binary data 17 | # GRAPHICS is a list of directories containing files to be processed by grit 18 | # 19 | # All directories are specified relative to the project directory where 20 | # the makefile is found 21 | # 22 | #--------------------------------------------------------------------------------- 23 | TARGET := $(notdir $(CURDIR)) 24 | BUILD := build 25 | SOURCES := source 26 | INCLUDES := include 27 | DATA := data 28 | MUSIC := music 29 | 30 | #--------------------------------------------------------------------------------- 31 | # options for code generation 32 | #--------------------------------------------------------------------------------- 33 | ARCH := -mthumb -mthumb-interwork 34 | 35 | CFLAGS := -g -Wall -O2 -fgcse-after-reload -gdwarf-4\ 36 | -mcpu=arm7tdmi -mtune=arm7tdmi\ 37 | -ffast-math -ffunction-sections\ 38 | $(ARCH) 39 | 40 | CFLAGS += $(INCLUDE) 41 | 42 | CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=c++20 43 | 44 | ASFLAGS := -g $(ARCH) 45 | LDFLAGS = -g $(ARCH) -Wl,-Map,$(notdir $*.map) 46 | 47 | #--------------------------------------------------------------------------------- 48 | # any extra libraries we wish to link with the project 49 | #--------------------------------------------------------------------------------- 50 | LIBS := -lmm -lgba 51 | 52 | 53 | #--------------------------------------------------------------------------------- 54 | # list of directories containing libraries, this must be the top level containing 55 | # include and lib 56 | #--------------------------------------------------------------------------------- 57 | LIBDIRS := $(LIBGBA) 58 | 59 | #--------------------------------------------------------------------------------- 60 | # no real need to edit anything past this point unless you need to add additional 61 | # rules for different file extensions 62 | #--------------------------------------------------------------------------------- 63 | 64 | 65 | ifneq ($(BUILD),$(notdir $(CURDIR))) 66 | #--------------------------------------------------------------------------------- 67 | 68 | export OUTPUT := $(CURDIR)/$(TARGET) 69 | 70 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 71 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ 72 | $(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir)) 73 | 74 | export DEPSDIR := $(CURDIR)/$(BUILD) 75 | 76 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 77 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 78 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 79 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 80 | 81 | #ifneq ($(strip $(MUSIC)),) 82 | # export AUDIOFILES := $(foreach dir,$(notdir $(wildcard $(MUSIC)/*.*)),$(CURDIR)/$(MUSIC)/$(dir)) 83 | # BINFILES += soundbank.bin 84 | #endif 85 | 86 | #--------------------------------------------------------------------------------- 87 | # use CXX for linking C++ projects, CC for standard C 88 | #--------------------------------------------------------------------------------- 89 | ifeq ($(strip $(CPPFILES)),) 90 | #--------------------------------------------------------------------------------- 91 | export LD := $(CC) 92 | #--------------------------------------------------------------------------------- 93 | else 94 | #--------------------------------------------------------------------------------- 95 | export LD := $(CXX) 96 | #--------------------------------------------------------------------------------- 97 | endif 98 | #--------------------------------------------------------------------------------- 99 | 100 | export OFILES_BIN := $(addsuffix .o,$(BINFILES)) 101 | 102 | export OFILES_SOURCES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 103 | 104 | export OFILES := $(OFILES_BIN) $(OFILES_SOURCES) 105 | 106 | export HFILES := $(addsuffix .h,$(subst .,_,$(BINFILES))) 107 | 108 | export INCLUDE := $(foreach dir,$(INCLUDES),-iquote $(CURDIR)/$(dir)) \ 109 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 110 | -I$(CURDIR)/$(BUILD) 111 | 112 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 113 | 114 | .PHONY: $(BUILD) clean 115 | 116 | #--------------------------------------------------------------------------------- 117 | $(BUILD): 118 | @[ -d $@ ] || mkdir -p $@ 119 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 120 | 121 | #--------------------------------------------------------------------------------- 122 | clean: 123 | @echo clean ... 124 | @rm -fr $(BUILD) $(TARGET).elf $(TARGET).gba 125 | 126 | 127 | #--------------------------------------------------------------------------------- 128 | else 129 | 130 | #--------------------------------------------------------------------------------- 131 | # main targets 132 | #--------------------------------------------------------------------------------- 133 | 134 | $(OUTPUT).gba : $(OUTPUT).elf 135 | 136 | $(OUTPUT).elf : $(OFILES) 137 | 138 | $(OFILES_SOURCES) : $(HFILES) 139 | 140 | #--------------------------------------------------------------------------------- 141 | # The bin2o rule should be copied and modified 142 | # for each extension used in the data directories 143 | #--------------------------------------------------------------------------------- 144 | 145 | #--------------------------------------------------------------------------------- 146 | # rule to build soundbank from music files 147 | #--------------------------------------------------------------------------------- 148 | #soundbank.bin soundbank.h : $(AUDIOFILES) 149 | #--------------------------------------------------------------------------------- 150 | # @mmutil $^ -osoundbank.bin -hsoundbank.h 151 | 152 | #--------------------------------------------------------------------------------- 153 | # This rule links in binary data with the .wad extension 154 | #--------------------------------------------------------------------------------- 155 | %.bin.o %_bin.h : %.bin 156 | #--------------------------------------------------------------------------------- 157 | @echo $(notdir $<) 158 | @$(bin2o) 159 | 160 | 161 | -include $(DEPSDIR)/*.d 162 | #--------------------------------------------------------------------------------------- 163 | endif 164 | #--------------------------------------------------------------------------------------- 165 | -------------------------------------------------------------------------------- /Potato3dExample2/source/mainloop.iwram.cpp: -------------------------------------------------------------------------------- 1 | #include "../include/mainloop.h" 2 | #include "../include/videosystem.h" 3 | 4 | 5 | MainLoop::MainLoop() 6 | { 7 | const P3D::fp halfHFov = hFov / 2; 8 | const P3D::fp halfVFov = vFov / 2; 9 | 10 | const P3D::fp halfFrustrumWidth = zFar * std::tan((float)P3D::pD2R(halfHFov)); 11 | const P3D::fp halfFrustrumHeight = zFar * std::tan((float)P3D::pD2R(halfVFov)); 12 | 13 | frustrumPoints[0] = P3D::V3(-halfFrustrumWidth, -halfFrustrumHeight, -zFar); 14 | frustrumPoints[1] = P3D::V3(halfFrustrumWidth, halfFrustrumHeight, -zFar); 15 | frustrumPoints[2] = P3D::V3(-halfFrustrumWidth, halfFrustrumHeight, -zFar); 16 | frustrumPoints[3] = P3D::V3(halfFrustrumWidth, -halfFrustrumHeight, -zFar); 17 | 18 | triBuffer.reserve(8192); 19 | } 20 | 21 | void MainLoop::Run() 22 | { 23 | vid.Setup(&keyState); 24 | vid.SetPalette(model.GetModel()->GetColorMap()); 25 | 26 | constexpr unsigned int flags = P3D::NoFlags; 27 | //constexpr unsigned int flags = P3D::SubdividePerspectiveMapping; 28 | //constexpr unsigned int flags = P3D::Fog; 29 | //constexpr unsigned int flags = P3D::SubdividePerspectiveMapping | P3D::Fog; 30 | //constexpr unsigned int flags = P3D::SubdividePerspectiveMapping | P3D::VertexLight | P3D::Fog; 31 | 32 | renderDev.SetRenderFlags>(); 33 | 34 | 35 | renderDev.SetPerspective(vFov, 1.5, zNear, zFar); 36 | 37 | renderDev.SetFogMode(P3D::FogExponential2); 38 | renderDev.SetFogDensity(1.33); 39 | 40 | //renderDev.SetFogMode(P3D::FogLinear); 41 | //renderDev.SetFogDepth(750, 1000); 42 | 43 | 44 | renderDev.SetFogLightMap(model.GetModel()->GetFogLightMap()); 45 | 46 | const P3D::pixel background = model.GetModel()->GetFogLightMap()[(P3D::FOG_LEVELS-1)*256]; 47 | 48 | while(true) 49 | { 50 | RunTimeslots(); 51 | 52 | renderDev.SetRenderTarget(vid.GetBackBuffer()); 53 | 54 | renderDev.ClearColor(background); 55 | 56 | renderDev.PushMatrix(); 57 | 58 | renderDev.RotateX(-camera.GetAngle().x); 59 | renderDev.RotateY(-camera.GetAngle().y); 60 | renderDev.RotateZ(-camera.GetAngle().z); 61 | 62 | renderDev.Translate(P3D::V3(-camera.GetEyePosition().x, -camera.GetEyePosition().y, -camera.GetEyePosition().z)); 63 | 64 | UpdateFrustrumBB(); 65 | 66 | renderDev.BeginFrame(); 67 | 68 | renderDev.BeginDraw(frustrumPlanes); 69 | 70 | RenderModel(); 71 | 72 | renderDev.EndDraw(); 73 | 74 | renderDev.EndFrame(); 75 | 76 | renderDev.PopMatrix(); 77 | 78 | vid.PageFlip(); 79 | } 80 | } 81 | 82 | void MainLoop::UpdateFrustrumBB() 83 | { 84 | viewFrustrumBB = P3D::AABB(); 85 | 86 | P3D::M4 camMatrix = renderDev.GetMatrix().Inverted(); 87 | 88 | P3D::V4 t1 = camMatrix * frustrumPoints[0]; 89 | P3D::V4 t2 = camMatrix * frustrumPoints[1]; 90 | P3D::V4 t3 = camMatrix * frustrumPoints[2]; 91 | P3D::V4 t4 = camMatrix * frustrumPoints[3]; 92 | 93 | viewFrustrumBB.AddPoint(camera.GetEyePosition()); 94 | 95 | viewFrustrumBB.AddPoint(P3D::V3(t1.x, t1.y, t1.z)); 96 | viewFrustrumBB.AddPoint(P3D::V3(t2.x, t2.y, t2.z)); 97 | viewFrustrumBB.AddPoint(P3D::V3(t3.x, t3.y, t3.z)); 98 | viewFrustrumBB.AddPoint(P3D::V3(t4.x, t4.y, t4.z)); 99 | } 100 | 101 | void MainLoop::RenderModel() 102 | { 103 | model.GetModel()->Sort(camera.GetEyePosition(), viewFrustrumBB, triBuffer, true); 104 | 105 | //for(int i = triBuffer.size()-1; i >= 0; i--) 106 | for(unsigned int i = 0; i < triBuffer.size(); i++) 107 | { 108 | const P3D::BspModelTriangle* tri = triBuffer[i]; 109 | 110 | if(!FrustrumTestTriangle(tri)) 111 | continue; 112 | 113 | const P3D::BspNodeTexture* ntex = model.GetModel()->GetTexture(tri->texture); 114 | 115 | P3D::V3 verts[3] = {tri->tri.verts[0].pos, tri->tri.verts[1].pos, tri->tri.verts[2].pos}; 116 | 117 | P3D::Material m; 118 | 119 | if(ntex) 120 | { 121 | m.type = P3D::Material::Texture; 122 | m.pixels = model.GetModel()->GetTexturePixels(ntex->texture_pixels_offset); 123 | 124 | const P3D::V3 lightVector(0.66,0.66,0.33); 125 | 126 | const P3D::fp lightLevel = P3D::fp(0.5) + P3D::pASR(P3D::fp(1) + tri->normal_plane.Normal().DotProduct(lightVector), 2); 127 | 128 | const P3D::fp light_levels[3] = {lightLevel, lightLevel, lightLevel}; 129 | 130 | const P3D::V2 uvs[3] = {tri->tri.verts[0].uv, tri->tri.verts[1].uv, tri->tri.verts[2].uv}; 131 | 132 | renderDev.SetMaterial(m); 133 | 134 | renderDev.DrawTriangle(verts, uvs, light_levels); 135 | //renderDev.DrawTriangle(verts, uvs); 136 | } 137 | else 138 | { 139 | m.color = tri->color; 140 | 141 | renderDev.SetMaterial(m); 142 | 143 | renderDev.DrawTriangle(verts); 144 | } 145 | } 146 | } 147 | 148 | bool MainLoop::FrustrumTestTriangle(const P3D::BspModelTriangle* tri) const 149 | { 150 | for(unsigned int i = P3D::Left; i <= P3D::Near; i++) 151 | { 152 | if(!frustrumPlanes[i].TriangleIsFrontside(tri->tri.verts[0].pos, tri->tri.verts[1].pos, tri->tri.verts[2].pos)) 153 | { 154 | return false; 155 | } 156 | } 157 | 158 | return true; 159 | } 160 | 161 | void MainLoop::ResolveCollisions() 162 | { 163 | const int bb_size = 100; 164 | P3D::AABB player_box(camera.GetPosition(), bb_size); 165 | 166 | model.GetModel()->Sort(camera.GetPosition(), player_box, triBuffer, true); 167 | 168 | int collision_count = 0; 169 | 170 | for(int i = triBuffer.size() - 1; i >= 0; i--) 171 | { 172 | P3D::V3 resolutionVector; 173 | 174 | if(collision.CheckCollision(triBuffer.at(i), camera.GetPosition(), 50, resolutionVector)) 175 | { 176 | if( (pAbs(resolutionVector.x) + pAbs(resolutionVector.z)) > (pASR(resolutionVector.y,1))) 177 | { 178 | resolutionVector.y = 0; 179 | } 180 | 181 | camera.MovePosition(resolutionVector); 182 | 183 | i = triBuffer.size() -1; 184 | collision_count++; 185 | 186 | if(collision_count > 5) 187 | break; 188 | } 189 | } 190 | 191 | if(collision_count > 0) 192 | gravity_velocity = 0; 193 | else 194 | gravity_velocity = P3D::pMin(gravity_velocity + 2, P3D::fp(25)); 195 | } 196 | 197 | void MainLoop::RunTimeslots() 198 | { 199 | static unsigned int gameTime = vid.GetTime(); 200 | 201 | unsigned int realTime = vid.GetTime(); 202 | 203 | while(gameTime < realTime) 204 | { 205 | vid.UpdateKeys(); 206 | camera.HandleInput(keyState, gravity_velocity); 207 | 208 | ResolveCollisions(); 209 | 210 | gameTime += frameTicks; 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /P3DBenchmark/Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #--------------------------------------------------------------------------------- 4 | 5 | ifeq ($(strip $(DEVKITARM)),) 6 | $(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") 7 | endif 8 | 9 | include $(DEVKITARM)/gba_rules 10 | 11 | #--------------------------------------------------------------------------------- 12 | # TARGET is the name of the output 13 | # BUILD is the directory where object files & intermediate files will be placed 14 | # SOURCES is a list of directories containing source code 15 | # INCLUDES is a list of directories containing extra header files 16 | # DATA is a list of directories containing binary data 17 | # GRAPHICS is a list of directories containing files to be processed by grit 18 | # 19 | # All directories are specified relative to the project directory where 20 | # the makefile is found 21 | # 22 | #--------------------------------------------------------------------------------- 23 | TARGET := $(notdir $(CURDIR)) 24 | BUILD := build 25 | SOURCES := source 26 | INCLUDES := include 27 | DATA := data 28 | MUSIC := music 29 | 30 | #--------------------------------------------------------------------------------- 31 | # options for code generation 32 | #--------------------------------------------------------------------------------- 33 | ARCH := -mthumb -mthumb-interwork 34 | 35 | CFLAGS := -g -Wall -O2 -fgcse-after-reload -gdwarf-4\ 36 | -mcpu=arm7tdmi -mtune=arm7tdmi -fno-threadsafe-statics -fallow-store-data-races\ 37 | -ffast-math -ffunction-sections -fdeclone-ctor-dtor\ 38 | -funswitch-loops -fgcse-las -flive-range-shrinkage -fira-hoist-pressure -fira-loop-pressure -ftree-partial-pre\ 39 | -fno-align-functions -fno-align-jumps -fno-align-labels -fno-align-loops\ 40 | -fno-schedule-insns\ 41 | $(ARCH) 42 | 43 | CFLAGS += $(INCLUDE) 44 | 45 | CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=c++20 46 | 47 | ASFLAGS := -g $(ARCH) 48 | LDFLAGS = -g $(ARCH) -Wl,-Map,$(notdir $*.map) 49 | 50 | #--------------------------------------------------------------------------------- 51 | # any extra libraries we wish to link with the project 52 | #--------------------------------------------------------------------------------- 53 | LIBS := -lmm -lgba 54 | 55 | 56 | #--------------------------------------------------------------------------------- 57 | # list of directories containing libraries, this must be the top level containing 58 | # include and lib 59 | #--------------------------------------------------------------------------------- 60 | LIBDIRS := $(LIBGBA) 61 | 62 | #--------------------------------------------------------------------------------- 63 | # no real need to edit anything past this point unless you need to add additional 64 | # rules for different file extensions 65 | #--------------------------------------------------------------------------------- 66 | 67 | 68 | ifneq ($(BUILD),$(notdir $(CURDIR))) 69 | #--------------------------------------------------------------------------------- 70 | 71 | export OUTPUT := $(CURDIR)/$(TARGET) 72 | 73 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 74 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ 75 | $(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir)) 76 | 77 | export DEPSDIR := $(CURDIR)/$(BUILD) 78 | 79 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 80 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 81 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 82 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 83 | 84 | #ifneq ($(strip $(MUSIC)),) 85 | # export AUDIOFILES := $(foreach dir,$(notdir $(wildcard $(MUSIC)/*.*)),$(CURDIR)/$(MUSIC)/$(dir)) 86 | # BINFILES += soundbank.bin 87 | #endif 88 | 89 | #--------------------------------------------------------------------------------- 90 | # use CXX for linking C++ projects, CC for standard C 91 | #--------------------------------------------------------------------------------- 92 | ifeq ($(strip $(CPPFILES)),) 93 | #--------------------------------------------------------------------------------- 94 | export LD := $(CC) 95 | #--------------------------------------------------------------------------------- 96 | else 97 | #--------------------------------------------------------------------------------- 98 | export LD := $(CXX) 99 | #--------------------------------------------------------------------------------- 100 | endif 101 | #--------------------------------------------------------------------------------- 102 | 103 | export OFILES_BIN := $(addsuffix .o,$(BINFILES)) 104 | 105 | export OFILES_SOURCES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 106 | 107 | export OFILES := $(OFILES_BIN) $(OFILES_SOURCES) 108 | 109 | export HFILES := $(addsuffix .h,$(subst .,_,$(BINFILES))) 110 | 111 | export INCLUDE := $(foreach dir,$(INCLUDES),-iquote $(CURDIR)/$(dir)) \ 112 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 113 | -I$(CURDIR)/$(BUILD) 114 | 115 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 116 | 117 | .PHONY: $(BUILD) clean 118 | 119 | #--------------------------------------------------------------------------------- 120 | $(BUILD): 121 | @[ -d $@ ] || mkdir -p $@ 122 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 123 | 124 | #--------------------------------------------------------------------------------- 125 | clean: 126 | @echo clean ... 127 | @rm -fr $(BUILD) $(TARGET).elf $(TARGET).gba 128 | 129 | 130 | #--------------------------------------------------------------------------------- 131 | else 132 | 133 | #--------------------------------------------------------------------------------- 134 | # main targets 135 | #--------------------------------------------------------------------------------- 136 | 137 | $(OUTPUT).gba : $(OUTPUT).elf 138 | 139 | $(OUTPUT).elf : $(OFILES) 140 | 141 | $(OFILES_SOURCES) : $(HFILES) 142 | 143 | #--------------------------------------------------------------------------------- 144 | # The bin2o rule should be copied and modified 145 | # for each extension used in the data directories 146 | #--------------------------------------------------------------------------------- 147 | 148 | #--------------------------------------------------------------------------------- 149 | # rule to build soundbank from music files 150 | #--------------------------------------------------------------------------------- 151 | #soundbank.bin soundbank.h : $(AUDIOFILES) 152 | #--------------------------------------------------------------------------------- 153 | # @mmutil $^ -osoundbank.bin -hsoundbank.h 154 | 155 | #--------------------------------------------------------------------------------- 156 | # This rule links in binary data with the .wad extension 157 | #--------------------------------------------------------------------------------- 158 | %.bin.o %_bin.h : %.bin 159 | #--------------------------------------------------------------------------------- 160 | @echo $(notdir $<) 161 | @$(bin2o) 162 | 163 | 164 | -include $(DEPSDIR)/*.d 165 | #--------------------------------------------------------------------------------------- 166 | endif 167 | #--------------------------------------------------------------------------------------- 168 | -------------------------------------------------------------------------------- /Potato3dExample/mainwindow2.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "mainwindow2.h" 3 | 4 | #include 5 | #include 6 | 7 | #include "../RenderDevice.h" 8 | #include "../RenderTarget.h" 9 | #include "../PixelShaderGBA8.h" 10 | 11 | const QImage::Format format = QImage::Format_Indexed8; 12 | 13 | MainWindow2::MainWindow2(QWidget *parent) 14 | : QMainWindow(parent) 15 | { 16 | this->resize(screenWidth * 3, screenHeight * 3); 17 | this->update(); 18 | 19 | fpsTimer.start(); 20 | 21 | frameBufferImage = QImage(screenWidth, screenHeight, format); 22 | 23 | QVector color_table; 24 | 25 | color_table.append(qRgb(0,0,0)); 26 | color_table.append(qRgb(0,255,0)); 27 | color_table.append(qRgb(255,0,0)); 28 | 29 | 30 | frameBufferImage.setColorTable(color_table); 31 | 32 | render_target = new P3D::RenderTarget(screenWidth, screenHeight, (P3D::pixel*)frameBufferImage.scanLine(0)); 33 | render_target->AttachZBuffer(); 34 | 35 | render_device = new P3D::RenderDevice(); 36 | 37 | render_device->SetRenderTarget(render_target); 38 | 39 | const float aspectRatio = (float)screenWidth / (float)screenHeight; 40 | 41 | render_device->SetPerspective(60, aspectRatio, 10, 1000); 42 | 43 | //render_device->SetRenderFlags>(); 44 | //render_device->SetRenderFlags(); 45 | render_device->SetRenderFlags>(); 46 | 47 | //render_device->SetRenderFlags(); 48 | 49 | //render_device->SetRenderFlags(); 50 | 51 | render_device->SetFogColor(0); 52 | 53 | #if 1 54 | render_device->SetFogMode(P3D::FogLinear); 55 | render_device->SetFogDepth(200, 400); 56 | #else 57 | render_device->SetFogMode(P3D::FogExponential2); 58 | render_device->SetFogDensity(0.0025); 59 | #endif 60 | } 61 | 62 | MainWindow2::~MainWindow2() 63 | { 64 | 65 | } 66 | static P3D::fp rotateY = 0; 67 | static P3D::fp translate = -200; 68 | 69 | void MainWindow2::paintEvent(QPaintEvent *event) 70 | { 71 | Q_UNUSED(event) 72 | 73 | 74 | static unsigned int frameCount = 0; 75 | static unsigned int currentFps = 0; 76 | 77 | static double aveRtime = 0; 78 | static unsigned int rTime = 0; 79 | 80 | renderTimer.restart(); 81 | 82 | 83 | QImage texture = QImage(":/models/test_text.png"); 84 | texture.convertTo(format); 85 | frameBufferImage.setColorTable(texture.colorTable()); 86 | 87 | P3D::Material mat1; 88 | mat1.type = P3D::Material::Texture; 89 | mat1.pixels = (P3D::pixel*)texture.constBits(); 90 | 91 | render_device->SetMaterial(mat1); 92 | 93 | render_device->ClearColor(0); 94 | render_device->ClearDepth(1); 95 | 96 | render_device->PushMatrix(); 97 | render_device->RotateY(rotateY); 98 | 99 | render_device->PushMatrix(); 100 | render_device->LoadIdentity(); 101 | render_device->Translate(P3D::V3(0,0,translate)); 102 | 103 | /* 104 | 105 | render_device->BeginFrame(); 106 | 107 | P3D::V3 v[3]; 108 | v[0] = P3D::V3(-100,100,0); 109 | v[1] = P3D::V3(100,100,0); 110 | v[2] = P3D::V3(100,-100,0); 111 | 112 | P3D::V2 uv[3]; 113 | uv[0] = P3D::V2(0,0); 114 | uv[1] = P3D::V2(64,0); 115 | uv[2] = P3D::V2(64,64); 116 | 117 | 118 | render_device->DrawTriangle(v, uv); 119 | 120 | v[0] = P3D::V3(-100,100,0); 121 | v[1] = P3D::V3(100,-100,0); 122 | v[2] = P3D::V3(-100,-100,0); 123 | 124 | uv[0] = P3D::V2(0,0); 125 | uv[1] = P3D::V2(64,64); 126 | uv[2] = P3D::V2(0,64); 127 | 128 | render_device->DrawTriangle(v, uv); 129 | 130 | render_device->EndFrame(); 131 | */ 132 | 133 | render_device->BeginFrame(); 134 | 135 | render_device->BeginDraw(); 136 | 137 | P3D::V3 v[4]; 138 | v[0] = P3D::V3(-100,100,0); 139 | v[1] = P3D::V3(100,100,0); 140 | v[2] = P3D::V3(100,-100,0); 141 | v[3] = P3D::V3(-100,-100,0); 142 | 143 | render_device->TransformVertexes(v, 4); 144 | 145 | const int uv_tile = 1; 146 | 147 | P3D::V2 uv[3]; 148 | uv[0] = P3D::V2(0,0); 149 | uv[1] = P3D::V2(64*uv_tile,0); 150 | uv[2] = P3D::V2(64*uv_tile,64*uv_tile); 151 | 152 | unsigned int vi[3] = {2,1,0}; 153 | 154 | render_device->DrawTriangle(vi, uv); 155 | 156 | uv[0] = P3D::V2(0,0); 157 | uv[1] = P3D::V2(64*uv_tile,64*uv_tile); 158 | uv[2] = P3D::V2(0,64*uv_tile); 159 | 160 | unsigned int vi2[3] = {0,2,3}; 161 | 162 | render_device->DrawTriangle(vi2, uv); 163 | 164 | 165 | render_device->EndDraw(); 166 | 167 | render_device->EndFrame(); 168 | 169 | render_device->PopMatrix(); 170 | render_device->PopMatrix(); 171 | 172 | 173 | rTime += renderTimer.elapsed(); 174 | 175 | QPainter p(this); 176 | 177 | p.drawImage(this->rect(), frameBufferImage); 178 | 179 | frameCount++; 180 | 181 | unsigned int elapsed = fpsTimer.elapsed(); 182 | 183 | P3D::RenderStats rs = render_device->GetRenderStats(); 184 | 185 | if(elapsed > 1000) 186 | { 187 | currentFps = qRound((double)frameCount / ((double)elapsed / 1000.0)); 188 | 189 | aveRtime = (double)rTime / (double)frameCount; 190 | 191 | rTime = 0; 192 | frameCount = 0; 193 | fpsTimer.restart(); 194 | } 195 | 196 | p.setPen(Qt::yellow); 197 | 198 | p.drawText(32,32, QString("FPS: %1").arg(currentFps)); 199 | p.drawText(32,48, QString("Ave Render Time: %1ms").arg(aveRtime)); 200 | 201 | p.drawText(32,64, QString("Triangles submitted: %1").arg(rs.triangles_submitted)); 202 | p.drawText(32,80, QString("Triangles drawn: %1").arg(rs.triangles_drawn)); 203 | p.drawText(32,96, QString("Vertexes transformed: %1").arg(rs.vertex_transformed)); 204 | p.drawText(32,112, QString("Scanlines drawn: %1").arg(rs.scanlines_drawn)); 205 | p.drawText(32,128, QString("Spans checked: %1").arg(rs.span_checks)); 206 | p.drawText(32,144, QString("Spans generated: %1").arg(rs.span_count)); 207 | p.drawText(32,160, QString("Triangles clipped: %1").arg(rs.triangles_clipped)); 208 | 209 | 210 | this->update(); 211 | } 212 | 213 | void MainWindow2::keyPressEvent(QKeyEvent *event) 214 | { 215 | if(event->key() == Qt::Key_Left) 216 | { 217 | rotateY -= P3D::fp(1); 218 | } 219 | else if(event->key() == Qt::Key_Right) 220 | { 221 | rotateY += P3D::fp(1); 222 | } 223 | 224 | if(event->key() == Qt::Key_Up) 225 | { 226 | translate += P3D::fp(1); 227 | } 228 | else if(event->key() == Qt::Key_Down) 229 | { 230 | translate -= P3D::fp(1); 231 | } 232 | 233 | if(rotateY >= 360) 234 | rotateY -= 360; 235 | 236 | if(rotateY < 0) 237 | rotateY += 360; 238 | } 239 | -------------------------------------------------------------------------------- /Potato3dExample2/Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #--------------------------------------------------------------------------------- 4 | 5 | ifeq ($(strip $(DEVKITARM)),) 6 | $(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") 7 | endif 8 | 9 | include $(DEVKITARM)/gba_rules 10 | 11 | #--------------------------------------------------------------------------------- 12 | # TARGET is the name of the output 13 | # BUILD is the directory where object files & intermediate files will be placed 14 | # SOURCES is a list of directories containing source code 15 | # INCLUDES is a list of directories containing extra header files 16 | # DATA is a list of directories containing binary data 17 | # GRAPHICS is a list of directories containing files to be processed by grit 18 | # 19 | # All directories are specified relative to the project directory where 20 | # the makefile is found 21 | # 22 | #--------------------------------------------------------------------------------- 23 | TARGET := $(notdir $(CURDIR)) 24 | BUILD := build 25 | SOURCES := source 26 | INCLUDES := include 27 | DATA := data 28 | MUSIC := music 29 | 30 | #--------------------------------------------------------------------------------- 31 | # options for code generation 32 | #--------------------------------------------------------------------------------- 33 | ARCH := -mthumb -mthumb-interwork 34 | 35 | CFLAGS := -g -Wall -O2 -gdwarf-4\ 36 | -mcpu=arm7tdmi -mtune=arm7tdmi -fno-threadsafe-statics -fallow-store-data-races\ 37 | -ffast-math -ffunction-sections\ 38 | # -fdeclone-ctor-dtor\ 39 | # -funswitch-loops -fgcse-las -fgcse-after-reload -flive-range-shrinkage -fira-hoist-pressure\ 40 | # -fira-loop-pressure -ftree-partial-pre\ 41 | # -fno-align-functions -fno-align-jumps -fno-align-labels -fno-align-loops\ 42 | # -fno-schedule-insns\ 43 | # -finline-limit=5\ 44 | $(ARCH) 45 | 46 | CFLAGS += $(INCLUDE) 47 | 48 | CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=c++20 49 | 50 | ASFLAGS := -g $(ARCH) 51 | LDFLAGS = -g $(ARCH) -Wl,-Map,$(notdir $*.map) 52 | 53 | #--------------------------------------------------------------------------------- 54 | # any extra libraries we wish to link with the project 55 | #--------------------------------------------------------------------------------- 56 | LIBS := -lmm -lgba 57 | 58 | 59 | #--------------------------------------------------------------------------------- 60 | # list of directories containing libraries, this must be the top level containing 61 | # include and lib 62 | #--------------------------------------------------------------------------------- 63 | LIBDIRS := $(LIBGBA) 64 | 65 | #--------------------------------------------------------------------------------- 66 | # no real need to edit anything past this point unless you need to add additional 67 | # rules for different file extensions 68 | #--------------------------------------------------------------------------------- 69 | 70 | 71 | ifneq ($(BUILD),$(notdir $(CURDIR))) 72 | #--------------------------------------------------------------------------------- 73 | 74 | export OUTPUT := $(CURDIR)/$(TARGET) 75 | 76 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 77 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ 78 | $(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir)) 79 | 80 | export DEPSDIR := $(CURDIR)/$(BUILD) 81 | 82 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 83 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 84 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 85 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 86 | 87 | #ifneq ($(strip $(MUSIC)),) 88 | # export AUDIOFILES := $(foreach dir,$(notdir $(wildcard $(MUSIC)/*.*)),$(CURDIR)/$(MUSIC)/$(dir)) 89 | # BINFILES += soundbank.bin 90 | #endif 91 | 92 | #--------------------------------------------------------------------------------- 93 | # use CXX for linking C++ projects, CC for standard C 94 | #--------------------------------------------------------------------------------- 95 | ifeq ($(strip $(CPPFILES)),) 96 | #--------------------------------------------------------------------------------- 97 | export LD := $(CC) 98 | #--------------------------------------------------------------------------------- 99 | else 100 | #--------------------------------------------------------------------------------- 101 | export LD := $(CXX) 102 | #--------------------------------------------------------------------------------- 103 | endif 104 | #--------------------------------------------------------------------------------- 105 | 106 | export OFILES_BIN := $(addsuffix .o,$(BINFILES)) 107 | 108 | export OFILES_SOURCES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 109 | 110 | export OFILES := $(OFILES_BIN) $(OFILES_SOURCES) 111 | 112 | export HFILES := $(addsuffix .h,$(subst .,_,$(BINFILES))) 113 | 114 | export INCLUDE := $(foreach dir,$(INCLUDES),-iquote $(CURDIR)/$(dir)) \ 115 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 116 | -I$(CURDIR)/$(BUILD) 117 | 118 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 119 | 120 | .PHONY: $(BUILD) clean 121 | 122 | #--------------------------------------------------------------------------------- 123 | $(BUILD): 124 | @[ -d $@ ] || mkdir -p $@ 125 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 126 | 127 | #--------------------------------------------------------------------------------- 128 | clean: 129 | @echo clean ... 130 | @rm -fr $(BUILD) $(TARGET).elf $(TARGET).gba 131 | 132 | 133 | #--------------------------------------------------------------------------------- 134 | else 135 | 136 | #--------------------------------------------------------------------------------- 137 | # main targets 138 | #--------------------------------------------------------------------------------- 139 | 140 | $(OUTPUT).gba : $(OUTPUT).elf 141 | 142 | $(OUTPUT).elf : $(OFILES) 143 | 144 | $(OFILES_SOURCES) : $(HFILES) 145 | 146 | #--------------------------------------------------------------------------------- 147 | # The bin2o rule should be copied and modified 148 | # for each extension used in the data directories 149 | #--------------------------------------------------------------------------------- 150 | 151 | #--------------------------------------------------------------------------------- 152 | # rule to build soundbank from music files 153 | #--------------------------------------------------------------------------------- 154 | #soundbank.bin soundbank.h : $(AUDIOFILES) 155 | #--------------------------------------------------------------------------------- 156 | # @mmutil $^ -osoundbank.bin -hsoundbank.h 157 | 158 | #--------------------------------------------------------------------------------- 159 | # This rule links in binary data with the .wad extension 160 | #--------------------------------------------------------------------------------- 161 | %.bin.o %_bin.h : %.bin 162 | #--------------------------------------------------------------------------------- 163 | @echo $(notdir $<) 164 | @$(bin2o) 165 | 166 | 167 | -include $(DEPSDIR)/*.d 168 | #--------------------------------------------------------------------------------------- 169 | endif 170 | #--------------------------------------------------------------------------------------- 171 | -------------------------------------------------------------------------------- /3dmaths/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H 2 | #define UTILS_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "fp.h" 10 | #include "recip.h" 11 | 12 | #ifdef __arm__ 13 | #include 14 | #include 15 | #endif 16 | 17 | #define M_PI 3.14159265358979323846 // pi 18 | 19 | 20 | namespace P3D 21 | { 22 | template 23 | constexpr inline T pD2R(const T degrees) 24 | { 25 | return (degrees * T(M_PI)) / T(180); 26 | } 27 | 28 | template 29 | constexpr inline T pLerp(const T a, const T b, const T frac) 30 | { 31 | return a + frac * (b - a); 32 | } 33 | 34 | template 35 | constexpr inline int pRound(const T val) 36 | { 37 | return (int)(val + T(0.5)); 38 | } 39 | 40 | template 41 | constexpr FP pRound(const FP val) 42 | { 43 | return val + (FP(1) >> 1); 44 | } 45 | 46 | template 47 | constexpr inline T pASL(const T val, const int shift) 48 | { 49 | return val * (1 << shift); 50 | } 51 | 52 | template 53 | constexpr inline FP pASL(const FP val, const int shift) 54 | { 55 | return val << shift; 56 | } 57 | 58 | template 59 | constexpr inline T pASR(const T val, const int shift) 60 | { 61 | return val / (1 << shift); 62 | } 63 | 64 | template 65 | constexpr inline FP pASR(const FP val, const int shift) 66 | { 67 | return val >> shift; 68 | } 69 | 70 | template 71 | constexpr inline T pCeil(const T val) 72 | { 73 | return std::ceil(val); 74 | } 75 | 76 | template 77 | constexpr inline FP pCeil(const FP val) 78 | { 79 | constexpr unsigned int fpbits = (FP(1).toFPInt()) - 1; 80 | 81 | return (val.toFPInt() + fpbits) >> fracbits; 82 | } 83 | 84 | template 85 | constexpr inline T pAbs(const T v) 86 | { 87 | if(v >= T(0)) 88 | return v; 89 | 90 | return -v; 91 | } 92 | 93 | template 94 | constexpr inline T pMin(const T a, const T b) 95 | { 96 | return a < b ? a : b; 97 | } 98 | 99 | template 100 | constexpr inline T pMax(const T a, const T b) 101 | { 102 | return a > b ? a : b; 103 | } 104 | 105 | template 106 | constexpr inline T pClamp(const T min, const T v, const T max) 107 | { 108 | return pMin(pMax(min, v), max); 109 | } 110 | 111 | template 112 | constexpr inline bool pAllLTZ3(const T a, const T b, const T c) 113 | { 114 | return (a < 0) && (b < 0) && (c < 0); 115 | } 116 | 117 | template 118 | constexpr inline bool pAllLTZ3(const FP a, const FP b, const FP c) 119 | { 120 | return (a & b & c) < 0; 121 | } 122 | 123 | template 124 | constexpr inline bool pAllGTEqZ3(const T a, const T b, const T c) 125 | { 126 | return (a >= 0) && (b >= 0) && (c >= 0); 127 | } 128 | 129 | template 130 | constexpr inline bool pAllGTEqZ3(const FP a, const FP b, const FP c) 131 | { 132 | return (a | b | c) >= 0; 133 | } 134 | 135 | template 136 | constexpr inline bool pSameSignBit(const T a, const T b) 137 | { 138 | return (std::signbit(a) == std::signbit(b)); 139 | } 140 | 141 | template 142 | constexpr inline bool pSameSignBit(const FP a, const FP b) 143 | { 144 | return (a ^ b) >= 0; 145 | } 146 | 147 | inline void FastCopy32(void* dest, const void* src, const unsigned int len) 148 | { 149 | #ifdef __arm__ 150 | const int words = len >> 2; 151 | 152 | DMA3COPY(src, dest, DMA_DST_INC | DMA_SRC_INC | DMA32 | DMA_IMMEDIATE | words) 153 | #else 154 | memcpy(dest, src, len & 0xfffffffc); 155 | #endif 156 | } 157 | 158 | inline void FastCopy16(void* dest, const void* src, const unsigned int len) 159 | { 160 | #ifdef __arm__ 161 | const int words = len >> 1; 162 | 163 | DMA3COPY(src, dest, DMA_DST_INC | DMA_SRC_INC | DMA16 | DMA_IMMEDIATE | words) 164 | #else 165 | memcpy(dest, src, len & 0xfffffffe); 166 | #endif 167 | } 168 | 169 | inline void FastFill32(unsigned int* dest, const unsigned int value, unsigned int words) 170 | { 171 | #ifndef __arm__ 172 | while(words--) 173 | { 174 | *dest++ = value; 175 | } 176 | #else 177 | volatile unsigned int v = value; 178 | 179 | DMA3COPY(&v, dest, DMA_SRC_FIXED | DMA_DST_INC | DMA32 | DMA_IMMEDIATE | words) 180 | #endif 181 | } 182 | 183 | inline void FastFill16(unsigned short* dest, const unsigned short value, unsigned int words) 184 | { 185 | #ifndef __arm__ 186 | std::wmemset((wchar_t*)dest, value, words); 187 | #else 188 | volatile unsigned short v = value; 189 | 190 | DMA3COPY(&v, dest, DMA_SRC_FIXED | DMA_DST_INC | DMA16 | DMA_IMMEDIATE | words) 191 | #endif 192 | } 193 | 194 | template 195 | constexpr inline T pReciprocal(const T val) 196 | { 197 | return T(1)/val; 198 | } 199 | 200 | template 201 | constexpr inline FP pReciprocal(const FP v) 202 | { 203 | FP val = v < 0 ? -v : v; 204 | 205 | int shift = 0; 206 | 207 | if constexpr(fracbits < 16) 208 | shift = (16 - fracbits); 209 | else if constexpr(fracbits > 16) 210 | shift = (fracbits - 16); 211 | 212 | while(val > 4) 213 | { 214 | val >>= 1; 215 | shift++; 216 | } 217 | 218 | unsigned int recip = reciprocalTable[val.toFPInt()]; 219 | 220 | if(shift < 0) 221 | recip <<= -shift; 222 | else if(shift > 0) 223 | recip >>= shift; 224 | 225 | FP result = FP::fromFPInt(recip); 226 | 227 | return v < 0 ? -result : result; 228 | } 229 | 230 | template<> 231 | constexpr inline FP16 pReciprocal(const FP16 v) 232 | { 233 | const FP16 val = v < 0 ? -v : v; 234 | 235 | const unsigned int shift = shiftTable[val.i()]; 236 | 237 | FP16 result = FP16::fromFPInt(reciprocalTable[val.toFPInt() >> shift] >> shift); 238 | 239 | if(shift) 240 | { 241 | //Newton-Raphson refinement. 242 | //result = (result * (FP16(2) - (val * result))); 243 | //result = (result * (FP16(2) - (val * result))); 244 | } 245 | 246 | return v < 0 ? -result : result; 247 | } 248 | 249 | //Approx fixed point divide of a/b using reciprocal. -> a * (1/b). 250 | template 251 | constexpr inline T pApproxDiv(const T a, const T b) 252 | { 253 | return a * pReciprocal(b); 254 | } 255 | 256 | template 257 | constexpr inline T pScaledReciprocal(const unsigned int shift, const T val) 258 | { 259 | return pReciprocal(pASR(val, shift)); 260 | } 261 | 262 | template 263 | constexpr inline T pExp(const T x) 264 | { 265 | return T(1) + x * x * x; 266 | } 267 | 268 | 269 | 270 | //Double width type. double_width -> uint32. 271 | template struct double_width; 272 | template using double_width_t = typename double_width::type; 273 | 274 | template struct t { using type = T; }; 275 | 276 | template <> struct double_width : t {}; 277 | template <> struct double_width : t {}; 278 | template <> struct double_width : t {}; 279 | 280 | template <> struct double_width : t {}; 281 | template <> struct double_width : t {}; 282 | template <> struct double_width : t {}; 283 | 284 | 285 | //Fast types. fast_int -> uint32 on ARM. 286 | template struct fast_int; 287 | template using fast_int_t = typename fast_int::type; 288 | 289 | template struct f { using type = T; }; 290 | 291 | template <> struct fast_int : t {}; 292 | template <> struct fast_int : t {}; 293 | template <> struct fast_int : t {}; 294 | template <> struct fast_int : t {}; 295 | 296 | template <> struct fast_int : t {}; 297 | template <> struct fast_int : t {}; 298 | template <> struct fast_int : t {}; 299 | template <> struct fast_int : t {}; 300 | 301 | } 302 | 303 | #endif // UTILS_H 304 | -------------------------------------------------------------------------------- /3dmaths/fp.h: -------------------------------------------------------------------------------- 1 | #ifndef FP_H 2 | #define FP_H 3 | 4 | #include 5 | #include "divide.h" 6 | 7 | //#define OVERFLOW_CHECK 8 | 9 | #ifdef OVERFLOW_CHECK 10 | #include 11 | #endif 12 | 13 | namespace P3D 14 | { 15 | template class FP 16 | { 17 | public: 18 | constexpr FP() {} 19 | 20 | constexpr FP(const FP& r) : n(r.n) {} 21 | constexpr FP(const int v) : n(v << fracbits) {} 22 | constexpr FP(const unsigned int v) : n(v << fracbits) {} 23 | constexpr FP(const float v) : n((int)(v * one)) {} 24 | constexpr FP(const double v): n((int)(v * one)) {} 25 | 26 | 27 | 28 | constexpr operator int() const {return i();} 29 | constexpr operator unsigned int() const {return i();} 30 | constexpr operator float() const {return f();} 31 | 32 | constexpr int i() const {return n >> fracbits;} 33 | constexpr float f() const {return (float)n / one;} 34 | 35 | static constexpr FP fromFPInt(const int r) {FP v(0); v.n = r; return v;} 36 | constexpr int toFPInt() const {return n;} 37 | 38 | constexpr int intMul(const int r) const {return ((long long int)n * r) >> fracbits;} 39 | 40 | constexpr static int max() 41 | { 42 | return (std::numeric_limits::max() >> fracbits); 43 | } 44 | 45 | constexpr static int min() 46 | { 47 | return (std::numeric_limits::min() >> fracbits); 48 | } 49 | 50 | constexpr FP& operator=(const FP& r) 51 | { 52 | n = r.n; return *this; 53 | } 54 | 55 | constexpr FP& operator=(const int r) 56 | { 57 | #ifdef OVERFLOW_CHECK 58 | 59 | if(r < min() || r > max()) 60 | { 61 | std::cout << "int assign overflow: " << r; 62 | } 63 | #endif 64 | n = (r << fracbits); return *this; 65 | } 66 | 67 | constexpr FP& operator=(const float r) 68 | { 69 | #ifdef OVERFLOW_CHECK 70 | 71 | if(r < min() || r > max()) 72 | { 73 | std::cout << "float assign overflow: " << r; 74 | } 75 | #endif 76 | 77 | n = (int)(r * one); return *this; 78 | } 79 | 80 | //Addition 81 | constexpr FP operator+(const FP& r) const {FP v(r); return v+=*this;} 82 | constexpr FP operator+(const int r) const {FP v(r); return v+=*this;} 83 | constexpr FP operator+(const float r) const {FP v(r); return v+=*this;} 84 | 85 | constexpr FP& operator+=(const FP& r) 86 | { 87 | #ifdef OVERFLOW_CHECK 88 | 89 | long long int tmp = r.n + n; 90 | 91 | if(tmp < std::numeric_limits::min() || tmp > std::numeric_limits::max()) 92 | { 93 | std::cout << "addition overflow: " << r.i() << this->i(); 94 | } 95 | #endif 96 | 97 | n += r.n; return *this; 98 | } 99 | 100 | constexpr FP& operator+=(const int r) {return *this+=FP(r);} 101 | constexpr FP& operator+=(const float r) {return *this+=FP(r);} 102 | 103 | //Subtraction 104 | constexpr FP operator-(const FP& r) const 105 | { 106 | 107 | #ifdef OVERFLOW_CHECK 108 | 109 | long long int tmp = n - r.n; 110 | 111 | if(tmp < std::numeric_limits::min() || tmp > std::numeric_limits::max()) 112 | { 113 | std::cout << "subtraction overflow: " << r.i() << this->i(); 114 | } 115 | #endif 116 | 117 | FP v(r); v.n = n - v.n; return v; 118 | } 119 | 120 | 121 | constexpr FP operator-(const int r) const {FP v(r); return *this-v;} 122 | constexpr FP operator-(const float r) const {FP v(r); return *this-v;} 123 | 124 | 125 | constexpr FP& operator-=(const FP& r) {n -= r.n; return *this;} 126 | constexpr FP& operator-=(const int& r) {return *this-=FP(r);} 127 | constexpr FP& operator-=(const float& r) {return *this-=FP(r);} 128 | 129 | constexpr FP operator-() const {FP r(0); r.n = -n; return r;} 130 | 131 | //Multiply 132 | constexpr FP operator*(const FP& r) const {FP v(r); return v*=*this;} 133 | constexpr FP operator*(const int r) const {FP v(r); return v*=*this;} 134 | constexpr FP operator*(const float r) const {FP v(r); return v*=*this;} 135 | 136 | constexpr FP& operator*=(const FP& r) 137 | { 138 | const long long int tmp = (((const long long int)n * r.n) >> fracbits); 139 | 140 | #ifdef OVERFLOW_CHECK 141 | 142 | if(tmp < std::numeric_limits::min() || tmp > std::numeric_limits::max()) 143 | { 144 | std::cout << "multiply overflow: " << r.i() << this->i() << r.i() * this->i(); 145 | } 146 | #endif 147 | 148 | n = (int)tmp; 149 | return *this; 150 | } 151 | 152 | constexpr FP& operator*=(const int& r) {return *this*=FP(r);} 153 | constexpr FP& operator*=(const float& r) {return *this*=FP(r);} 154 | 155 | //Divide 156 | constexpr FP operator/(const FP& r) const 157 | { 158 | FP v(r); v.n = FixedDiv(n, r.n, fracbits); 159 | return v; 160 | } 161 | 162 | constexpr FP operator/(const int r) const {FP v(r); return *this/v;} 163 | constexpr FP operator/(const float r) const {FP v(r); return *this/v;} 164 | 165 | constexpr FP& operator/=(const FP& r) 166 | { 167 | n = FixedDiv(n, r.n, fracbits); 168 | return *this; 169 | } 170 | 171 | constexpr FP& operator/=(const int& r) {return *this/=FP(r);} 172 | constexpr FP& operator/=(const float& r) {return *this/=FP(r);} 173 | 174 | //Equality 175 | constexpr bool operator==(const FP& r) const {return n == r.n;} 176 | constexpr bool operator==(const int& r) const {return *this==FP(r);} 177 | constexpr bool operator==(const float& r) const {return *this==FP(r);} 178 | 179 | //Inequality 180 | constexpr bool operator!=(const FP& r) const {return n != r.n;} 181 | constexpr bool operator!=(const int& r) const {return *this!=FP(r);} 182 | constexpr bool operator!=(const float& r) const {return *this!=FP(r);} 183 | 184 | //Less than 185 | constexpr bool operator<(const FP& r) const {return n < r.n;} 186 | constexpr bool operator<(const int& r) const {return *this(const FP& r) const {return n > r.n;} 191 | constexpr bool operator>(const int& r) const {return *this>FP(r);} 192 | constexpr bool operator>(const float& r) const {return *this>FP(r);} 193 | 194 | //Greater than= 195 | constexpr bool operator>=(const FP& r) const {return n >= r.n;} 196 | constexpr bool operator>=(const int& r) const {return *this>=FP(r);} 197 | constexpr bool operator>=(const float& r) const {return *this>=FP(r);} 198 | 199 | //Less than= 200 | constexpr bool operator<=(const FP& r) const {return n <= r.n;} 201 | constexpr bool operator<=(const int& r) const {return *this<=FP(r);} 202 | constexpr bool operator<=(const float& r) const {return *this<=FP(r);} 203 | 204 | //& 205 | constexpr FP operator&(const FP& r) const {FP v(r); v.n &= n; return v;} 206 | constexpr FP operator&(const int r) const {FP v(r); return v&=*this;} 207 | 208 | constexpr FP& operator&=(const FP& r) {n &= r.n; return *this;} 209 | constexpr FP& operator&=(const int& r) {return *this&=FP(r);} 210 | 211 | //| 212 | constexpr FP operator|(const FP& r) const {FP v(r); v.n |= n; return v;} 213 | constexpr FP operator|(const int r) const {FP v(r); return v|=*this;} 214 | constexpr FP& operator|=(const FP& r) {n |= r.n; return *this;} 215 | constexpr FP& operator|=(const int& r) {return *this|=FP(r);} 216 | 217 | //^ 218 | constexpr FP operator^(const FP& r) const {FP v(r); v.n ^= n; return v;} 219 | constexpr FP operator^(const int r) const {FP v(r); return v^=*this;} 220 | constexpr FP& operator^=(const FP& r) {n ^= r.n; return *this;} 221 | constexpr FP& operator^=(const int& r) {return *this^=FP(r);} 222 | 223 | //<< 224 | constexpr FP operator<<(const int r) const {FP v(*this); return v<<=r;} 225 | constexpr FP& operator<<=(const int r) {n <<= r; return *this;} 226 | 227 | //>> 228 | constexpr FP operator>>(const int r) const {FP v(*this); return v>>=r;} 229 | constexpr FP& operator>>=(const int r) {n >>= r; return *this;} 230 | 231 | private: 232 | int n; 233 | 234 | static constexpr int one = (1 << fracbits); 235 | }; 236 | 237 | typedef FP<16> FP16; //s + 15 + 16 (-32768 to +32767) (0.000015) 238 | typedef FP<8> FP8; //s + 23 + 8 (-8,388,608 to +8,388,608) (0.039) 239 | typedef FP<24> FP24; //s + 7 + 24 (-128 to +127) (0.0000000596) 240 | } 241 | 242 | template 243 | struct std::numeric_limits> : public std::numeric_limits 244 | { 245 | static constexpr bool is_specialized = true; 246 | static constexpr P3D::FP min() noexcept { return P3D::FP::fromFPInt(std::numeric_limits::min()); } 247 | static constexpr P3D::FP max() noexcept { return P3D::FP::fromFPInt(std::numeric_limits::max()); } 248 | static constexpr P3D::FP lowest() noexcept { return min(); } 249 | static constexpr P3D::FP epsilon() noexcept { return P3D::FP::fromFPInt(1); } 250 | }; 251 | 252 | #endif // FP_H 253 | -------------------------------------------------------------------------------- /object3d.cpp: -------------------------------------------------------------------------------- 1 | #include "Config.h" 2 | #include "object3d.h" 3 | 4 | #include "PixelShaderDefault.h" 5 | #include "PixelShaderGBA8.h" 6 | #include "qlogging.h" 7 | 8 | #include 9 | 10 | namespace P3D 11 | { 12 | Object3d::Object3d() 13 | { 14 | } 15 | 16 | bool Object3d::Setup(unsigned int screenWidth, unsigned int screenHeight, fp hFov, fp zNear, fp zFar, pixel *framebuffer) 17 | { 18 | //fp halfVFov = (aspect * hFov) / 2; 19 | 20 | fp halfVFov = 45; 21 | 22 | fp halfFrustrumWidth = zFar * std::tan((float)pD2R(halfVFov)); 23 | fp halfFrustrumHeight = zFar * std::tan((float)pD2R(pASR(hFov, 1))); 24 | 25 | frustrumPoints[0] = V3(-halfFrustrumWidth, -halfFrustrumHeight, -zFar); 26 | frustrumPoints[1] = V3(halfFrustrumWidth, halfFrustrumHeight, -zFar); 27 | frustrumPoints[2] = V3(-halfFrustrumWidth, halfFrustrumHeight, -zFar); 28 | frustrumPoints[3] = V3(halfFrustrumWidth, -halfFrustrumHeight, -zFar); 29 | 30 | P3D::RenderTarget* render_target = new P3D::RenderTarget(screenWidth, screenHeight, framebuffer); 31 | render_target->AttachZBuffer(); 32 | 33 | 34 | render_device = new P3D::RenderDevice(); 35 | 36 | render_device->SetRenderTarget(render_target); 37 | 38 | float aspectRatio = (float)screenWidth / (float)screenHeight; 39 | 40 | render_device->SetPerspective(hFov, aspectRatio, zNear, zFar); 41 | 42 | render_device->SetRenderFlags>(); 43 | //render_device->SetRenderFlags>(); 44 | //render_device->SetRenderFlags(); 45 | //render_device->SetRenderFlags>(); 46 | //render_device->SetRenderFlags(); 47 | 48 | #if 0 49 | render_device->SetFogMode(FogLinear); 50 | render_device->SetFogColor(0x799ED7); 51 | render_device->SetFogDepth(750, 1500); 52 | #else 53 | render_device->SetFogMode(FogExponential2); 54 | render_device->SetFogColor(0x799ED7); 55 | render_device->SetFogDensity(2); 56 | #endif 57 | 58 | if(sizeof(P3D::pixel) == 1) 59 | { 60 | render_device->SetFogLightMap(model->GetFogLightMap()); 61 | } 62 | 63 | //render_device->SetRenderFlags>(); 64 | 65 | return true; 66 | } 67 | 68 | V3& Object3d::CameraPos() 69 | { 70 | return cameraPos; 71 | } 72 | 73 | V3& Object3d::CameraAngle() 74 | { 75 | return cameraAngle; 76 | } 77 | 78 | void Object3d::UpdateFrustrumAABB() 79 | { 80 | viewFrustrumBB = AABB(); 81 | 82 | M4 camMatrix = render_device->GetMatrix().Inverted(); 83 | 84 | V4 t1 = camMatrix * frustrumPoints[0]; 85 | V4 t2 = camMatrix * frustrumPoints[1]; 86 | V4 t3 = camMatrix * frustrumPoints[2]; 87 | V4 t4 = camMatrix * frustrumPoints[3]; 88 | #if 1 89 | viewFrustrumBB.AddPoint(eyePos); 90 | 91 | viewFrustrumBB.AddPoint(V3(t1.x, t1.y, t1.z)); 92 | viewFrustrumBB.AddPoint(V3(t2.x, t2.y, t2.z)); 93 | viewFrustrumBB.AddPoint(V3(t3.x, t3.y, t3.z)); 94 | viewFrustrumBB.AddPoint(V3(t4.x, t4.y, t4.z)); 95 | #else 96 | fp bb_size = 100; 97 | viewFrustrumBB.AddPoint(cameraPos); 98 | viewFrustrumBB.x1 -= bb_size; 99 | viewFrustrumBB.x2 += bb_size; 100 | 101 | viewFrustrumBB.y1 -= bb_size; 102 | viewFrustrumBB.y2 += bb_size; 103 | 104 | viewFrustrumBB.z1 -= bb_size; 105 | viewFrustrumBB.z2 += bb_size; 106 | #endif 107 | 108 | V3 t[3]; 109 | 110 | t[0] = V3(t1.x, t1.y, t1.z); 111 | t[1] = V3(t2.x, t2.y, t2.z); 112 | t[2] = V3(t3.x, t3.y, t3.z); 113 | 114 | //render_device->DrawTriangle(t, nullptr); 115 | } 116 | 117 | void Object3d::DoCollisions() 118 | { 119 | const int bb_size = 100; 120 | AABB player_box(cameraPos, bb_size); 121 | 122 | std::vector tris; 123 | /* 124 | BspModelTriangle t; 125 | t.tri.verts[0].pos = V3(0,0,0); 126 | t.tri.verts[1].pos = V3(-100,0,0); 127 | t.tri.verts[2].pos = V3(0,0,-100); 128 | 129 | V3 p(-25, 10, -25); 130 | 131 | CheckCollision2(&t, p, 50); 132 | */ 133 | model->SortFrontToBack(cameraPos, player_box, tris, true); 134 | 135 | int collision_count = 0; 136 | 137 | for(int i = 0; i < tris.size(); i++) 138 | { 139 | if(CheckCollision(tris.at(i), cameraPos, 25)) 140 | { 141 | i = 0; 142 | collision_count++; 143 | 144 | if(collision_count < 10) 145 | continue; 146 | 147 | break; 148 | } 149 | } 150 | 151 | } 152 | 153 | bool Object3d::CheckCollision(const BspModelTriangle* tri, V3& point, const fp radius) 154 | { 155 | fp distance = tri->normal_plane.DistanceToPoint(point); 156 | 157 | //No collision 158 | if(distance < 0 || distance >= radius) 159 | return false; 160 | 161 | fp margin = -fp(radius / 4); 162 | 163 | 164 | if(tri->edge_plane_0_1.DistanceToPoint(point) < margin) 165 | return false; 166 | 167 | if(tri->edge_plane_1_2.DistanceToPoint(point) < margin) 168 | return false; 169 | 170 | if(tri->edge_plane_2_0.DistanceToPoint(point) < margin) 171 | return false; 172 | 173 | //Move in direction of normal. 174 | fp penetrationDepth = radius - distance; 175 | 176 | point += tri->normal_plane.Normal() * (penetrationDepth + fp(0.01)); 177 | 178 | return true; 179 | } 180 | 181 | void Object3d::RenderScene() 182 | { 183 | if(do_collisions) 184 | { 185 | cameraPos.y -= 5; 186 | DoCollisions(); 187 | } 188 | 189 | eyePos = cameraPos; 190 | eyePos.y += 25; 191 | 192 | render_device->ClearColor(backgroundColor); 193 | render_device->ClearDepth(1); 194 | 195 | render_device->PushMatrix(); 196 | 197 | render_device->RotateX(-cameraAngle.x); 198 | render_device->RotateY(-cameraAngle.y); 199 | render_device->RotateZ(-cameraAngle.z); 200 | 201 | render_device->Translate(V3(-eyePos.x, -(eyePos.y), -eyePos.z)); 202 | 203 | if(update_frustrum_bb) 204 | UpdateFrustrumAABB(); 205 | 206 | render_device->BeginFrame(); 207 | 208 | render_device->BeginDraw(update_frustrum_bb ? frustrumPlanes : nullptr); 209 | 210 | RenderBsp(); 211 | 212 | render_device->EndDraw(); 213 | 214 | render_device->EndFrame(); 215 | 216 | render_device->PopMatrix(); 217 | } 218 | 219 | void Object3d::RenderBsp() 220 | { 221 | if(model == nullptr) 222 | return; 223 | 224 | static std::vector tris; 225 | 226 | //model->SortBackToFront(eyePos, viewFrustrumBB, tris, true); 227 | model->SortBackToFront(eyePos, viewFrustrumBB, tris, true); 228 | 229 | for(unsigned int i = 0; i < tris.size(); i++) 230 | { 231 | const BspModelTriangle* tri = tris[i]; 232 | #if 1 233 | if(!frustrumPlanes[Left].TriangleIsFrontside(tri->tri.verts[0].pos, tri->tri.verts[1].pos, tri->tri.verts[2].pos)) 234 | continue; 235 | 236 | if(!frustrumPlanes[Right].TriangleIsFrontside(tri->tri.verts[0].pos, tri->tri.verts[1].pos, tri->tri.verts[2].pos)) 237 | continue; 238 | 239 | if(!frustrumPlanes[Top].TriangleIsFrontside(tri->tri.verts[0].pos, tri->tri.verts[1].pos, tri->tri.verts[2].pos)) 240 | continue; 241 | 242 | if(!frustrumPlanes[Bottom].TriangleIsFrontside(tri->tri.verts[0].pos, tri->tri.verts[1].pos, tri->tri.verts[2].pos)) 243 | continue; 244 | 245 | if(!frustrumPlanes[Near].TriangleIsFrontside(tri->tri.verts[0].pos, tri->tri.verts[1].pos, tri->tri.verts[2].pos)) 246 | continue; 247 | 248 | if(!frustrumPlanes[Far].TriangleIsFrontside(tri->tri.verts[0].pos, tri->tri.verts[1].pos, tri->tri.verts[2].pos)) 249 | continue; 250 | #endif 251 | const BspNodeTexture* ntex = model->GetTexture(tri->texture); 252 | 253 | V3 verts[3] = {tri->tri.verts[0].pos, tri->tri.verts[1].pos, tri->tri.verts[2].pos}; 254 | 255 | Material m; 256 | 257 | if(ntex) 258 | { 259 | m.type = Material::Texture; 260 | m.pixels = model->GetTexturePixels(ntex->texture_pixels_offset); 261 | 262 | //m.type = Material::Color; 263 | //m.color = *model->GetTexturePixels(ntex->texture_pixels_offset); 264 | 265 | fp light_levels[3] = {light_level, light_level, light_level}; 266 | 267 | V2 uvs[3] = {tri->tri.verts[0].uv, tri->tri.verts[1].uv, tri->tri.verts[2].uv}; 268 | 269 | render_device->SetMaterial(m); 270 | 271 | render_device->DrawTriangle(verts, uvs, light_levels); 272 | } 273 | else 274 | { 275 | m.color = tri->color; 276 | 277 | render_device->SetMaterial(m); 278 | 279 | render_device->DrawTriangle(verts); 280 | } 281 | } 282 | 283 | tris.clear(); 284 | } 285 | 286 | void Object3d::SetModel(const BspModel *model) 287 | { 288 | this->model = model; 289 | } 290 | 291 | void Object3d::SetBackgroundColor(pixel color) 292 | { 293 | backgroundColor = color; 294 | } 295 | 296 | void Object3d::SetFrameBuffer(pixel* buffer) 297 | { 298 | int w = render_target->GetWidth(); 299 | int h = render_target->GetHeight(); 300 | 301 | render_target->AttachColorBuffer(w, h, buffer); 302 | 303 | render_device->SetRenderTarget(render_target); 304 | } 305 | 306 | #ifdef RENDER_STATS 307 | const RenderStats& Object3d::GetRenderStats() 308 | { 309 | return render_device->GetRenderStats(); 310 | } 311 | #endif 312 | } 313 | -------------------------------------------------------------------------------- /P3DObj2Bsp/bspmodelexport.cpp: -------------------------------------------------------------------------------- 1 | #include "bspmodelexport.h" 2 | 3 | namespace Obj2Bsp 4 | { 5 | BspModelExport::BspModelExport() {} 6 | 7 | QByteArray BspModelExport::ExportBSPModel(Obj2Bsp::BspNode* root, Model3d *model) 8 | { 9 | QList nodeList; 10 | 11 | TraverseNodesRecursive(root, nodeList); 12 | 13 | QByteArray bytes; 14 | QBuffer buffer(&bytes); 15 | buffer.open(QIODevice::WriteOnly); 16 | 17 | QList textureList; 18 | QList triangleList; 19 | 20 | QList modelNodeList; 21 | QList modelTextureList; 22 | QList modelTriList; 23 | 24 | 25 | QByteArray texturePixels; 26 | 27 | 28 | for(int i = 0; i < nodeList.length(); i++) 29 | { 30 | //Sort nodes by texture to stop cache thrashing... 31 | std::sort(std::begin(nodeList[i]->front_tris), 32 | std::end(nodeList[i]->front_tris), 33 | [](const Obj2Bsp::BspTriangle* a, const Obj2Bsp::BspTriangle* b) -> bool {return a->texture > b->texture; }); 34 | 35 | std::sort(std::begin(nodeList[i]->back_tris), 36 | std::end(nodeList[i]->back_tris), 37 | [](const Obj2Bsp::BspTriangle* a, const Obj2Bsp::BspTriangle* b) -> bool {return a->texture > b->texture; }); 38 | 39 | 40 | 41 | P3D::BspModelNode bn; 42 | 43 | P3D::V3 normal = P3D::V3(nodeList[i]->plane.normal.x(), nodeList[i]->plane.normal.y(), nodeList[i]->plane.normal.z()); 44 | bn.plane = P3D::Plane(normal, nodeList[i]->plane.distance); 45 | 46 | bn.node_bb = P3D::AABB(nodeList[i]->node_bb.x1, nodeList[i]->node_bb.x2, 47 | nodeList[i]->node_bb.y1, nodeList[i]->node_bb.y2, 48 | nodeList[i]->node_bb.z1, nodeList[i]->node_bb.z2); 49 | 50 | bn.child_bb = P3D::AABB(nodeList[i]->child_node_bb.x1, nodeList[i]->child_node_bb.x2, 51 | nodeList[i]->child_node_bb.y1, nodeList[i]->child_node_bb.y2, 52 | nodeList[i]->child_node_bb.z1, nodeList[i]->child_node_bb.z2); 53 | 54 | bn.front_tris.count = nodeList[i]->front_tris.size() & 0xffff; 55 | bn.front_tris.offset = modelTriList.length(); 56 | 57 | for(unsigned int j = 0; j < nodeList[i]->front_tris.size(); j++) 58 | { 59 | P3D::BspModelTriangle bmt; 60 | 61 | nodeList[i]->front_tris[j]->ComputePlanes(); 62 | 63 | for(int v = 0; v < 3; v++) 64 | { 65 | bmt.tri.verts[v].pos.x = nodeList[i]->front_tris[j]->tri->verts[v].pos.x(); 66 | bmt.tri.verts[v].pos.y = nodeList[i]->front_tris[j]->tri->verts[v].pos.y(); 67 | bmt.tri.verts[v].pos.z = nodeList[i]->front_tris[j]->tri->verts[v].pos.z(); 68 | bmt.tri.verts[v].uv.x = nodeList[i]->front_tris[j]->tri->verts[v].uv.x(); 69 | bmt.tri.verts[v].uv.y = nodeList[i]->front_tris[j]->tri->verts[v].uv.y(); 70 | bmt.tri.verts[v].vertex_id = nodeList[i]->front_tris[j]->tri->verts[v].vertex_id; 71 | } 72 | 73 | bmt.color = nodeList[i]->front_tris[j]->color; 74 | bmt.texture = -1; 75 | 76 | P3D::V3 normal = P3D::V3(nodeList[i]->front_tris[j]->normal_plane.normal.x(), 77 | nodeList[i]->front_tris[j]->normal_plane.normal.y(), 78 | nodeList[i]->front_tris[j]->normal_plane.normal.z()); 79 | bmt.normal_plane = P3D::Plane(normal, nodeList[i]->front_tris[j]->normal_plane.distance); 80 | 81 | normal = P3D::V3( nodeList[i]->front_tris[j]->edge_plane_01.normal.x(), 82 | nodeList[i]->front_tris[j]->edge_plane_01.normal.y(), 83 | nodeList[i]->front_tris[j]->edge_plane_01.normal.z()); 84 | bmt.edge_plane_0_1 = P3D::Plane(normal, nodeList[i]->front_tris[j]->edge_plane_01.distance); 85 | 86 | normal = P3D::V3( nodeList[i]->front_tris[j]->edge_plane_12.normal.x(), 87 | nodeList[i]->front_tris[j]->edge_plane_12.normal.y(), 88 | nodeList[i]->front_tris[j]->edge_plane_12.normal.z()); 89 | bmt.edge_plane_1_2 = P3D::Plane(normal, nodeList[i]->front_tris[j]->edge_plane_12.distance); 90 | 91 | normal = P3D::V3( nodeList[i]->front_tris[j]->edge_plane_20.normal.x(), 92 | nodeList[i]->front_tris[j]->edge_plane_20.normal.y(), 93 | nodeList[i]->front_tris[j]->edge_plane_20.normal.z()); 94 | bmt.edge_plane_2_0 = P3D::Plane(normal, nodeList[i]->front_tris[j]->edge_plane_20.distance); 95 | 96 | bmt.tri_bb.AddTriangle(bmt.tri.verts[0].pos, bmt.tri.verts[1].pos, bmt.tri.verts[2].pos); 97 | 98 | const Texture* tex = nodeList[i]->front_tris[j]->texture; 99 | 100 | if(tex) 101 | { 102 | if(textureList.contains(tex)) 103 | { 104 | bmt.texture = textureList.indexOf(tex); 105 | } 106 | else 107 | { 108 | P3D::BspNodeTexture bnt; 109 | bnt.alpha = nodeList[i]->front_tris[j]->texture->alpha; 110 | bnt.width = nodeList[i]->front_tris[j]->texture->width; 111 | bnt.height = nodeList[i]->front_tris[j]->texture->height; 112 | bnt.texture_pixels_offset = texturePixels.length() / sizeof(P3D::pixel); 113 | 114 | texturePixels.append(nodeList[i]->front_tris[j]->texture->pixels); 115 | 116 | bmt.texture = modelTextureList.length(); 117 | 118 | modelTextureList.append(bnt); 119 | textureList.append(tex); 120 | } 121 | } 122 | 123 | modelTriList.append(bmt); 124 | } 125 | 126 | 127 | 128 | bn.back_tris.count = nodeList[i]->back_tris.size() & 0xffff; 129 | bn.back_tris.offset = modelTriList.length(); 130 | 131 | for(unsigned int j = 0; j < nodeList[i]->back_tris.size(); j++) 132 | { 133 | P3D::BspModelTriangle bmt; 134 | 135 | nodeList[i]->back_tris[j]->ComputePlanes(); 136 | 137 | for(int v = 0; v < 3; v++) 138 | { 139 | bmt.tri.verts[v].pos.x = nodeList[i]->back_tris[j]->tri->verts[v].pos.x(); 140 | bmt.tri.verts[v].pos.y = nodeList[i]->back_tris[j]->tri->verts[v].pos.y(); 141 | bmt.tri.verts[v].pos.z = nodeList[i]->back_tris[j]->tri->verts[v].pos.z(); 142 | bmt.tri.verts[v].uv.x = nodeList[i]->back_tris[j]->tri->verts[v].uv.x(); 143 | bmt.tri.verts[v].uv.y = nodeList[i]->back_tris[j]->tri->verts[v].uv.y(); 144 | bmt.tri.verts[v].vertex_id = nodeList[i]->back_tris[j]->tri->verts[v].vertex_id; 145 | } 146 | 147 | bmt.color = nodeList[i]->back_tris[j]->color; 148 | bmt.texture = -1; 149 | 150 | P3D::V3 normal = P3D::V3(nodeList[i]->back_tris[j]->normal_plane.normal.x(), 151 | nodeList[i]->back_tris[j]->normal_plane.normal.y(), 152 | nodeList[i]->back_tris[j]->normal_plane.normal.z()); 153 | bmt.normal_plane = P3D::Plane(normal, nodeList[i]->back_tris[j]->normal_plane.distance); 154 | 155 | normal = P3D::V3( nodeList[i]->back_tris[j]->edge_plane_01.normal.x(), 156 | nodeList[i]->back_tris[j]->edge_plane_01.normal.y(), 157 | nodeList[i]->back_tris[j]->edge_plane_01.normal.z()); 158 | bmt.edge_plane_0_1 = P3D::Plane(normal, nodeList[i]->back_tris[j]->edge_plane_01.distance); 159 | 160 | normal = P3D::V3( nodeList[i]->back_tris[j]->edge_plane_12.normal.x(), 161 | nodeList[i]->back_tris[j]->edge_plane_12.normal.y(), 162 | nodeList[i]->back_tris[j]->edge_plane_12.normal.z()); 163 | bmt.edge_plane_1_2 = P3D::Plane(normal, nodeList[i]->back_tris[j]->edge_plane_12.distance); 164 | 165 | normal = P3D::V3( nodeList[i]->back_tris[j]->edge_plane_20.normal.x(), 166 | nodeList[i]->back_tris[j]->edge_plane_20.normal.y(), 167 | nodeList[i]->back_tris[j]->edge_plane_20.normal.z()); 168 | bmt.edge_plane_2_0 = P3D::Plane(normal, nodeList[i]->back_tris[j]->edge_plane_20.distance); 169 | 170 | bmt.tri_bb.AddTriangle(bmt.tri.verts[0].pos, bmt.tri.verts[1].pos, bmt.tri.verts[2].pos); 171 | 172 | const Texture* tex = nodeList[i]->back_tris[j]->texture; 173 | 174 | if(tex) 175 | { 176 | if(textureList.contains(tex)) 177 | { 178 | bmt.texture = textureList.indexOf(tex); 179 | } 180 | else 181 | { 182 | P3D::BspNodeTexture bnt; 183 | bnt.alpha = nodeList[i]->back_tris[j]->texture->alpha; 184 | bnt.width = nodeList[i]->back_tris[j]->texture->width; 185 | bnt.height = nodeList[i]->back_tris[j]->texture->height; 186 | bnt.texture_pixels_offset = texturePixels.length() / sizeof(P3D::pixel); 187 | 188 | texturePixels.append(nodeList[i]->back_tris[j]->texture->pixels); 189 | 190 | bmt.texture = modelTextureList.length(); 191 | 192 | modelTextureList.append(bnt); 193 | textureList.append(tex); 194 | } 195 | } 196 | 197 | modelTriList.append(bmt); 198 | } 199 | 200 | 201 | 202 | bn.front_node = 0; 203 | bn.back_node = 0; 204 | bn.parent_node = 0; 205 | 206 | if(nodeList[i]->front) 207 | { 208 | bn.front_node = nodeList.indexOf(nodeList[i]->front); 209 | } 210 | 211 | if(nodeList[i]->back) 212 | { 213 | bn.back_node = nodeList.indexOf(nodeList[i]->back); 214 | } 215 | 216 | if(nodeList[i]->parent) 217 | bn.parent_node = nodeList.indexOf(nodeList[i]->parent); 218 | else 219 | bn.parent_node = -1; 220 | 221 | modelNodeList.append(bn); 222 | } 223 | 224 | P3D::BspModelHeader bmh; 225 | 226 | buffer.write((const char*)&bmh, sizeof(bmh)); 227 | 228 | bmh.node_count = modelNodeList.length(); 229 | bmh.node_offset = buffer.pos(); 230 | 231 | for(int i = 0; i < modelNodeList.length(); i++) 232 | { 233 | buffer.write((const char*)&modelNodeList[i], sizeof(modelNodeList[i])); 234 | } 235 | 236 | bmh.triangle_count = modelTriList.length(); 237 | bmh.triangle_offset = buffer.pos(); 238 | 239 | for(int i = 0; i < modelTriList.length(); i++) 240 | { 241 | buffer.write((const char*)&modelTriList[i], sizeof(modelTriList[i])); 242 | } 243 | 244 | bmh.texture_count = modelTextureList.length(); 245 | bmh.texture_offset = buffer.pos(); 246 | 247 | for(int i = 0; i < modelTextureList.length(); i++) 248 | { 249 | buffer.write((const char*)&modelTextureList[i], sizeof(modelTextureList[i])); 250 | } 251 | 252 | bmh.texture_pixels_offset = buffer.pos(); 253 | buffer.write(texturePixels.data(), texturePixels.length()); 254 | 255 | bmh.texture_palette_offset = buffer.pos(); 256 | buffer.write((const char*)model->colormap, 256 * 4); 257 | 258 | bmh.fog_lightmap_offset = buffer.pos(); 259 | buffer.write((const char*)model->foglightmap, 256 * FOG_LEVELS * LIGHT_LEVELS); 260 | 261 | //Now overwrite the header with offsets. 262 | P3D::BspModelHeader* hdr = (P3D::BspModelHeader*)bytes.data(); 263 | 264 | *hdr = bmh; 265 | 266 | return bytes; 267 | } 268 | 269 | void BspModelExport::TraverseNodesRecursive(BspNode* n, QList& nodeList) 270 | { 271 | if (!n) return; 272 | 273 | nodeList.append(n); 274 | 275 | TraverseNodesRecursive(n->front, nodeList); 276 | TraverseNodesRecursive(n->back, nodeList); 277 | } 278 | } 279 | 280 | -------------------------------------------------------------------------------- /RenderDevice.h: -------------------------------------------------------------------------------- 1 | #ifndef RENDERDEVICE_H 2 | #define RENDERDEVICE_H 3 | 4 | #include 5 | #include "Config.h" 6 | 7 | #include "RenderCommon.h" 8 | #include "RenderTarget.h" 9 | #include "RenderTriangle.h" 10 | #include "TextureCache.h" 11 | #include "PixelShaderDefault.h" 12 | 13 | namespace P3D 14 | { 15 | class RenderDevice 16 | { 17 | public: 18 | explicit RenderDevice() 19 | { 20 | texture_cache = new TextureCacheDefault(); 21 | 22 | current_material = new Material(); 23 | 24 | //Populate matrix stack with 1 identity matrix. 25 | model_view_matrix_stack.push_back(M4()); 26 | LoadIdentity(); 27 | } 28 | 29 | ~RenderDevice() {} 30 | 31 | //Render Target 32 | void SetRenderTarget(const RenderTarget *target) 33 | { 34 | render_target = target; 35 | 36 | SetViewport(0, 0, render_target->GetWidth(), render_target->GetHeight()); 37 | } 38 | 39 | void SetViewport(unsigned int x, unsigned int y, unsigned int width, unsigned int height) 40 | { 41 | if(((x + width) > render_target->GetWidth()) || (y + height > render_target->GetHeight())) 42 | { 43 | x = 0; 44 | y = 0; 45 | width = render_target->GetWidth(); 46 | height = render_target->GetHeight(); 47 | } 48 | 49 | viewport.width = width; 50 | viewport.height = height; 51 | 52 | viewport.start = &render_target->GetColorBuffer()[(y * render_target->GetColorBufferYPitch()) + x]; 53 | viewport.y_pitch = render_target->GetColorBufferYPitch(); 54 | 55 | if(render_target->GetZBuffer()) 56 | { 57 | viewport.z_start = &render_target->GetZBuffer()[(y * render_target->GetZBufferYPitch()) + x]; 58 | viewport.z_y_pitch = render_target->GetZBufferYPitch(); 59 | } 60 | else 61 | { 62 | viewport.z_start = nullptr; 63 | viewport.z_y_pitch = 0; 64 | } 65 | 66 | if(triangle_render) 67 | { 68 | triangle_render->SetRenderStateViewport(viewport); 69 | } 70 | } 71 | 72 | template> void SetRenderFlags() 73 | { 74 | if(triangle_render) 75 | delete triangle_render; 76 | 77 | triangle_render = new P3D::Internal::RenderTriangle(); 78 | 79 | triangle_render->SetRenderStateViewport(viewport); 80 | triangle_render->SetZPlanes(z_planes); 81 | triangle_render->SetTextureCache(texture_cache); 82 | triangle_render->SetFogParams(fog_params); 83 | 84 | #ifdef RENDER_STATS 85 | triangle_render->SetRenderStats(render_stats); 86 | #endif 87 | } 88 | 89 | //Matrix 90 | void SetPerspective(const fp vertical_fov, const fp aspect_ratio, const fp z_near, const fp z_far) 91 | { 92 | projection_matrix.perspective(vertical_fov, aspect_ratio, z_near, z_far); 93 | 94 | z_planes.z_near = z_near; 95 | z_planes.z_far = z_far; 96 | 97 | z_planes.z_ratio_1 = z_far / (z_far - z_near); 98 | 99 | int zn = z_near; 100 | int zf = z_far; 101 | 102 | int zi = (-zf * zn); 103 | int zj = (zf - zn); 104 | 105 | //z_planes.z_ratio_2 = fp::fromFPInt(FixedDiv(zi, zj, 16)); 106 | z_planes.z_ratio_2 = zi / zj; 107 | 108 | z_planes.z_ratio_3 = pReciprocal(z_far - z_near); 109 | 110 | if(triangle_render) 111 | { 112 | triangle_render->SetZPlanes(z_planes); 113 | } 114 | } 115 | 116 | void SetOrthographic(const fp left, const fp right, const fp bottom, const fp top, const fp z_near, const fp z_far) 117 | { 118 | projection_matrix.orthographic(left, right, bottom, top, z_near, z_far); 119 | 120 | z_planes.z_near = z_near; 121 | z_planes.z_far = z_far; 122 | } 123 | 124 | unsigned int PushMatrix() 125 | { 126 | const M4 m = model_view_matrix_stack.back(); 127 | 128 | model_view_matrix_stack.push_back(m); 129 | 130 | return (unsigned int)model_view_matrix_stack.size(); 131 | } 132 | 133 | unsigned int PopMatrix() 134 | { 135 | if(model_view_matrix_stack.size() > 1) 136 | { 137 | model_view_matrix_stack.pop_back(); 138 | } 139 | 140 | return (unsigned int)model_view_matrix_stack.size(); 141 | } 142 | 143 | M4& GetMatrix() 144 | { 145 | return model_view_matrix_stack.back(); 146 | } 147 | 148 | void LoadMatrix(const M4 &matrix) 149 | { 150 | model_view_matrix_stack.pop_back(); 151 | 152 | model_view_matrix_stack.push_back(matrix); 153 | } 154 | 155 | void LoadIdentity() 156 | { 157 | model_view_matrix_stack.back().setToIdentity(); 158 | } 159 | 160 | void Translate(const V3& v) 161 | { 162 | model_view_matrix_stack.back().translate(v); 163 | } 164 | 165 | void RotateX(const fp angle) 166 | { 167 | model_view_matrix_stack.back().rotateX(angle); 168 | } 169 | 170 | void RotateY(const fp angle) 171 | { 172 | model_view_matrix_stack.back().rotateY(angle); 173 | } 174 | 175 | void RotateZ(const fp angle) 176 | { 177 | model_view_matrix_stack.back().rotateZ(angle); 178 | } 179 | 180 | 181 | //Clear 182 | void ClearColor(const pixel color) 183 | { 184 | unsigned int c32 = color; 185 | unsigned int shift = 0; 186 | 187 | if constexpr(sizeof(pixel) == 1) 188 | { 189 | c32 = (c32 << 24) | (c32 << 16) | (c32 << 8) | c32; 190 | shift = 2; 191 | } 192 | else if constexpr(sizeof(pixel) == 2) 193 | { 194 | c32 = (c32 << 16) | c32; 195 | shift = 1; 196 | } 197 | 198 | for(unsigned int y = 0; y < render_target->GetHeight(); y++) 199 | { 200 | pixel* p = &render_target->GetColorBuffer()[y * render_target->GetColorBufferYPitch()]; 201 | 202 | FastFill32((unsigned int*)p, c32, render_target->GetWidth() >> shift); 203 | } 204 | } 205 | 206 | void ClearDepth(const z_val depth) 207 | { 208 | for(unsigned int y = 0; y < render_target->GetHeight(); y++) 209 | { 210 | z_val* z = &render_target->GetZBuffer()[y * render_target->GetZBufferYPitch()]; 211 | 212 | for(unsigned int x = 0; x < render_target->GetWidth(); x++) 213 | { 214 | z[x] = depth; 215 | } 216 | } 217 | } 218 | 219 | void ClearViewportColor(const pixel color) 220 | { 221 | unsigned int c32 = color; 222 | unsigned int shift = 0; 223 | 224 | if constexpr(sizeof(pixel) == 1) 225 | { 226 | c32 = (c32 << 24) | (c32 << 16) | (c32 << 8) | c32; 227 | shift = 2; 228 | } 229 | else if constexpr(sizeof(pixel) == 2) 230 | { 231 | c32 = (c32 << 16) | c32; 232 | shift = 1; 233 | } 234 | 235 | for(unsigned int y = 0; y < viewport.height; y++) 236 | { 237 | unsigned int count = viewport.width; 238 | 239 | pixel* p = &viewport.start[y * viewport.y_pitch]; 240 | 241 | while(size_t(p) & 3) 242 | { 243 | *p = color; 244 | p++; 245 | count--; 246 | } 247 | 248 | FastFill32((unsigned int*)p, c32, count >> shift); 249 | 250 | while(count & 3) 251 | { 252 | *p++ = color; 253 | count--; 254 | } 255 | } 256 | } 257 | 258 | void ClearViewportDepth(const z_val depth) 259 | { 260 | for(unsigned int y = 0; y < viewport.height; y++) 261 | { 262 | z_val* z = &viewport.z_start[y * viewport.z_y_pitch]; 263 | 264 | for(unsigned int x = 0; x < viewport.width; x++) 265 | { 266 | z[x] = depth; 267 | } 268 | } 269 | } 270 | 271 | void SetFogColor(const pixel color) 272 | { 273 | fog_params.fog_color = color; 274 | } 275 | 276 | void SetFogMode(FogMode mode) 277 | { 278 | fog_params.mode = mode; 279 | } 280 | 281 | void SetFogDepth(fp fog_start, fp fog_end) 282 | { 283 | fog_params.fog_start = fog_start; 284 | fog_params.fog_end = fog_end; 285 | } 286 | 287 | void SetFogDensity(fp density) 288 | { 289 | fog_params.fog_density = density; 290 | } 291 | 292 | //Begin/End Frame. 293 | void BeginFrame() 294 | { 295 | #ifdef RENDER_STATS 296 | render_stats.ResetToZero(); 297 | #endif 298 | } 299 | 300 | void EndFrame() {} 301 | 302 | void BeginDraw(Plane frustrumPlanes[6] = nullptr) 303 | { 304 | UpdateTransformMatrix(); 305 | 306 | if(frustrumPlanes) 307 | { 308 | transform_matrix.ExtractFrustrumPlanes(frustrumPlanes); 309 | } 310 | } 311 | 312 | void EndDraw() {} 313 | 314 | // 315 | void SetTextureCache(TextureCacheBase* cache) 316 | { 317 | if(texture_cache) 318 | delete texture_cache; 319 | 320 | texture_cache = cache; 321 | 322 | triangle_render->SetTextureCache(texture_cache); 323 | } 324 | 325 | void SetMaterial(const Material& material, const signed char importance = 0) 326 | { 327 | current_material = &material; 328 | 329 | if(material.type == Material::Texture) 330 | { 331 | texture_cache->AddTexture(material.pixels, importance); 332 | } 333 | } 334 | 335 | void SetFogLightMap(const unsigned char* colorMap) 336 | { 337 | triangle_render->SetFogLightMap(colorMap); 338 | } 339 | 340 | //Draw Objects. 341 | void DrawTriangle(const V3 vertexes[3], const V2 uvs[3] = nullptr, const fp light_levels[3] = nullptr) 342 | { 343 | TransformVertexes(vertexes, 3); 344 | 345 | const unsigned int indexes[3] = {0,1,2}; 346 | 347 | DrawTriangle(indexes, uvs, light_levels); 348 | } 349 | 350 | void TransformVertexes(const V3* vertexes, const unsigned int count) 351 | { 352 | if(transformed_vertexes_buffer_count < count) 353 | { 354 | delete[] transformed_vertexes; 355 | transformed_vertexes = new V4[count]; 356 | transformed_vertexes_buffer_count = count; 357 | } 358 | 359 | for(unsigned int i = 0; i < count; i++) 360 | { 361 | transformed_vertexes[i] = transform_matrix * vertexes[i]; 362 | } 363 | 364 | #ifdef RENDER_STATS 365 | render_stats.vertex_transformed += count; 366 | #endif 367 | } 368 | 369 | void DrawTriangle(const unsigned int indexes[3], const V2 uvs[3] = nullptr, const fp light_levels[3] = nullptr) const 370 | { 371 | P3D::Internal::TransformedTriangle tri; 372 | 373 | tri.verts[0].pos = transformed_vertexes[indexes[0]]; 374 | tri.verts[1].pos = transformed_vertexes[indexes[1]]; 375 | tri.verts[2].pos = transformed_vertexes[indexes[2]]; 376 | 377 | if(current_material->type == Material::Texture) 378 | { 379 | tri.verts[0].uv = uvs[0]; 380 | tri.verts[1].uv = uvs[1]; 381 | tri.verts[2].uv = uvs[2]; 382 | } 383 | 384 | if(light_levels) 385 | { 386 | tri.verts[0].light_factor = pClamp(fp(0), fp(1) - light_levels[0], LIGHT_MAX); 387 | tri.verts[1].light_factor = pClamp(fp(0), fp(1) - light_levels[1], LIGHT_MAX); 388 | tri.verts[2].light_factor = pClamp(fp(0), fp(1) - light_levels[2], LIGHT_MAX); 389 | } 390 | 391 | triangle_render->DrawTriangle(tri, *current_material); 392 | } 393 | 394 | #ifdef RENDER_STATS 395 | const RenderStats& GetRenderStats() const 396 | { 397 | return render_stats; 398 | } 399 | #endif 400 | 401 | private: 402 | 403 | void UpdateTransformMatrix() 404 | { 405 | transform_matrix = projection_matrix * model_view_matrix_stack.back(); 406 | 407 | for(int i = model_view_matrix_stack.size() -2; i >= 0; i--) 408 | { 409 | transform_matrix *= model_view_matrix_stack[i]; 410 | } 411 | } 412 | 413 | const RenderTarget* render_target = nullptr; 414 | P3D::Internal::RenderTargetViewport viewport; 415 | P3D::Internal::RenderDeviceNearFarPlanes z_planes; 416 | P3D::Internal::RenderDeviceFogParameters fog_params; 417 | 418 | //Matrixes. 419 | M4 projection_matrix; 420 | M4 transform_matrix; //P*V*Stack 421 | std::vector> model_view_matrix_stack; 422 | 423 | V4* transformed_vertexes = nullptr; 424 | unsigned int transformed_vertexes_buffer_count = 0; 425 | 426 | TextureCacheBase* texture_cache = nullptr; 427 | const Material* current_material = nullptr; 428 | 429 | P3D::Internal::RenderTriangleBase* triangle_render = nullptr; 430 | 431 | #ifdef RENDER_STATS 432 | RenderStats render_stats; 433 | #endif 434 | }; 435 | }; 436 | #endif // RENDERDEVICE_H 437 | --------------------------------------------------------------------------------