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(null, 1000); 82 83 gFloor = New!GeomBox(world, 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(world, 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 sBox.free(); 155 156 Delete(world); 157 158 font.free(); 159 fpsText.free(); 160 } 161 162 override void onKeyDown(int key) 163 { 164 super.onKeyDown(key); 165 } 166 167 int prevMouseX; 168 int prevMouseY; 169 170 override void onMouseButtonDown(int button) 171 { 172 if (button == SDL_BUTTON_LEFT) 173 { 174 Ray mray = mouseRay(); 175 selectedEntity = pickEntity(mray); 176 if (selectedEntity) 177 dragDistance = distance(selectedEntity.rbody.position, camera.getPosition); 178 } 179 else if (button == SDL_BUTTON_MIDDLE) 180 { 181 prevMouseX = eventManager.mouseX; 182 prevMouseY = eventManager.mouseY; 183 } 184 else if (button == SDL_BUTTON_WHEELUP) 185 { 186 camera.zoom(1.0f); 187 } 188 else if (button == SDL_BUTTON_WHEELDOWN) 189 { 190 camera.zoom(-1.0f); 191 } 192 } 193 194 PhysicsEntity pickEntity(Ray r) 195 { 196 PhysicsEntity res = null; 197 198 float min_t = float.max; 199 foreach(e; entities.data) 200 { 201 AABB aabb = e.getAABB(); 202 float t; 203 if (aabb.intersectsSegment(r.p0, r.p1, t)) 204 { 205 if (t < min_t) 206 { 207 min_t = t; 208 res = e; 209 } 210 } 211 } 212 213 return res; 214 } 215 216 override void free() 217 { 218 Delete(this); 219 } 220 221 double time = 0.0; 222 223 override void onUpdate() 224 { 225 double dt = eventManager.deltaTime; 226 227 time += dt; 228 if (time >= fixedTimeStep) 229 { 230 time -= fixedTimeStep; 231 world.update(fixedTimeStep); 232 } 233 234 updateCamera(); 235 236 if (eventManager.mouseButtonPressed[SDL_BUTTON_LEFT] && selectedEntity) 237 { 238 Vector3f dragPosition = cameraMousePoint(dragDistance); 239 selectedEntity.rbody.position = dragPosition; 240 } 241 242 fpsText.setText(format("FPS: %s", eventManager.fps)); 243 } 244 245 override void onRedraw() 246 { 247 double dt = eventManager.deltaTime; 248 249 aspectRatio = cast(float)eventManager.windowWidth / cast(float)eventManager.windowHeight; 250 251 glMatrixMode(GL_PROJECTION); 252 glLoadIdentity(); 253 gluPerspective(fovY, aspectRatio, 0.1, 1000.0); 254 glMatrixMode(GL_MODELVIEW); 255 256 glLoadIdentity(); 257 glPushMatrix(); 258 camera.bind(dt); 259 260 grid.draw(dt); 261 glDisable(GL_DEPTH_TEST); 262 axes.draw(dt); 263 glEnable(GL_DEPTH_TEST); 264 265 glEnable(GL_LIGHTING); 266 glEnable(GL_LIGHT0); 267 glLightfv(GL_LIGHT0, GL_POSITION, lightPosition.arrayof.ptr); 268 269 foreach(d; drawables.data) 270 d.draw(dt); 271 if (selectedEntity) 272 { 273 selectedEntity.useMaterial = false; 274 drawWireframe(selectedEntity, dt); 275 selectedEntity.useMaterial = true; 276 } 277 glDisable(GL_LIGHTING); 278 279 camera.unbind(); 280 glPopMatrix(); 281 282 glMatrixMode(GL_PROJECTION); 283 glLoadIdentity(); 284 glOrtho(0, eventManager.windowWidth, 0, eventManager.windowHeight, 0, 1); 285 glMatrixMode(GL_MODELVIEW); 286 287 fpsText.draw(dt); 288 } 289 290 void updateCamera() 291 { 292 if (eventManager.mouseButtonPressed[SDL_BUTTON_MIDDLE] && eventManager.keyPressed[SDLK_LSHIFT]) 293 { 294 float shift_x = (eventManager.mouseX - prevMouseX) * 0.1f; 295 float shift_y = (eventManager.mouseY - prevMouseY) * 0.1f; 296 Vector3f trans = camera.getUpVector * shift_y + camera.getRightVector * shift_x; 297 camera.translateTarget(trans); 298 } 299 else if (eventManager.mouseButtonPressed[SDL_BUTTON_MIDDLE] && eventManager.keyPressed[SDLK_LCTRL]) 300 { 301 float shift_x = (eventManager.mouseX - prevMouseX); 302 float shift_y = (eventManager.mouseY - prevMouseY); 303 camera.zoom((shift_x + shift_y) * 0.1f); 304 } 305 else if (eventManager.mouseButtonPressed[SDL_BUTTON_MIDDLE]) 306 { 307 float turn_m = (eventManager.mouseX - prevMouseX); 308 float pitch_m = -(eventManager.mouseY - prevMouseY); 309 camera.pitch(pitch_m); 310 camera.turn(turn_m); 311 } 312 313 prevMouseX = eventManager.mouseX; 314 prevMouseY = eventManager.mouseY; 315 } 316 317 void drawWireframe(Drawable drw, double dt) 318 { 319 glDisable(GL_DEPTH_TEST); 320 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); 321 glDisable(GL_LIGHTING); 322 glColor4f(1.0f, 1.0f, 1.0f, 1.0f); 323 drw.draw(dt); 324 glEnable(GL_LIGHTING); 325 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); 326 glEnable(GL_DEPTH_TEST); 327 } 328 329 Vector3f cameraPoint(float x, float y, float dist) 330 { 331 return camera.getPosition() + cameraDir(x, y) * dist; 332 } 333 334 Vector3f cameraMousePoint(float dist) 335 { 336 return cameraPoint(eventManager.mouseX, eventManager.windowHeight - eventManager.mouseY, dist); 337 } 338 339 Vector3f cameraDir(float x, float y) 340 { 341 Vector3f camDir = -camera.getDirectionVector(); 342 343 float fovX = fovXfromY(fovY, aspectRatio); 344 345 float tfov1 = tan(fovY*PI/360.0f); 346 float tfov2 = tan(fovX*PI/360.0f); 347 348 Vector3f camUp = camera.getUpVector() * tfov1; 349 Vector3f camRight = -camera.getRightVector() * tfov2; 350 351 float width = 1.0f - 2.0f * x / eventManager.windowWidth; 352 float height = 1.0f - 2.0f * y / eventManager.windowHeight; 353 354 Vector3f m = camDir + camUp * height + camRight * width; 355 Vector3f dir = m.normalized; 356 357 return dir; 358 } 359 360 Ray cameraRay(float x, float y) 361 { 362 Vector3f camPos = camera.getPosition(); 363 Ray r = Ray(camPos, camPos + cameraDir(x, y) * maxPickDistance); 364 return r; 365 } 366 367 Vector3f mouseDir() 368 { 369 return cameraDir(eventManager.mouseX, eventManager.windowHeight - eventManager.mouseY); 370 } 371 372 Ray mouseRay() 373 { 374 return cameraRay(eventManager.mouseX, eventManager.windowHeight - eventManager.mouseY); 375 } 376 377 Ray mouseRay(float x, float y) 378 { 379 return cameraRay(x, eventManager.windowHeight - y); 380 } 381 } 382 383 void main(string[] args) 384 { 385 loadLibraries(); 386 writefln("Allocated memory at start: %s", allocatedMemory); 387 auto app = New!TestApp(); 388 app.run(); 389 Delete(app); 390 writefln("Allocated memory at end: %s", allocatedMemory); 391 }