1 module d3d;
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.objimport;
13 import dlangui.graphics.scene.fbximport;
14 import dlangui.graphics.glsupport;
15 import dlangui.graphics.gldrawbuf;
16 import derelict.opengl3.gl3;
17 import derelict.opengl3.gl;
18 
19 mixin APP_ENTRY_POINT;
20 
21 /// entry point for dlangui based application
22 extern (C) int UIAppMain(string[] args) {
23     // embed resources listed in views/resources.list into executable
24     embeddedResourceList.addResources(embedResourcesFromList!("resources.list")());
25 
26     // create window
27     Window window = Platform.instance.createWindow("DlangUI example - 3D Application", null, WindowFlag.Resizable, 600, 500);
28     static if (ENABLE_OPENGL) {
29         window.mainWidget = new UiWidget();
30     } else {
31         window.mainWidget = new TextWidget("error", "Please build with OpenGL enabled"d);
32     }
33 
34     //MeshPart part = new MeshPart();
35 
36     // show window
37     window.show();
38 
39     // run message loop
40     return Platform.instance.enterMessageLoop();
41 }
42 
43 static if (ENABLE_OPENGL):
44 
45 class UiWidget : VerticalLayout {
46     this() {
47         super("OpenGLView");
48         layoutWidth = FILL_PARENT;
49         layoutHeight = FILL_PARENT;
50         alignment = Align.Center;
51         try {
52             parseML(q{
53                 {
54                   margins: 10
55                   padding: 10
56                   backgroundImageId: "tx_fabric.tiled"
57                   layoutWidth: fill
58                   layoutHeight: fill
59 
60                   VerticalLayout {
61                     id: glView
62                     margins: 10
63                     padding: 10
64                     layoutWidth: fill
65                     layoutHeight: fill
66                     TextWidget { text: "There should be OpenGL animation on background"; textColor: "red"; fontSize: 150%; fontWeight: 800; fontFace: "Arial" }
67                     TextWidget { text: "Do you see it? If no, there is some bug in Mesh rendering code..."; fontSize: 120% }
68                     // arrange controls as form - table with two columns
69                     TableLayout {
70                         colCount: 6
71                         TextWidget { text: "Translation X" }
72                         TextWidget { id: lblTranslationX; text: "0.0"; minWidth: 80; backgroundColor: 0x80FFFFFF }
73                         ScrollBar { id: sbTranslationX; orientation: horizontal; minValue: -100; maxValue: 100; position: 0; minWidth: 200; alpha: 0.6 }
74                         TextWidget { text: "Rotation X" }
75                         TextWidget { id: lblRotationX; text: "0.0"; minWidth: 80; backgroundColor: 0x80FFFFFF }
76                         ScrollBar { id: sbRotationX; orientation: horizontal; minValue: -180; maxValue: 180; position: 0; minWidth: 200; alpha: 0.6 }
77                         TextWidget { text: "Translation Y" }
78                         TextWidget { id: lblTranslationY; text: "0.0"; minWidth: 80; backgroundColor: 0x80FFFFFF }
79                         ScrollBar { id: sbTranslationY; orientation: horizontal; minValue: -100; maxValue: 100; position: 15; minWidth: 200; alpha: 0.6 }
80                         TextWidget { text: "Rotation Y" }
81                         TextWidget { id: lblRotationY; text: "0.0"; minWidth: 80; backgroundColor: 0x80FFFFFF }
82                         ScrollBar { id: sbRotationY; orientation: horizontal; minValue: -180; maxValue: 180; position: 0; minWidth: 150; alpha: 0.6 }
83                         TextWidget { text: "Translation Z" }
84                         TextWidget { id: lblTranslationZ; text: "0.0"; minWidth: 80; backgroundColor: 0x80FFFFFF }
85                         ScrollBar { id: sbTranslationZ; orientation: horizontal; minValue: -100; maxValue: 100; position: 45; minWidth: 150; alpha: 0.6 }
86                         TextWidget { text: "Rotation Z" }
87                         TextWidget { id: lblRotationZ; text: "0.0"; minWidth: 80; backgroundColor: 0x80FFFFFF }
88                         ScrollBar { id: sbRotationZ; orientation: horizontal; minValue: -180; maxValue: 180; position: 0; minWidth: 150; alpha: 0.6 }
89                         TextWidget { text: "Near" }
90                         TextWidget { id: lblNear; text: "0.1"; minWidth: 80; backgroundColor: 0x80FFFFFF }
91                         ScrollBar { id: sbNear; orientation: horizontal; minValue: 1; maxValue: 100; position: 1; minWidth: 150; alpha: 0.6 }
92                         TextWidget { text: "Far" }
93                         TextWidget { id: lblFar; text: "0.0"; minWidth: 80; backgroundColor: 0x80FFFFFF }
94                         ScrollBar { id: sbFar; orientation: horizontal; minValue: 20; maxValue: 1000; position: 1000; minWidth: 150; alpha: 0.6 }
95                     }
96                     VSpacer { layoutWeight: 30 }
97                     HorizontalLayout {
98                         TextWidget { text: "Some buttons:" }
99                         Button { id: btnOk; text: "Ok"; fontSize: 27px }
100                         Button { id: btnCancel; text: "Cancel"; fontSize: 27px }
101                     }
102                   }
103                 }
104             }, "", this);
105         } catch (Exception e) {
106             Log.e("Failed to parse dml", e);
107         }
108         // assign OpenGL drawable to child widget background
109         childById("glView").backgroundDrawable = DrawableRef(new OpenGLDrawable(&doDraw));
110         controlsToVars();
111         assignHandlers();
112 
113         _scene = new Scene3d();
114 
115         _cam = new Camera();
116         _cam.translate(vec3(0, 14, -7));
117 
118         _scene.activeCamera = _cam;
119 
120         dirLightNode = new Node3d();
121         //dirLightNode.lookAt(vec3(-5, -5, -5), vec3(0, 0, 0), vec3(0, 1, 0));
122         dirLightNode.rotateY(-15);
123         //dirLightNode.rotateX(20);
124         dirLightNode.translateX(2);
125         dirLightNode.translateY(3);
126         dirLightNode.translateZ(0);
127         dirLightNode.light = Light.createPoint(vec3(2, 2, 2), 15); //Light.createDirectional(vec3(1, 0.5, 0.5));
128         //dirLightNode.light = Light.createDirectional(vec3(1, 0.5, 0.8));
129         dirLightNode.light.enabled = true;
130         _scene.addChild(dirLightNode);
131 
132         int x0 = 0;
133         int y0 = 0;
134         int z0 = 0;
135 
136         Mesh _mesh = Mesh.createCubeMesh(vec3(x0+ 0, y0 + 0, z0 + 0), 0.3f);
137         for (int i = 0; i < 10; i++) {
138             _mesh.addCubeMesh(vec3(x0+ 0, y0+0, z0+ i * 2 + 1.0f), 0.2f, vec4(i / 12, 1, 1, 1));
139             _mesh.addCubeMesh(vec3(x0+ i * 2 + 1.0f, y0+0, z0+ 0), 0.2f, vec4(1, i / 12, 1, 1));
140             _mesh.addCubeMesh(vec3(x0+ -i * 2 - 1.0f, y0+0, z0+ 0), 0.2f, vec4(1, i / 12, 1, 1));
141             _mesh.addCubeMesh(vec3(x0+ 0, y0+i * 2 + 1.0f, z0+ 0), 0.2f, vec4(1, 1, i / 12 + 0.1, 1));
142             _mesh.addCubeMesh(vec3(x0+ 0, y0+-i * 2 - 1.0f, z0+ 0), 0.2f, vec4(1, 1, i / 12 + 0.1, 1));
143             _mesh.addCubeMesh(vec3(x0+ i * 2 + 1.0f, y0+i * 2 + 1.0f, z0+ i * 2 + 1.0f), 0.2f, vec4(i / 12, i / 12, i / 12, 1));
144             _mesh.addCubeMesh(vec3(x0+ -i * 2 + 1.0f, y0+i * 2 + 1.0f, z0+ i * 2 + 1.0f), 0.2f, vec4(i / 12, i / 12, 1 - i / 12, 1));
145             _mesh.addCubeMesh(vec3(x0+  i * 2 + 1.0f, y0+-i * 2 + 1.0f, z0+ i * 2 + 1.0f), 0.2f, vec4(i / 12, 1 - i / 12, i / 12, 1));
146             _mesh.addCubeMesh(vec3(x0+ -i * 2 - 1.0f, y0+-i * 2 - 1.0f, z0+ -i * 2 - 1.0f), 0.2f, vec4(1 - i / 12, i / 12, i / 12, 1));
147         }
148         Material cubeMaterial = new Material(EffectId("textured.vert", "textured.frag", null), "crate");
149         Model cubeDrawable = new Model(cubeMaterial, _mesh);
150         Node3d cubeNode = new Node3d("cubes", cubeDrawable);
151         _scene.addChild(cubeNode);
152 
153         debug(fbximport) {
154             // test FBX import
155             FbxModelImport importer;
156             string src = loadTextResource("suzanne.fbx");
157             importer.filename = "suzanne.fbx";
158             importer.parse(src);
159         }
160 
161         ObjModelImport importer;
162         string src = loadTextResource("suzanne.obj");
163         importer.parse(src);
164         Log.d("suzanne mesh:", importer.mesh.dumpVertexes(20));
165         Material suzanneMaterial = new Material(EffectId("colored.vert", "colored.frag", null), null); //"SPECULAR"
166         //suzanneMaterial.ambientColor = vec3(0.5, 0.5, 0.5);
167         suzanneMaterial.diffuseColor = vec4(0.7, 0.7, 0.5, 1.0);
168         //suzanneMaterial.specular = true;
169         Model suzanneDrawable = new Model(suzanneMaterial, importer.mesh);
170         suzanneNode = new Node3d("suzanne", suzanneDrawable);
171         suzanneNode.translate(vec3(2, 2, -5));
172         _scene.addChild(suzanneNode);
173 
174 
175         brickNode = new Node3d("brick");
176         brickNode.translate(vec3(-2, 2, -3));
177         Mesh brickMesh = Mesh.createCubeMesh(vec3(0, 0, 0), 0.8, vec4(0.8, 0.8, 0.8, 1));
178         Material brickMaterial = new Material(EffectId("textured.vert", "textured.frag", null), "brick", "brickn"); // with bump mapping
179         //brickMaterial.specular = true;
180         brickNode.drawable = new Model(brickMaterial, brickMesh);
181         _scene.addChild(brickNode);
182 
183     }
184 
185     Node3d dirLightNode;
186     Node3d suzanneNode;
187     Node3d brickNode;
188 
189     float rotationX;
190     float rotationY;
191     float rotationZ;
192     float translationX;
193     float translationY;
194     float translationZ;
195     float near;
196     float far;
197 
198     /// handle scroll event
199     bool onScrollEvent(AbstractSlider source, ScrollEvent event) {
200         controlsToVars();
201         return true;
202     }
203 
204     void assignHandlers() {
205         childById!ScrollBar("sbNear").scrollEvent = &onScrollEvent;
206         childById!ScrollBar("sbFar").scrollEvent = &onScrollEvent;
207         childById!ScrollBar("sbRotationX").scrollEvent = &onScrollEvent;
208         childById!ScrollBar("sbRotationY").scrollEvent = &onScrollEvent;
209         childById!ScrollBar("sbRotationZ").scrollEvent = &onScrollEvent;
210         childById!ScrollBar("sbTranslationX").scrollEvent = &onScrollEvent;
211         childById!ScrollBar("sbTranslationY").scrollEvent = &onScrollEvent;
212         childById!ScrollBar("sbTranslationZ").scrollEvent = &onScrollEvent;
213     }
214 
215     void controlsToVars() {
216         near = childById!ScrollBar("sbNear").position / 10.0f;
217         far = childById!ScrollBar("sbFar").position / 10.0f;
218         translationX = childById!ScrollBar("sbTranslationX").position / 10.0f;
219         translationY = childById!ScrollBar("sbTranslationY").position / 10.0f;
220         translationZ = childById!ScrollBar("sbTranslationZ").position / 10.0f;
221         rotationX = childById!ScrollBar("sbRotationX").position;
222         rotationY = childById!ScrollBar("sbRotationY").position;
223         rotationZ = childById!ScrollBar("sbRotationZ").position;
224         childById("lblNear").text = to!dstring(near);
225         childById("lblFar").text = to!dstring(far);
226         childById("lblTranslationX").text = to!dstring(translationX);
227         childById("lblTranslationY").text = to!dstring(translationY);
228         childById("lblTranslationZ").text = to!dstring(translationZ);
229         childById("lblRotationX").text = to!dstring(rotationX);
230         childById("lblRotationY").text = to!dstring(rotationY);
231         childById("lblRotationZ").text = to!dstring(rotationZ);
232     }
233 
234     /// returns true is widget is being animated - need to call animate() and redraw
235     @property override bool animating() { return true; }
236     /// animates window; interval is time left from previous draw, in hnsecs (1/10000000 of second)
237     override void animate(long interval) {
238         //Log.d("animating");
239         _cam.rotateX(0.01);
240         _cam.rotateY(0.02);
241         angle += interval * 0.000002f;
242         invalidate();
243         suzanneNode.rotateY(interval * 0.000002f);
244         brickNode.rotateY(interval * 0.00000123f);
245         brickNode.rotateZ(interval * 0.0000004123f);
246         brickNode.rotateX(interval * 0.0000007543f);
247     }
248     float angle = 0;
249 
250     Scene3dRef _scene;
251     Camera _cam;
252 
253     /// this is OpenGLDrawableDelegate implementation
254     private void doDraw(Rect windowRect, Rect rc) {
255         _cam.setPerspective(rc.width, rc.height, 45.0f, near, far);
256         _cam.setIdentity();
257         _cam.translateX(translationX);
258         _cam.translateY(translationY);
259         _cam.translateZ(translationZ);
260         _cam.rotateX(rotationX);
261         _cam.rotateY(rotationY);
262         _cam.rotateZ(rotationZ);
263 
264         checkgl!glEnable(GL_CULL_FACE);
265         //checkgl!glDisable(GL_CULL_FACE);
266         checkgl!glEnable(GL_DEPTH_TEST);
267         checkgl!glCullFace(GL_BACK);
268 
269         _scene.drawScene(false);
270 
271         checkgl!glDisable(GL_DEPTH_TEST);
272         checkgl!glDisable(GL_CULL_FACE);
273     }
274 
275     ~this() {
276     }
277 }