diff --git a/dlangui-monod-linux.dproj b/dlangui-monod-linux.dproj
index e272d9da..1376d1e7 100644
--- a/dlangui-monod-linux.dproj
+++ b/dlangui-monod-linux.dproj
@@ -280,9 +280,11 @@
+
+
diff --git a/dlangui-monod-osx.dproj b/dlangui-monod-osx.dproj
index 087da1de..665ca7b0 100644
--- a/dlangui-monod-osx.dproj
+++ b/dlangui-monod-osx.dproj
@@ -139,9 +139,11 @@
+
+
diff --git a/dlangui-msvc.visualdproj b/dlangui-msvc.visualdproj
index 855550f2..cec418cb 100644
--- a/dlangui-msvc.visualdproj
+++ b/dlangui-msvc.visualdproj
@@ -771,9 +771,11 @@
+
+
diff --git a/examples/d3d/src/d3d.d b/examples/d3d/src/d3d.d
index e0db3c7d..3f7d392f 100644
--- a/examples/d3d/src/d3d.d
+++ b/examples/d3d/src/d3d.d
@@ -6,6 +6,8 @@ import dlangui.graphics.scene.camera;
import dlangui.graphics.scene.mesh;
import dlangui.graphics.scene.material;
import dlangui.graphics.scene.effect;
+import dlangui.graphics.scene.model;
+import dlangui.graphics.scene.node;
import dlangui.graphics.glsupport;
import dlangui.graphics.gldrawbuf;
import derelict.opengl3.gl3;
@@ -114,7 +116,7 @@ class UiWidget : VerticalLayout, CellVisitor {
int y0 = 0;
int z0 = 0;
- _mesh = Mesh.createCubeMesh(vec3(x0+ 0, y0 + 0, z0 + 0), 0.3f);
+ Mesh _mesh = Mesh.createCubeMesh(vec3(x0+ 0, y0 + 0, z0 + 0), 0.3f);
for (int i = 0; i < 10; i++) {
_mesh.addCubeMesh(vec3(x0+ 0, y0+0, z0+ i * 2 + 1.0f), 0.2f, vec4(i / 12, 1, 1, 1));
_mesh.addCubeMesh(vec3(x0+ i * 2 + 1.0f, y0+0, z0+ 0), 0.2f, vec4(1, i / 12, 1, 1));
@@ -126,6 +128,11 @@ class UiWidget : VerticalLayout, CellVisitor {
_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));
_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));
}
+ Material cubeMaterial = new Material(EffectId("textured.vert", "textured.frag", null), "crate");
+ Model cubeDrawable = new Model(cubeMaterial, _mesh);
+ Node3d cubeNode = new Node3d("cubes", cubeDrawable);
+ _scene.addChild(cubeNode);
+
_minerMesh = new Mesh(VertexFormat(VertexElementType.POSITION, VertexElementType.NORMAL, VertexElementType.COLOR, VertexElementType.TEXCOORD0));
_world = new World();
@@ -146,6 +153,12 @@ class UiWidget : VerticalLayout, CellVisitor {
_world.camPosition = Position(Vector3d(0, 3, 0), Vector3d(0, 0, 1));
updateMinerMesh();
+
+ Material minerMaterial = new Material(EffectId("textured.vert", "textured.frag", null), "blocks");
+ Model minerDrawable = new Model(minerMaterial, _minerMesh);
+ Node3d minerNode = new Node3d("miner", minerDrawable);
+ _scene.addChild(minerNode);
+
//CellVisitor visitor = new TestVisitor();
//Log.d("Testing cell visitor");
//long ts = currentTimeMillis;
@@ -229,28 +242,13 @@ class UiWidget : VerticalLayout, CellVisitor {
}
float angle = 0;
- EffectRef _program;
- Scene3d _scene;
+ Scene3dRef _scene;
Camera _cam;
- Mesh _mesh;
Mesh _minerMesh;
- GLTexture _tx;
- GLTexture _blockstx;
/// this is OpenGLDrawableDelegate implementation
private void doDraw(Rect windowRect, Rect rc) {
- if (_program.isNull) {
- _program = EffectCache.instance.get("textured.vert", "textured.frag");
- }
- if (!_tx)
- _tx = new GLTexture("crate");
- if (!_blockstx)
- _blockstx = new GLTexture("blocks");
- if (!_tx.isValid || !_blockstx.isValid) {
- Log.e("Invalid texture");
- return;
- }
_cam.setPerspective(rc.width, rc.height, 45.0f, near, far);
_cam.setIdentity();
//_cam.translate(vec3(
@@ -329,33 +327,13 @@ class UiWidget : VerticalLayout, CellVisitor {
checkgl!glEnable(GL_DEPTH_TEST);
checkgl!glCullFace(GL_BACK);
- _program.bind();
- _program.setUniform("matrix", projectionViewModelMatrix);
- _tx.texture.setup();
- _tx.texture.setSamplerParams(true);
+ _scene.drawScene(false);
- _program.draw(_mesh);
-
- _tx.texture.unbind();
-
- _blockstx.texture.setup();
- _blockstx.texture.setSamplerParams(false);
-
- _program.draw(_minerMesh);
-
- _blockstx.texture.unbind();
-
- _program.unbind();
checkgl!glDisable(GL_DEPTH_TEST);
checkgl!glDisable(GL_CULL_FACE);
}
~this() {
- destroy(_scene);
- if (_program)
- destroy(_program);
- if (_tx)
- destroy(_tx);
destroy(_world);
}
}
diff --git a/src/dlangui/graphics/scene/camera.d b/src/dlangui/graphics/scene/camera.d
index 5b5a10ca..ecf5d485 100644
--- a/src/dlangui/graphics/scene/camera.d
+++ b/src/dlangui/graphics/scene/camera.d
@@ -15,7 +15,8 @@ class Camera : Node3d {
protected bool _enabled;
- this() {
+ this(string ID = null) {
+ super(ID);
_enabled = true;
setPerspective(4.0f, 3.0f, 45.0f, 0.1f, 100.0f);
}
@@ -30,6 +31,11 @@ class Camera : Node3d {
_enabled = v;
}
+ /// returns true if some changes occured in projection or view matrix since last matrix getter call
+ @property bool viewChanged() {
+ return _dirtyTransform || _dirtyViewProjection || _dirtyView;
+ }
+
/// get projection matrix
@property ref const(mat4) projectionMatrix() {
return _projectionMatrix;
diff --git a/src/dlangui/graphics/scene/effect.d b/src/dlangui/graphics/scene/effect.d
index 806f07b2..f018b9f2 100644
--- a/src/dlangui/graphics/scene/effect.d
+++ b/src/dlangui/graphics/scene/effect.d
@@ -12,40 +12,6 @@ import dlangui.graphics.scene.mesh;
/// Reference counted Effect object
alias EffectRef = Ref!Effect;
-/// Effect ID
-struct EffectId {
- string vertexShaderName;
- string fragmentShaderName;
- string definitions;
- this(string vertexShader, string fragmentShader, string defs) {
- vertexShaderName = vertexShader;
- fragmentShaderName = fragmentShader;
- definitions = defs;
- }
-
- size_t toHash() const @safe pure nothrow
- {
- size_t hash;
- foreach (char c; vertexShaderName)
- hash = (hash * 9) + c;
- hash = (hash * 31) + 198237283;
- foreach (char c; fragmentShaderName)
- hash = (hash * 9) + c;
- hash = (hash * 31) + 84574112;
- foreach (char c; definitions)
- hash = (hash * 9) + c;
- return hash;
- }
-
- bool opEquals(ref const EffectId s) const @safe pure nothrow
- {
- return
- std.string.cmp(this.vertexShaderName, s.vertexShaderName) == 0 &&
- std.string.cmp(this.fragmentShaderName, s.fragmentShaderName) == 0 &&
- std.string.cmp(this.definitions, s.definitions) == 0;
- }
-}
-
/// Effect (aka OpenGL program)
class Effect : GLProgram {
EffectId _id;
@@ -181,3 +147,43 @@ class EffectCache {
}
}
+
+/// Effect ID
+struct EffectId {
+ string vertexShaderName;
+ string fragmentShaderName;
+ string definitions;
+ this(string vertexShader, string fragmentShader, string defs) {
+ vertexShaderName = vertexShader;
+ fragmentShaderName = fragmentShader;
+ definitions = defs;
+ }
+
+ /// returns true if ID is not assigned
+ @property bool empty() {
+ return !vertexShaderName.length || !vertexShaderName.length;
+ }
+
+ size_t toHash() const @safe pure nothrow
+ {
+ size_t hash;
+ foreach (char c; vertexShaderName)
+ hash = (hash * 9) + c;
+ hash = (hash * 31) + 198237283;
+ foreach (char c; fragmentShaderName)
+ hash = (hash * 9) + c;
+ hash = (hash * 31) + 84574112;
+ foreach (char c; definitions)
+ hash = (hash * 9) + c;
+ return hash;
+ }
+
+ bool opEquals(ref const EffectId s) const @safe pure nothrow
+ {
+ return
+ std.string.cmp(this.vertexShaderName, s.vertexShaderName) == 0 &&
+ std.string.cmp(this.fragmentShaderName, s.fragmentShaderName) == 0 &&
+ std.string.cmp(this.definitions, s.definitions) == 0;
+ }
+}
+
diff --git a/src/dlangui/graphics/scene/material.d b/src/dlangui/graphics/scene/material.d
index ee44c3a3..f9c6e525 100644
--- a/src/dlangui/graphics/scene/material.d
+++ b/src/dlangui/graphics/scene/material.d
@@ -7,16 +7,32 @@ import dlangui.core.logger;
import dlangui.graphics.glsupport;
import dlangui.graphics.gldrawbuf;
import dlangui.graphics.scene.effect;
+import dlangui.graphics.scene.node;
+import dlangui.graphics.scene.mesh;
/// Reference counted Material object
alias MaterialRef = Ref!Material;
class Material : RefCountedObject {
protected EffectRef _effect;
+ protected EffectId _effectId;
protected TextureRef _texture;
+ protected string _textureId;
// TODO: more material properties
- @property EffectRef effect() { return _effect; }
+ this() {
+ }
+
+ this(EffectId effectId, string textureId) {
+ _effectId = effectId;
+ _textureId = textureId;
+ }
+
+ @property EffectRef effect() {
+ if (_effect.isNull && !_effectId.empty)
+ _effect = EffectCache.instance.get(_effectId);
+ return _effect;
+ }
/// set as effect instance
@property Material effect(EffectRef e) {
_effect = e;
@@ -24,11 +40,19 @@ class Material : RefCountedObject {
}
/// set as effect id
@property Material effect(EffectId effectId) {
- _effect = EffectCache.instance.get(effectId);
+ if (_effectId == effectId)
+ return this; // no change
+ _effectId = effectId;
+ _effect.clear();
return this;
}
- @property TextureRef texture() { return _texture; }
+ @property TextureRef texture() {
+ if (_texture.isNull && _textureId.length) {
+ _texture = GLTextureCache.instance.get(_textureId);
+ }
+ return _texture;
+ }
/// set texture
@property Material texture(TextureRef e) {
_texture = e;
@@ -36,7 +60,32 @@ class Material : RefCountedObject {
}
/// set texture from resourceId
@property Material texture(string resourceId) {
- _texture = GLTextureCache.instance.get(resourceId);
+ if (_textureId == resourceId)
+ return this; // no change
+ _texture.clear();
+ _textureId = resourceId;
return this;
}
+
+ void bind(Node3d node) {
+ assert(!effect.isNull);
+ effect.bind();
+ if (!texture.isNull) {
+ texture.texture.setup();
+ texture.texture.setSamplerParams(true);
+ }
+ // TODO: more uniforms
+ _effect.setUniform("matrix", node.projectionViewModelMatrix);
+ }
+
+ void drawMesh(Mesh mesh) {
+ effect.draw(mesh);
+ }
+
+ void unbind() {
+ if (!texture.isNull) {
+ texture.texture.unbind();
+ }
+ effect.unbind();
+ }
}
diff --git a/src/dlangui/graphics/scene/node.d b/src/dlangui/graphics/scene/node.d
index 1365a22a..a81c0952 100644
--- a/src/dlangui/graphics/scene/node.d
+++ b/src/dlangui/graphics/scene/node.d
@@ -5,21 +5,33 @@ import dlangui.core.math3d;
import dlangui.graphics.scene.transform;
import dlangui.core.collections;
import dlangui.graphics.scene.scene3d;
+import dlangui.graphics.scene.drawableobject;
/// 3D scene node
class Node3d : Transform {
protected Node3d _parent;
protected Scene3d _scene;
protected string _id;
+ protected DrawableObjectRef _drawable;
protected mat4 _worldMatrix;
protected ObjectList!Node3d _children;
- this() {
+ this(string id = null) {
super();
+ _id = id;
}
+ this(string id, DrawableObject drawable) {
+ super();
+ _id = id;
+ _drawable = drawable;
+ }
+
+ /// drawable attached to node
+ @property ref DrawableObjectRef drawable() { return _drawable; }
+
/// returns scene for node
@property Scene3d scene() {
if (_scene)
@@ -41,11 +53,12 @@ class Node3d : Transform {
return _children[index];
}
- /// add child node
- void addChild(Node3d node) {
+ /// add child node, return current node
+ Node3d addChild(Node3d node) {
_children.add(node);
node.parent = this;
node.scene = scene;
+ return this;
}
/// removes and destroys child node by index
@@ -53,6 +66,8 @@ class Node3d : Transform {
destroy(_children.remove(index));
}
+ @property ref ObjectList!Node3d children() { return _children; }
+
/// parent node
@property Node3d parent() {
return _parent;
@@ -60,6 +75,7 @@ class Node3d : Transform {
@property Node3d parent(Node3d v) {
_parent = v;
+ _scene = v.scene;
return this;
}
/// id of node
@@ -71,4 +87,9 @@ class Node3d : Transform {
_id = v;
return this;
}
+
+ /// returns projectionMatrix * viewMatrix * modelMatrix
+ @property mat4 projectionViewModelMatrix() {
+ return _scene.projectionViewMatrix * matrix;
+ }
}
diff --git a/src/dlangui/graphics/scene/scene3d.d b/src/dlangui/graphics/scene/scene3d.d
index c9dc0dbd..586c10b3 100644
--- a/src/dlangui/graphics/scene/scene3d.d
+++ b/src/dlangui/graphics/scene/scene3d.d
@@ -1,10 +1,14 @@
module dlangui.graphics.scene.scene3d;
+import dlangui.core.types;
import dlangui.graphics.scene.node;
import dlangui.graphics.scene.camera;
public import dlangui.core.math3d;
+/// Reference counted Scene3d object
+alias Scene3dRef = Ref!Scene3d;
+
/// 3D scene
class Scene3d : Node3d {
@@ -47,6 +51,29 @@ class Scene3d : Node3d {
static mat4 dummyIdentityMatrix;
return dummyIdentityMatrix;
}
+
+ protected bool _wireframe;
+ void drawScene(bool wireframe) {
+ _wireframe = wireframe;
+ visit(this, &sceneDrawVisitor);
+ }
+
+ protected bool sceneDrawVisitor(Node3d node) {
+ if (!node.drawable.isNull)
+ node.drawable.draw(node, _wireframe);
+ return false;
+ }
}
-
+/// depth-first recursive node traversion, stops if visitor returns true
+bool visit(Node3d node, bool delegate(Node3d node) visitor) {
+ bool res = visitor(node);
+ if (res)
+ return true;
+ foreach(child; node.children) {
+ bool res = visit(child, visitor);
+ if (res)
+ return true;
+ }
+ return false;
+}
diff --git a/src/dlangui/graphics/scene/transform.d b/src/dlangui/graphics/scene/transform.d
index 16a02178..826cf746 100644
--- a/src/dlangui/graphics/scene/transform.d
+++ b/src/dlangui/graphics/scene/transform.d
@@ -1,9 +1,10 @@
module dlangui.graphics.scene.transform;
import dlangui.core.math3d;
+import dlangui.core.types;
/// 3d transform: scale + translation + rotation
-class Transform {
+class Transform : RefCountedObject {
// transform flags
protected bool _dirtyTransform = true;
protected bool _hasScale = false;