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 }