1 module dlangui.graphics.scene.scene3d;
2 
3 public import dlangui.core.config;
4 static if (ENABLE_OPENGL):
5 static if (BACKEND_GUI):
6 
7 import dlangui.core.types;
8 
9 import dlangui.core.math3d;
10 import dlangui.graphics.scene.node;
11 import dlangui.graphics.scene.skybox;
12 
13 /// Reference counted Scene3d object
14 alias Scene3dRef = Ref!Scene3d;
15 
16 
17 /// 3D scene
18 class Scene3d : Node3d {
19     import dlangui.graphics.scene.camera;
20     import dlangui.graphics.scene.light;
21 
22     protected vec3 _ambientColor;
23     protected Camera _activeCamera;
24     protected SkyBox _skyBox;
25 
26     /// ambient light color
27     @property vec3 ambientColor() { return _ambientColor; }
28     /// set ambient light color
29     @property void ambientColor(const ref vec3 v) { _ambientColor = v; }
30 
31     this(string id = null) {
32         super(id);
33         _skyBox = new SkyBox(this);
34     }
35 
36     ~this() {
37         destroy(_skyBox);
38     }
39 
40     @property SkyBox skyBox() { return _skyBox; }
41 
42     /// active camera
43     override @property Camera activeCamera() {
44         if (_activeCamera)
45             return _activeCamera;
46         // TODO: find camera in child nodes
47         return null;
48     }
49     /// set or clear current active camera
50     @property void activeCamera(Camera cam) {
51         _activeCamera = cam;
52         if (cam && cam.parent != this)
53             addChild(cam);
54     }
55 
56     /// returns scene for node
57     override @property Scene3d scene() {
58         return this;
59     }
60 
61     override @property void scene(Scene3d v) {
62         //ignore
63     }
64 
65     protected bool _wireframe;
66     void drawScene(bool wireframe) {
67         _wireframe = wireframe;
68         updateAutoboundLights();
69         if (_skyBox.visible) {
70             import dlangui.graphics.glsupport;
71             checkgl!glDisable(GL_DEPTH_TEST);
72             checkgl!glDisable(GL_CULL_FACE);
73             if (_activeCamera) {
74                 _skyBox.translation = _activeCamera.translation;
75                 _skyBox.scaling = _activeCamera.farRange * 0.3;
76             }
77             visit(_skyBox, &sceneDrawVisitor);
78             checkgl!glEnable(GL_DEPTH_TEST);
79             checkgl!glEnable(GL_CULL_FACE);
80             checkgl!glClear(GL_DEPTH_BUFFER_BIT);
81         }
82         visit(this, &sceneDrawVisitor);
83     }
84 
85     protected bool sceneDrawVisitor(Node3d node) {
86         if (!node.visible)
87             return false;
88         if (!node.drawable.isNull)
89             node.drawable.draw(node, _wireframe);
90         return false;
91     }
92 
93     void updateAutoboundLights() {
94         _lights.reset();
95         visit(this, &lightBindingVisitor);
96     }
97 
98     protected bool lightBindingVisitor(Node3d node) {
99         if (!node.light.isNull && node.light.enabled && node.light.autobind)
100             _lights.add(node.light);
101         return false;
102     }
103 
104     protected LightParams _lights;
105 
106     @property ref LightParams boundLights() {
107         return _lights;
108     }
109 
110     @property LightParams * boundLightsPtr() {
111         return _lights.empty ? null : &_lights;
112     }
113 }
114 
115 /// depth-first recursive node traversion, stops if visitor returns true
116 bool visit(Node3d node, bool delegate(Node3d node) visitor) {
117     if (!node.visible)
118         return false;
119     bool res = visitor(node);
120     if (res)
121         return true;
122     foreach(child; node.children) {
123         bool res = visit(child, visitor);
124         if (res)
125             return true;
126     }
127     return false;
128 }