1 module pyramid; 2 3 import std.stdio; 4 import std.format; 5 import std.math; 6 7 import dlib.core.memory; 8 import dlib.container.array; 9 import dlib.math.vector; 10 import dlib.math.utils; 11 import dlib.image.color; 12 import dlib.geometry.ray; 13 import dlib.geometry.aabb; 14 15 import derelict.opengl.gl; 16 import derelict.opengl.glu; 17 import derelict.sdl.sdl; 18 19 import dgl.core.application; 20 import dgl.core.interfaces; 21 import dgl.core.event; 22 import dgl.graphics.shapes; 23 import dgl.graphics.material; 24 import dgl.graphics.tbcamera; 25 import dgl.graphics.axes; 26 import dgl.ui.font; 27 import dgl.ui.ftfont; 28 import dgl.ui.textline; 29 30 import dmech.world; 31 import dmech.geometry; 32 import dmech.rigidbody; 33 import dmech.shape; 34 35 import testbed.grid; 36 import testbed.physicsentity; 37 38 class TestApp: Application 39 { 40 TrackballCamera camera; 41 DynamicArray!Material materials; 42 DynamicArray!Drawable drawables; 43 Vector4f lightPosition; 44 45 Axes axes; 46 Grid grid; 47 ShapeBox sBox; 48 49 PhysicsWorld world; 50 GeomBox gFloor; 51 GeomBox gBox; 52 53 enum fixedTimeStep = 1.0 / 60.0; 54 55 Font font; 56 TextLine fpsText; 57 58 DynamicArray!PhysicsEntity entities; 59 PhysicsEntity selectedEntity; 60 61 float aspectRatio; 62 float fovY = 60; 63 float maxPickDistance = 1000.0f; 64 float dragDistance; 65 66 this() 67 { 68 super(800, 600, "dmech test"); 69 clearColor = Color4f(0.2f, 0.2f, 0.2f); 70 71 camera = New!TrackballCamera(); 72 camera.pitch(45.0f); 73 camera.turn(45.0f); 74 camera.setZoom(20.0f); 75 76 lightPosition = Vector4f(1, 1, 0.5, 0); 77 78 axes = New!Axes(); 79 grid = New!Grid(); 80 81 world = New!PhysicsWorld(1000); 82 83 gFloor = New!GeomBox(Vector3f(40, 1, 40)); 84 auto bFloor = world.addStaticBody(Vector3f(0, -1, 0)); 85 world.addShapeComponent(bFloor, gFloor, Vector3f(0, 0, 0), 1.0f); 86 87 gBox = New!GeomBox(Vector3f(1, 1, 1)); // Physical shape 88 sBox = New!ShapeBox(Vector3f(1, 1, 1)); // Graphical shape 89 90 buildPyramid(7); 91 92 font = New!FreeTypeFont("data/fonts/droid/DroidSans.ttf", 18); 93 94 fpsText = New!TextLine(font, "FPS: 0", Vector2f(8, 8)); 95 fpsText.color = Color4f(1, 1, 1); 96 } 97 98 void buildPyramid(uint pyramidSize) 99 { 100 float size = 1.0f; 101 102 float cubeHeight = 2.0f; 103 104 float width = size * 2.0f; 105 float height = cubeHeight; 106 float horizontal_spacing = 0.1f; 107 float veritcal_spacing = 0.1f; 108 109 foreach(i; 0..pyramidSize) 110 foreach(e; i..pyramidSize) 111 { 112 auto position = Vector3f( 113 (e - i * 0.5f) * (width + horizontal_spacing) - ((width + horizontal_spacing) * 5), 114 6.0f + (height + veritcal_spacing * 0.5f) + i * height + 0.26f, 115 0.0f); 116 117 PhysicsEntity entity = addBoxEntity(position); 118 Material mat = New!Material(); 119 auto col = Color4f((randomUnitVector3!float + 0.5f).normalized); 120 mat.ambientColor = col; 121 mat.diffuseColor = col; 122 entity.material = mat; 123 materials.append(mat); 124 } 125 } 126 127 PhysicsEntity addBoxEntity(Vector3f pos) 128 { 129 auto bBox = world.addDynamicBody(pos, 0.0f); 130 auto scBox = world.addShapeComponent(bBox, gBox, Vector3f(0, 0, 0), 1.0f); 131 PhysicsEntity peBox = New!PhysicsEntity(sBox, bBox); 132 addDrawable(peBox); 133 entities.append(peBox); 134 return peBox; 135 } 136 137 void addDrawable(Drawable d) 138 { 139 drawables.append(d); 140 } 141 142 ~this() 143 { 144 camera.free(); 145 foreach(m; materials.data) 146 m.free(); 147 materials.free(); 148 foreach(d; drawables.data) 149 d.free(); 150 drawables.free(); 151 entities.free(); 152 axes.free(); 153 grid.free(); 154 world.free(); 155 sBox.free(); 156 157 gFloor.free(); 158 gBox.free(); 159 160 font.free(); 161 fpsText.free(); 162 } 163 164 override void onKeyDown(int key) 165 { 166 super.onKeyDown(key); 167 } 168 169 int prevMouseX; 170 int prevMouseY; 171 172 override void onMouseButtonDown(int button) 173 { 174 if (button == SDL_BUTTON_LEFT) 175 { 176 Ray mray = mouseRay(); 177 selectedEntity = pickEntity(mray); 178 if (selectedEntity) 179 dragDistance = distance(selectedEntity.rbody.position, camera.getPosition); 180 } 181 else if (button == SDL_BUTTON_MIDDLE) 182 { 183 prevMouseX = eventManager.mouseX; 184 prevMouseY = eventManager.mouseY; 185 } 186 else if (button == SDL_BUTTON_WHEELUP) 187 { 188 camera.zoom(1.0f); 189 } 190 else if (button == SDL_BUTTON_WHEELDOWN) 191 { 192 camera.zoom(-1.0f); 193 } 194 } 195 196 PhysicsEntity pickEntity(Ray r) 197 { 198 PhysicsEntity res = null; 199 200 float min_t = float.max; 201 foreach(e; entities.data) 202 { 203 AABB aabb = e.getAABB(); 204 float t; 205 if (aabb.intersectsSegment(r.p0, r.p1, t)) 206 { 207 if (t < min_t) 208 { 209 min_t = t; 210 res = e; 211 } 212 } 213 } 214 215 return res; 216 } 217 218 override void free() 219 { 220 Delete(this); 221 } 222 223 double time = 0.0; 224 225 override void onUpdate() 226 { 227 double dt = eventManager.deltaTime; 228 229 time += dt; 230 if (time >= fixedTimeStep) 231 { 232 time -= fixedTimeStep; 233 world.update(fixedTimeStep); 234 } 235 236 updateCamera(); 237 238 if (eventManager.mouseButtonPressed[SDL_BUTTON_LEFT] && selectedEntity) 239 { 240 Vector3f dragPosition = cameraMousePoint(dragDistance); 241 selectedEntity.rbody.position = dragPosition; 242 } 243 244 fpsText.setText(format("FPS: %s", eventManager.fps)); 245 } 246 247 override void onRedraw() 248 { 249 double dt = eventManager.deltaTime; 250 251 aspectRatio = cast(float)eventManager.windowWidth / cast(float)eventManager.windowHeight; 252 253 glMatrixMode(GL_PROJECTION); 254 glLoadIdentity(); 255 gluPerspective(fovY, aspectRatio, 0.1, 1000.0); 256 glMatrixMode(GL_MODELVIEW); 257 258 glLoadIdentity(); 259 glPushMatrix(); 260 camera.bind(dt); 261 262 grid.draw(dt); 263 glDisable(GL_DEPTH_TEST); 264 axes.draw(dt); 265 glEnable(GL_DEPTH_TEST); 266 267 glEnable(GL_LIGHTING); 268 glEnable(GL_LIGHT0); 269 glLightfv(GL_LIGHT0, GL_POSITION, lightPosition.arrayof.ptr); 270 271 foreach(d; drawables.data) 272 d.draw(dt); 273 if (selectedEntity) 274 { 275 selectedEntity.useMaterial = false; 276 drawWireframe(selectedEntity, dt); 277 selectedEntity.useMaterial = true; 278 } 279 glDisable(GL_LIGHTING); 280 281 camera.unbind(); 282 glPopMatrix(); 283 284 glMatrixMode(GL_PROJECTION); 285 glLoadIdentity(); 286 glOrtho(0, eventManager.windowWidth, 0, eventManager.windowHeight, 0, 1); 287 glMatrixMode(GL_MODELVIEW); 288 289 fpsText.draw(dt); 290 } 291 292 void updateCamera() 293 { 294 if (eventManager.mouseButtonPressed[SDL_BUTTON_MIDDLE] && eventManager.keyPressed[SDLK_LSHIFT]) 295 { 296 float shift_x = (eventManager.mouseX - prevMouseX) * 0.1f; 297 float shift_y = (eventManager.mouseY - prevMouseY) * 0.1f; 298 Vector3f trans = camera.getUpVector * shift_y + camera.getRightVector * shift_x; 299 camera.translateTarget(trans); 300 } 301 else if (eventManager.mouseButtonPressed[SDL_BUTTON_MIDDLE] && eventManager.keyPressed[SDLK_LCTRL]) 302 { 303 float shift_x = (eventManager.mouseX - prevMouseX); 304 float shift_y = (eventManager.mouseY - prevMouseY); 305 camera.zoom((shift_x + shift_y) * 0.1f); 306 } 307 else if (eventManager.mouseButtonPressed[SDL_BUTTON_MIDDLE]) 308 { 309 float turn_m = (eventManager.mouseX - prevMouseX); 310 float pitch_m = -(eventManager.mouseY - prevMouseY); 311 camera.pitch(pitch_m); 312 camera.turn(turn_m); 313 } 314 315 prevMouseX = eventManager.mouseX; 316 prevMouseY = eventManager.mouseY; 317 } 318 319 void drawWireframe(Drawable drw, double dt) 320 { 321 glDisable(GL_DEPTH_TEST); 322 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); 323 glDisable(GL_LIGHTING); 324 glColor4f(1.0f, 1.0f, 1.0f, 1.0f); 325 drw.draw(dt); 326 glEnable(GL_LIGHTING); 327 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); 328 glEnable(GL_DEPTH_TEST); 329 } 330 331 Vector3f cameraPoint(float x, float y, float dist) 332 { 333 return camera.getPosition() + cameraDir(x, y) * dist; 334 } 335 336 Vector3f cameraMousePoint(float dist) 337 { 338 return cameraPoint(eventManager.mouseX, eventManager.windowHeight - eventManager.mouseY, dist); 339 } 340 341 Vector3f cameraDir(float x, float y) 342 { 343 Vector3f camDir = -camera.getDirectionVector(); 344 345 float fovX = fovXfromY(fovY, aspectRatio); 346 347 float tfov1 = tan(fovY*PI/360.0f); 348 float tfov2 = tan(fovX*PI/360.0f); 349 350 Vector3f camUp = camera.getUpVector() * tfov1; 351 Vector3f camRight = -camera.getRightVector() * tfov2; 352 353 float width = 1.0f - 2.0f * x / eventManager.windowWidth; 354 float height = 1.0f - 2.0f * y / eventManager.windowHeight; 355 356 Vector3f m = camDir + camUp * height + camRight * width; 357 Vector3f dir = m.normalized; 358 359 return dir; 360 } 361 362 Ray cameraRay(float x, float y) 363 { 364 Vector3f camPos = camera.getPosition(); 365 Ray r = Ray(camPos, camPos + cameraDir(x, y) * maxPickDistance); 366 return r; 367 } 368 369 Vector3f mouseDir() 370 { 371 return cameraDir(eventManager.mouseX, eventManager.windowHeight - eventManager.mouseY); 372 } 373 374 Ray mouseRay() 375 { 376 return cameraRay(eventManager.mouseX, eventManager.windowHeight - eventManager.mouseY); 377 } 378 379 Ray mouseRay(float x, float y) 380 { 381 return cameraRay(x, eventManager.windowHeight - y); 382 } 383 } 384 385 void main(string[] args) 386 { 387 loadLibraries(); 388 writefln("Allocated memory at start: %s", allocatedMemory); 389 auto app = New!TestApp(); 390 app.run(); 391 Delete(app); 392 writefln("Allocated memory at end: %s", allocatedMemory); 393 }