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;