1 module minermain; 2 3 import dlangui; 4 import dlangui.graphics.scene.scene3d; 5 import dlangui.graphics.scene.camera; 6 import dlangui.graphics.scene.mesh; 7 import dlangui.graphics.scene.material; 8 import dlangui.graphics.scene.effect; 9 import dlangui.graphics.scene.model; 10 import dlangui.graphics.scene.node; 11 import dlangui.graphics.scene.light; 12 import dlangui.graphics.scene.drawableobject; 13 import dlangui.graphics.scene.skybox; 14 import dlangui.graphics.scene.effect; 15 import dlangui.graphics.glsupport; 16 import dlangui.graphics.gldrawbuf; 17 18 /* 19 version (Android) { 20 //enum SUPPORT_LEGACY_OPENGL = false; 21 public import EGL.eglplatform : EGLint; 22 public import EGL.egl; 23 //public import GLES2.gl2; 24 public import GLES3.gl3; 25 } else { 26 //enum SUPPORT_LEGACY_OPENGL = true; 27 import derelict.opengl3.gl3; 28 import derelict.opengl3.gl; 29 } 30 */ 31 32 import dminer.core.minetypes; 33 import dminer.core.blocks; 34 import dminer.core.world; 35 import dminer.core.generators; 36 import dminer.core.chunk; 37 38 mixin APP_ENTRY_POINT; 39 40 /// entry point for dlangui based application 41 extern (C) int UIAppMain(string[] args) { 42 // embed resources listed in views/resources.list into executable 43 embeddedResourceList.addResources(embedResourcesFromList!("resources.list")()); 44 //embeddedResourceList.dumpEmbeddedResources(); 45 46 debug { 47 testPlanes(); 48 } 49 50 // create window 51 Window window = Platform.instance.createWindow("DlangUI Voxel RPG", null, WindowFlag.Resizable, 600, 500); 52 window.mainWidget = new UiWidget(); 53 54 //MeshPart part = new MeshPart(); 55 56 // show window 57 window.show(); 58 59 // run message loop 60 return Platform.instance.enterMessageLoop(); 61 } 62 63 class ChunkVisitCounter : ChunkVisitor { 64 int count; 65 void visit(World world, SmallChunk * chunk) { 66 count++; 67 } 68 } 69 70 class MinerDrawable : MaterialDrawableObject, ChunkVisitor { 71 72 import dlangui.graphics.scene.node; 73 private World _world; 74 private ChunkDiamondVisitor _chunkVisitor; 75 private VisibilityCheckIterator _chunkIterator; 76 private Vector3d _pos; 77 private Node3d _node; 78 private Camera _cam; 79 private vec3 _camPosition; 80 private vec3 _camForwardVector; 81 82 this(World world, Material material, Camera cam) { 83 super(material); 84 _world = world; 85 _cam = cam; 86 } 87 int _skippedCount; 88 int _drawnCount; 89 override void draw(Node3d node, bool wireframe) { 90 /// override it 91 _node = node; 92 //Log.d("drawing Miner scene"); 93 //_chunkVisitor.init(_world, MAX_VIEW_DISTANCE, this); 94 _pos = _world.camPosition.pos; 95 _camPosition = _cam.translation; 96 _camForwardVector = _cam.forwardVectorWorld; 97 _camPosition -= _camForwardVector * 8; 98 _skippedCount = _drawnCount = 0; 99 long ts = currentTimeMillis(); 100 //_chunkVisitor.visitChunks(_pos); 101 Vector3d camVector; 102 camVector.x = cast(int)(_camForwardVector.x * 256); 103 camVector.y = cast(int)(_camForwardVector.y * 256); 104 camVector.z = cast(int)(_camForwardVector.z * 256); 105 ChunkVisitCounter countVisitor = new ChunkVisitCounter(); 106 _chunkIterator.start(_world, _world.camPosition.pos, MAX_VIEW_DISTANCE); 107 _chunkIterator.visitVisibleChunks(countVisitor, camVector); 108 long durationNoDraw = currentTimeMillis() - ts; 109 _chunkIterator.start(_world, _world.camPosition.pos, MAX_VIEW_DISTANCE); 110 _chunkIterator.visitVisibleChunks(this, camVector); 111 long duration = currentTimeMillis() - ts; 112 Log.d("drawing of Miner scene finished in ", duration, " ms skipped:", _skippedCount, " drawn:", _drawnCount, " duration(noDraw)=", durationNoDraw); 113 } 114 void visit(World world, SmallChunk * chunk) { 115 if (chunk) { 116 Vector3d p = chunk.position; 117 vec3 chunkPos = vec3(p.x + 4, p.y + 4, p.z + 4); 118 vec3 chunkDirection = (chunkPos - _camPosition).normalized; 119 float dot = _camForwardVector.dot(chunkDirection); 120 //Log.d("visit() chunkPos ", chunkPos, " chunkDir ", chunkDirection, " camDir "); 121 if (dot < 0.7) { // cos(45) 122 _skippedCount++; 123 return; 124 } 125 Mesh mesh = chunk.getMesh(world); 126 if (mesh) { 127 _material.bind(_node, mesh, lights(_node)); 128 _material.drawMesh(mesh); 129 _material.unbind(); 130 _drawnCount++; 131 } 132 } 133 } 134 } 135 136 class UiWidget : VerticalLayout { //, CellVisitor 137 this() { 138 super("OpenGLView"); 139 layoutWidth = FILL_PARENT; 140 layoutHeight = FILL_PARENT; 141 alignment = Align.Center; 142 try { 143 parseML(q{ 144 { 145 margins: 0 146 padding: 0 147 //backgroundImageId: "tx_fabric.tiled" 148 backgroundColor: 0x000000; 149 layoutWidth: fill 150 layoutHeight: fill 151 152 VerticalLayout { 153 id: glView 154 margins: 0 155 padding: 0 156 layoutWidth: fill 157 layoutHeight: fill 158 TextWidget { text: "MinerD example"; textColor: "red"; fontSize: 150%; fontWeight: 800; fontFace: "Arial" } 159 VSpacer { layoutWeight: 30 } 160 TextWidget { id: lblPosition; text: ""; backgroundColor: 0x80202020; textColor: 0xFFE0E0 } 161 } 162 } 163 }, "", this); 164 } catch (Exception e) { 165 Log.e("Failed to parse dml", e); 166 } 167 // assign OpenGL drawable to child widget background 168 childById("glView").backgroundDrawable = DrawableRef(new OpenGLDrawable(&doDraw)); 169 170 _scene = new Scene3d(); 171 172 _cam = new Camera(); 173 _cam.translate(vec3(0, 14, -7)); 174 175 _scene.activeCamera = _cam; 176 177 static if (true) { 178 _scene.skyBox.setFaceTexture(SkyBox.Face.Right, "skybox_night_right1"); 179 _scene.skyBox.setFaceTexture(SkyBox.Face.Left, "skybox_night_left2"); 180 _scene.skyBox.setFaceTexture(SkyBox.Face.Top, "skybox_night_top3"); 181 _scene.skyBox.setFaceTexture(SkyBox.Face.Bottom, "skybox_night_bottom4"); 182 _scene.skyBox.setFaceTexture(SkyBox.Face.Front, "skybox_night_front5"); 183 _scene.skyBox.setFaceTexture(SkyBox.Face.Back, "skybox_night_back6"); 184 } else { 185 _scene.skyBox.setFaceTexture(SkyBox.Face.Right, "debug_right"); 186 _scene.skyBox.setFaceTexture(SkyBox.Face.Left, "debug_left"); 187 _scene.skyBox.setFaceTexture(SkyBox.Face.Top, "debug_top"); 188 _scene.skyBox.setFaceTexture(SkyBox.Face.Bottom, "debug_bottom"); 189 _scene.skyBox.setFaceTexture(SkyBox.Face.Front, "debug_front"); 190 _scene.skyBox.setFaceTexture(SkyBox.Face.Back, "debug_back"); 191 } 192 193 dirLightNode = new Node3d(); 194 dirLightNode.rotateY(-15); 195 dirLightNode.translateX(2); 196 dirLightNode.translateY(3); 197 dirLightNode.translateZ(0); 198 dirLightNode.light = Light.createPoint(vec3(1.0, 1.0, 1.0), 55); //Light.createDirectional(vec3(1, 0.5, 0.5)); 199 //dirLightNode.light = Light.createDirectional(vec3(1, 0.5, 0.5)); 200 dirLightNode.light.enabled = true; 201 _scene.addChild(dirLightNode); 202 203 204 int x0 = 0; 205 int y0 = 0; 206 int z0 = 0; 207 208 209 _minerMesh = new Mesh(VertexFormat(VertexElementType.POSITION, VertexElementType.NORMAL, VertexElementType.COLOR, VertexElementType.TEXCOORD0)); 210 _world = new World(); 211 212 initWorldTerrain(_world); 213 214 int cy0 = 3; 215 for (int y = CHUNK_DY - 1; y > 0; y--) 216 if (!_world.canPass(Vector3d(0, y, 0))) { 217 cy0 = y; 218 break; 219 } 220 _world.camPosition = Position(Vector3d(0, cy0, 0), Vector3d(0, 0, 1)); 221 222 _world.setCell(5, cy0 + 5, 7, BlockId.face_test); 223 _world.setCell(-5, cy0 + 5, 7, BlockId.face_test); 224 _world.setCell(5, cy0 + 5, -7, BlockId.face_test); 225 _world.setCell(3, cy0 + 5, 13, BlockId.face_test); 226 227 228 //_world.makeCastleWall(Vector3d(25, cy0 - 5, 12), Vector3d(1, 0, 0), 12, 30, 4, BlockId.brick); 229 _world.makeCastle(Vector3d(0, cy0, 60), 30, 12); 230 231 updateCamPosition(false); 232 //updateMinerMesh(); 233 234 Material minerMaterial = new Material(EffectId("textured.vert", "textured.frag", null), "blocks"); 235 minerMaterial.ambientColor = vec3(0.05,0.05,0.05); 236 minerMaterial.textureLinear = false; 237 minerMaterial.fogParams = new FogParams(vec4(0.01, 0.01, 0.01, 1), 12, 80); 238 //minerMaterial.specular = 10; 239 _minerDrawable = new MinerDrawable(_world, minerMaterial, _cam); 240 //_minerDrawable.autobindLights = false; 241 //Model minerDrawable = new Model(minerMaterial, _minerMesh); 242 Node3d minerNode = new Node3d("miner", _minerDrawable); 243 _scene.addChild(minerNode); 244 245 246 focusable = true; 247 } 248 249 MinerDrawable _minerDrawable; 250 251 int lastMouseX; 252 int lastMouseY; 253 /// process key event, return true if event is processed. 254 override bool onMouseEvent(MouseEvent event) { 255 if (event.action == MouseAction.ButtonDown) { 256 lastMouseX = event.x; 257 lastMouseY = event.y; 258 if (event.button == MouseButton.Left && false) { 259 int x = event.x; 260 int y = event.y; 261 int xindex = 0; 262 if (x > width * 2 / 3) 263 xindex = 2; 264 else if (x > width * 1 / 3) 265 xindex = 1; 266 int yindex = 0; 267 if (y > height * 2 / 3) 268 yindex = 2; 269 else if (y > height * 1 / 3) 270 yindex = 1; 271 int index = yindex * 3 + xindex; 272 /* 273 index: 274 0 1 2 275 3 4 5 276 6 7 8 277 */ 278 switch(index) { 279 default: 280 case 1: 281 case 4: 282 //_world.camPosition.forward(1); 283 //updateCamPosition(); 284 startMoveAnimation(_world.camPosition.direction.forward); 285 break; 286 case 0: 287 case 3: 288 _world.camPosition.turnLeft(); 289 updateCamPosition(); 290 break; 291 case 2: 292 case 5: 293 _world.camPosition.turnRight(); 294 updateCamPosition(); 295 break; 296 case 7: 297 //_world.camPosition.backward(1); 298 //updateCamPosition(); 299 startMoveAnimation(-_world.camPosition.direction.forward); 300 break; 301 case 6: 302 //_world.camPosition.moveLeft(); 303 //updateCamPosition(); 304 startMoveAnimation(_world.camPosition.direction.left); 305 break; 306 case 8: 307 //_world.camPosition.moveRight(); 308 //updateCamPosition(); 309 startMoveAnimation(_world.camPosition.direction.right); 310 break; 311 } 312 } 313 } else if (event.action == MouseAction.Move) { 314 if (event.lbutton.isDown) { 315 int deltaX = event.x - lastMouseX; 316 int deltaY = event.y - lastMouseY; 317 int maxshift = width > 100 ? width : 100; 318 float deltaAngleX = deltaX * 45.0f / maxshift; 319 float deltaAngleY = deltaY * 45.0f / maxshift; 320 lastMouseX = event.x; 321 lastMouseY = event.y; 322 float newAngle = _angle + deltaAngleX; 323 if (newAngle < -180) 324 newAngle += 360; 325 else if (newAngle > 180) 326 newAngle -= 360; 327 setAngle(newAngle, true); 328 float newAngleY = _yAngle + deltaAngleY; 329 if (newAngleY < -65) 330 newAngleY = -65; 331 else if (newAngleY > 65) 332 newAngleY = 65; 333 setYAngle(newAngleY, true); 334 } 335 } else if (event.action == MouseAction.ButtonUp || event.action == MouseAction.Cancel) { 336 stopMoveAnimation(); 337 } 338 return true; 339 } 340 341 /// process key event, return true if event is processed. 342 override bool onKeyEvent(KeyEvent event) { 343 if (event.action == KeyAction.KeyDown) { 344 switch(event.keyCode) with(KeyCode) { 345 case KEY_W: 346 case UP: 347 _world.camPosition.forward(1); 348 updateCamPosition(); 349 return true; 350 case DOWN: 351 case KEY_S: 352 _world.camPosition.backward(1); 353 updateCamPosition(); 354 return true; 355 case KEY_A: 356 case LEFT: 357 _world.camPosition.turnLeft(); 358 updateCamPosition(); 359 return true; 360 case KEY_D: 361 case RIGHT: 362 _world.camPosition.turnRight(); 363 updateCamPosition(); 364 return true; 365 case HOME: 366 case KEY_E: 367 _world.camPosition.moveUp(); 368 updateCamPosition(); 369 return true; 370 case END: 371 case KEY_Q: 372 _world.camPosition.moveDown(); 373 updateCamPosition(); 374 return true; 375 case KEY_Z: 376 _world.camPosition.moveLeft(); 377 updateCamPosition(); 378 return true; 379 case KEY_C: 380 _world.camPosition.moveRight(); 381 updateCamPosition(); 382 return true; 383 case KEY_F: 384 flying = !flying; 385 if (!flying) 386 _world.camPosition.pos.y = CHUNK_DY - 3; 387 updateCamPosition(); 388 return true; 389 case KEY_U: 390 enableMeshUpdate = !enableMeshUpdate; 391 updateCamPosition(); 392 return true; 393 default: 394 return false; 395 } 396 } 397 return false; 398 } 399 400 Node3d dirLightNode; 401 402 //void visit(World world, ref Position camPosition, Vector3d pos, cell_t cell, int visibleFaces) { 403 // BlockDef def = BLOCK_DEFS[cell]; 404 // def.createFaces(world, world.camPosition, pos, visibleFaces, _minerMesh); 405 //} 406 407 bool flying = false; 408 bool enableMeshUpdate = true; 409 Vector3d _moveAnimationDirection; 410 411 void animateMoving() { 412 if (_moveAnimationDirection != Vector3d(0,0,0)) { 413 Vector3d animPos = _world.camPosition.pos + _moveAnimationDirection; 414 vec3 p = vec3(animPos.x + 0.5f, animPos.y + 0.5f, animPos.z + 0.5f); 415 if ((_animatingPosition - p).length < 2) { 416 _world.camPosition.pos += _moveAnimationDirection; 417 updateCamPosition(true); 418 } 419 } 420 } 421 422 void updateCamPosition(bool animateIt = true) { 423 import std.string; 424 import std.conv : to; 425 import std.utf : toUTF32; 426 import std.format; 427 428 if (!flying) { 429 animateMoving(); 430 while(_world.canPass(_world.camPosition.pos + Vector3d(0, -1, 0))) 431 _world.camPosition.pos += Vector3d(0, -1, 0); 432 if(!_world.canPass(_world.camPosition.pos + Vector3d(0, -1, 0))) { 433 if (_world.canPass(_world.camPosition.pos + Vector3d(0, 1, 0))) 434 _world.camPosition.pos += Vector3d(0, 1, 0); 435 else if (_world.canPass(_world.camPosition.pos + Vector3d(1, 0, 0))) 436 _world.camPosition.pos += Vector3d(1, 0, 0); 437 else if (_world.canPass(_world.camPosition.pos + Vector3d(-1, 0, 0))) 438 _world.camPosition.pos += Vector3d(-1, 0, 0); 439 else if (_world.canPass(_world.camPosition.pos + Vector3d(0, 0, 1))) 440 _world.camPosition.pos += Vector3d(0, 0, 1); 441 else if (_world.canPass(_world.camPosition.pos + Vector3d(0, 0, -1))) 442 _world.camPosition.pos += Vector3d(0, 0, -1); 443 while(_world.canPass(_world.camPosition.pos + Vector3d(0, -1, 0))) 444 _world.camPosition.pos += Vector3d(0, -1, 0); 445 } 446 } 447 448 setPos(vec3(_world.camPosition.pos.x + 0.5f, _world.camPosition.pos.y + 0.5f, _world.camPosition.pos.z + 0.5f), animateIt); 449 setAngle(_world.camPosition.direction.angle, animateIt); 450 451 updatePositionMessage(); 452 } 453 454 void updatePositionMessage() { 455 import std.string : format; 456 Widget w = childById("lblPosition"); 457 string dir = _world.camPosition.direction.dir.to!string; 458 dstring s = format("pos(%d,%d) h=%d fps:%d %s [F]lying: %s [U]pdateMesh: %s", _world.camPosition.pos.x, _world.camPosition.pos.z, _world.camPosition.pos.y, 459 _fps, 460 dir, 461 flying, 462 enableMeshUpdate).toUTF32; 463 w.text = s; 464 } 465 466 int _fps = 0; 467 468 void startMoveAnimation(Vector3d direction) { 469 _moveAnimationDirection = direction; 470 updateCamPosition(); 471 } 472 473 void stopMoveAnimation() { 474 _moveAnimationDirection = Vector3d(0, 0, 0); 475 updateCamPosition(); 476 } 477 478 //void updateMinerMesh() { 479 // _minerMesh.reset(); 480 // long ts = currentTimeMillis; 481 // _world.visitVisibleCells(_world.camPosition, this); 482 // long duration = currentTimeMillis - ts; 483 // Log.d("DiamondVisitor finished in ", duration, " ms ", "Vertex count: ", _minerMesh.vertexCount); 484 // 485 // invalidate(); 486 // //for (int i = 0; i < 20; i++) 487 // // Log.d("vertex: ", _minerMesh.vertex(i)); 488 //} 489 490 World _world; 491 vec3 _position; 492 float _directionAngle = 0; 493 float _yAngle = -15; 494 float _angle; 495 vec3 _animatingPosition; 496 float _animatingAngle; 497 float _animatingYAngle; 498 499 void setPos(vec3 newPos, bool animateIt = false) { 500 if (animateIt) { 501 _position = newPos; 502 } else { 503 _animatingPosition = newPos; 504 _position = newPos; 505 } 506 } 507 508 void setAngle(float newAngle, bool animateIt = false) { 509 if (animateIt) { 510 _angle = newAngle; 511 } else { 512 _animatingAngle = newAngle; 513 _angle = newAngle; 514 } 515 } 516 517 void setYAngle(float newAngle, bool animateIt = false) { 518 if (animateIt) { 519 _yAngle = newAngle; 520 } else { 521 _animatingYAngle = newAngle; 522 _yAngle = newAngle; 523 } 524 } 525 526 /// returns true is widget is being animated - need to call animate() and redraw 527 @property override bool animating() { return true; } 528 /// animates window; interval is time left from previous draw, in hnsecs (1/10000000 of second) 529 override void animate(long interval) { 530 //Log.d("animating"); 531 if (interval > 0) { 532 int newfps = cast(int)(10000000.0 / interval); 533 if (newfps < _fps - 3 || newfps > _fps + 3) { 534 _fps = newfps; 535 updatePositionMessage(); 536 } 537 } 538 animateMoving(); 539 if (_animatingAngle != _angle) { 540 float delta = _angle - _animatingAngle; 541 if (delta > 180) 542 delta -= 360; 543 else if (delta < -180) 544 delta += 360; 545 float dist = delta < 0 ? -delta : delta; 546 if (dist < 5) { 547 _animatingAngle = _angle; 548 } else { 549 float speed = 360 / 2; 550 float step = speed * interval / 10000000.0f; 551 //Log.d("Rotate animation delta=", delta, " dist=", dist, " elapsed=", interval, " step=", step); 552 if (step > dist) 553 step = dist; 554 delta = delta * (step /dist); 555 _animatingAngle += delta; 556 } 557 } 558 if (_animatingYAngle != _yAngle) { 559 float delta = _yAngle - _animatingYAngle; 560 if (delta > 180) 561 delta -= 360; 562 else if (delta < -180) 563 delta += 360; 564 float dist = delta < 0 ? -delta : delta; 565 if (dist < 5) { 566 _animatingYAngle = _yAngle; 567 } else { 568 float speed = 360 / 2; 569 float step = speed * interval / 10000000.0f; 570 //Log.d("Rotate animation delta=", delta, " dist=", dist, " elapsed=", interval, " step=", step); 571 if (step > dist) 572 step = dist; 573 delta = delta * (step /dist); 574 _animatingYAngle += delta; 575 } 576 } 577 if (_animatingPosition != _position) { 578 vec3 delta = _position - _animatingPosition; 579 float dist = delta.length; 580 if (dist < 0.01) { 581 _animatingPosition = _position; 582 // done 583 } else { 584 float speed = 8; 585 if (dist > 2) 586 speed = (dist - 2) * 3 + speed; 587 float step = speed * interval / 10000000.0f; 588 //Log.d("Move animation delta=", delta, " dist=", dist, " elapsed=", interval, " step=", step); 589 if (step > dist) 590 step = dist; 591 delta = delta * (step / dist); 592 _animatingPosition += delta; 593 } 594 } 595 invalidate(); 596 } 597 float angle = 0; 598 599 Scene3d _scene; 600 Camera _cam; 601 Mesh _minerMesh; 602 603 604 /// this is OpenGLDrawableDelegate implementation 605 private void doDraw(Rect windowRect, Rect rc) { 606 _cam.setPerspective(rc.width, rc.height, 45.0f, 0.3, MAX_VIEW_DISTANCE); 607 _cam.setIdentity(); 608 _cam.translate(_animatingPosition); 609 _cam.rotateY(_animatingAngle); 610 _cam.rotateX(_yAngle); 611 612 613 dirLightNode.setIdentity(); 614 dirLightNode.translate(_animatingPosition); 615 dirLightNode.rotateY(_animatingAngle); 616 617 checkgl!glEnable(GL_CULL_FACE); 618 //checkgl!glDisable(GL_CULL_FACE); 619 checkgl!glEnable(GL_DEPTH_TEST); 620 checkgl!glCullFace(GL_BACK); 621 622 Log.d("Drawing position ", _animatingPosition, " angle ", _animatingAngle); 623 624 _scene.drawScene(false); 625 626 checkgl!glDisable(GL_DEPTH_TEST); 627 checkgl!glDisable(GL_CULL_FACE); 628 } 629 630 ~this() { 631 destroy(_scene); 632 destroy(_world); 633 } 634 }