1 module isodi.tests;
2 
3 import isodi;
4 import raylib;
5 import core.runtime;
6 
7 import std.file;
8 import std.stdio;
9 
10 
11 @system:
12 version (unittest):
13 
14 
15 // This never detects anything useful. Only false-positives.
16 private extern(C) __gshared string[] rt_options = ["oncycle=ignore"];
17 
18 shared static this() {
19 
20     // Redirect to our main
21     Runtime.extendedModuleUnitTester = {
22 
23         // Run tests
24         UnitTestResult result;
25         foreach (m; ModuleInfo) {
26 
27             if (!m) continue;
28 
29             // If the module has unittests
30             if (m.unitTest) try {
31 
32                 // Run them
33                 ++result.executed;
34                 m.unitTest()();
35                 ++result.passed;
36 
37             }
38 
39             // Print exceptions
40             catch (Throwable e) writeln(e);
41 
42         }
43 
44         // Run main if all tests pass
45         result.runMain = result.executed == result.passed;
46 
47         // Print the results
48         result.summarize = true;
49 
50         return result;
51 
52     };
53 
54 }
55 
56 
57 void main() {
58 
59     Runtime.extendedModuleUnitTester = null;
60 
61     // Create the window
62     SetTraceLogLevel(TraceLogLevel.LOG_WARNING);
63     SetConfigFlags(ConfigFlags.FLAG_WINDOW_RESIZABLE);
64     InitWindow(1600, 900, "Isodi test runner");
65     SetWindowMinSize(800, 600);
66     SetTargetFPS(60);
67     scope (exit) CloseWindow();
68 
69     /// Prepare the camera
70     CameraController controller;
71     CameraKeybindings cameraKeys = {
72 
73         zoomIn:  KeyboardKey.KEY_EQUAL,
74         zoomOut: KeyboardKey.KEY_MINUS,
75 
76         moveForward:  KeyboardKey.KEY_W,
77         moveBackward: KeyboardKey.KEY_S,
78         moveLeft:     KeyboardKey.KEY_A,
79         moveRight:    KeyboardKey.KEY_D,
80         rotateLeft:   KeyboardKey.KEY_Q,
81         rotateRight:  KeyboardKey.KEY_E,
82         rotateUp:     KeyboardKey.KEY_R,
83         rotateDown:   KeyboardKey.KEY_F,
84 
85     };
86 
87     const grass = BlockType(0);
88 
89     // Load packs
90     auto pack = getPack("res/samerion-retro/pack.json");
91 
92     IsodiModel[] models;
93 
94     // Load chunks
95     foreach (map; "/home/soaku/git/samerion/server/resources/maps".dirEntries(SpanMode.shallow)) {
96         // Yes, I'm loading some chunks I have not uploaded to the repository
97         // They're legacy, so I'm gonna replace them with something new later on
98         // Contact me for the proper ones, ok? Not feeling like having them here!
99 
100         import std.array, std.algorithm;
101 
102         string[] declarations;
103 
104         // Load the chunk
105         auto chunk = pack.loadTilemap(cast(ubyte[]) map.read, declarations);
106         chunk.properties.transform = MatrixTranslate(0.5, 0, 0.5);
107 
108         // Build the model
109         auto texture = pack.blockTexture(declarations.sort.array, chunk.atlas);
110 
111         models ~= chunk.makeModel(texture);
112 
113     }
114 
115     // One more chunk for testing stuff
116     {
117 
118         Chunk chunk;
119         chunk.properties.transform = MatrixTranslate(0.5, 0, 0.5);
120         chunk.properties.tint = Vector4(0.8, 0.8, 0.8, 1);
121         chunk.atlas[grass] = pack.options(ResourceType.block, "grass").blockUV;
122 
123         chunk.addX(
124             grass,
125             BlockPosition(2, 2, 0, 5),  20, 22, 24, 26, 28, 30,
126             BlockPosition(2, 3, 0, 5),  20, 20, 20, 20, 20, 20,
127             BlockPosition(2, 4, 0, 35), 60, 62, 64, 66, 68, 70,
128         );
129 
130         auto texture = pack.blockTexture(["grass"], chunk.atlas);
131         models ~= chunk.makeModel(texture);
132 
133         BlockUV[BlockType] uv1, uv2;
134         assert(pack.blockTexture(["grass"], uv1) == pack.blockTexture(["grass"], uv2));
135         assert(uv1 == uv2);
136 
137     }
138 
139     Texture2D defaultPoseTexture, advancedPoseTexture;
140     scope (exit) {
141         UnloadTexture(defaultPoseTexture);
142         UnloadTexture(advancedPoseTexture);
143     }
144 
145     // Load a skeleton
146     Skeleton skeleton;
147     {
148 
149         const model = "white-wraith";
150 
151         // Load the skeleton
152         skeleton.bones = pack.skeleton("humanoid", model);
153 
154         // Load the bone set texture
155         auto texture = pack.boneSetTexture([model], skeleton.atlas);
156 
157         // Create a matrix texture for the default pose
158         defaultPoseTexture = LoadTextureFromImage(skeleton.matrixImage);
159 
160         // Test instances for billboard behavior etc
161         foreach (i; 2..8) {
162 
163             skeleton.properties.transform = MatrixTranslate(i + 0.5, 2.0, 3.5);
164 
165             models ~= skeleton.makeModel(texture, defaultPoseTexture);
166 
167         }
168 
169         void mulbone(T...)(T matrices, ref Matrix matrix) {
170 
171             matrix = mul(matrices, matrix);
172 
173         }
174 
175         // Create a more advanced pose
176         foreach (i; 0..2) {
177 
178             const im = 4 + 3*i;
179             mulbone(MatrixRotateX(PI/5), MatrixRotateY(PI/4), skeleton.bones[im].transform);
180             mulbone(MatrixRotateZ(PI/2), skeleton.bones[im+1].transform);
181             mulbone(MatrixRotateX(PI/4), skeleton.bones[im+2].transform);
182 
183         }
184 
185         // Make a matrix for it
186         advancedPoseTexture = LoadTextureFromImage(skeleton.matrixImage);
187 
188         // Instance one with it
189         skeleton.properties.transform = MatrixTranslate(0.5, 0.2, 0.5);
190         models ~= skeleton.makeModel(texture, advancedPoseTexture);
191 
192     }
193 
194     auto buf = new Matrix[skeleton.bones.length];
195 
196     // Drawing loop
197     while (!WindowShouldClose) {
198 
199         BeginDrawing();
200         scope (exit) EndDrawing();
201 
202         ClearBackground(Colors.WHITE);
203 
204         // Update the camera
205         auto camera = controller.update(cameraKeys);
206 
207         {
208 
209             BeginMode3D(camera);
210             scope (exit) EndMode3D();
211 
212             DrawGrid(100, 1);
213 
214             // Draw each model
215             foreach (model; models) {
216 
217                 model.draw();
218 
219             }
220 
221             // Draw skeleton debug
222             skeleton.drawBoneLines(buf);
223             skeleton.drawBoneNormals(buf);
224 
225             // Draw spheres to show the terrain direction
226             DrawSphere(Vector3( 0, 0, -1), 0.2, Colors.GREEN);   // North
227             DrawSphere(Vector3(+1, 0,  0), 0.2, Colors.BLUE);    // East
228             DrawSphere(Vector3( 0, 0, +1), 0.2, Colors.PINK);    // South
229             DrawSphere(Vector3(-1, 0,  0), 0.2, Colors.YELLOW);  // West
230 
231         }
232 
233         DrawFPS(0, 0);
234 
235     }
236 
237 }