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 }