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