From 846c25c652bd7d42fead9b85ac1e70b9bfff8cb6 Mon Sep 17 00:00:00 2001 From: Vadim Lopatin Date: Mon, 4 Apr 2016 14:56:58 +0300 Subject: [PATCH] #183 light bindings --- src/dlangui/core/math3d.d | 11 ++ src/dlangui/graphics/glsupport.d | 48 ++++++ src/dlangui/graphics/scene/effect.d | 2 + src/dlangui/graphics/scene/light.d | 206 ++++++++++++++++++++++++- src/dlangui/graphics/scene/material.d | 11 ++ src/dlangui/graphics/scene/mesh.d | 41 ++++- src/dlangui/graphics/scene/model.d | 16 ++ src/dlangui/graphics/scene/node.d | 21 +++ src/dlangui/graphics/scene/objimport.d | 4 +- src/dlangui/graphics/scene/scene3d.d | 19 +++ 10 files changed, 369 insertions(+), 10 deletions(-) diff --git a/src/dlangui/core/math3d.d b/src/dlangui/core/math3d.d index 767983e2..b1a85c01 100644 --- a/src/dlangui/core/math3d.d +++ b/src/dlangui/core/math3d.d @@ -1595,6 +1595,17 @@ struct mat4 { return (a0 * b5 - a1 * b4 + a2 * b3 + a3 * b2 - a4 * b1 + a5 * b0); } + @property vec3 forwardVector() const + { + return vec3(-m[8], -m[9], -m[10]); + } + + @property vec3 backVector() const + { + return vec3(m[8], m[9], m[10]); + } + + static __gshared const mat4 IDENTITY; } diff --git a/src/dlangui/graphics/glsupport.d b/src/dlangui/graphics/glsupport.d index f807d076..538ee9ab 100644 --- a/src/dlangui/graphics/glsupport.d +++ b/src/dlangui/graphics/glsupport.d @@ -319,6 +319,14 @@ class GLProgram : dlangui.graphics.scene.mesh.GraphicsEffect { return res; } + override void setUniform(string uniformName, const vec2[] vec) { + checkgl!glUniform2fv(getUniformLocation(uniformName), cast(int)vec.length, vec[0].vec.ptr); + } + + override void setUniform(DefaultUniform id, const vec2[] vec) { + checkgl!glUniform2fv(getUniformLocation(id), cast(int)vec.length, vec[0].vec.ptr); + } + override void setUniform(string uniformName, vec2 vec) { checkgl!glUniform2fv(getUniformLocation(uniformName), 1, vec.vec.ptr); } @@ -335,6 +343,14 @@ class GLProgram : dlangui.graphics.scene.mesh.GraphicsEffect { checkgl!glUniform3fv(getUniformLocation(id), 1, vec.vec.ptr); } + override void setUniform(string uniformName, const vec3[] vec) { + checkgl!glUniform3fv(getUniformLocation(uniformName), cast(int)vec.length, vec[0].vec.ptr); + } + + override void setUniform(DefaultUniform id, const vec3[] vec) { + checkgl!glUniform3fv(getUniformLocation(id), cast(int)vec.length, vec[0].vec.ptr); + } + override void setUniform(string uniformName, vec4 vec) { checkgl!glUniform4fv(getUniformLocation(uniformName), 1, vec.vec.ptr); } @@ -343,6 +359,14 @@ class GLProgram : dlangui.graphics.scene.mesh.GraphicsEffect { checkgl!glUniform4fv(getUniformLocation(id), 1, vec.vec.ptr); } + override void setUniform(string uniformName, const vec4[] vec) { + checkgl!glUniform4fv(getUniformLocation(uniformName), cast(int)vec.length, vec[0].vec.ptr); + } + + override void setUniform(DefaultUniform id, const vec4[] vec) { + checkgl!glUniform4fv(getUniformLocation(id), cast(int)vec.length, vec[0].vec.ptr); + } + override void setUniform(string uniformName, ref const(mat4) matrix) { checkgl!glUniformMatrix4fv(getUniformLocation(uniformName), 1, false, matrix.m.ptr); } @@ -351,6 +375,30 @@ class GLProgram : dlangui.graphics.scene.mesh.GraphicsEffect { checkgl!glUniformMatrix4fv(getUniformLocation(id), 1, false, matrix.m.ptr); } + override void setUniform(string uniformName, const(mat4)[] matrix) { + checkgl!glUniformMatrix4fv(getUniformLocation(uniformName), cast(int)matrix.length, false, matrix[0].m.ptr); + } + + override void setUniform(DefaultUniform id, const(mat4)[] matrix) { + checkgl!glUniformMatrix4fv(getUniformLocation(id), cast(int)matrix.length, false, matrix[0].m.ptr); + } + + override void setUniform(string uniformName, float v) { + checkgl!glUniform1f(getUniformLocation(uniformName), v); + } + + override void setUniform(DefaultUniform id, float v) { + checkgl!glUniform1f(getUniformLocation(id), v); + } + + override void setUniform(string uniformName, const float[] v) { + checkgl!glUniform1fv(getUniformLocation(uniformName), cast(int)v.length, v.ptr); + } + + override void setUniform(DefaultUniform id, const float[] v) { + checkgl!glUniform1fv(getUniformLocation(id), cast(int)v.length, v.ptr); + } + /// returns true if effect has uniform override bool hasUniform(DefaultUniform id) { return getUniformLocation(id) >= 0; diff --git a/src/dlangui/graphics/scene/effect.d b/src/dlangui/graphics/scene/effect.d index a9a4247a..c0c0b859 100644 --- a/src/dlangui/graphics/scene/effect.d +++ b/src/dlangui/graphics/scene/effect.d @@ -137,6 +137,8 @@ class Effect : GLProgram { return getAttribLocation(DefaultAttribute.a_color); case TEXCOORD0: return getAttribLocation(DefaultAttribute.a_texCoord); + case NORMAL: + return getAttribLocation(DefaultAttribute.a_normal); default: return super.getVertexElementLocation(type); } diff --git a/src/dlangui/graphics/scene/light.d b/src/dlangui/graphics/scene/light.d index d4c9f2b6..ad960030 100644 --- a/src/dlangui/graphics/scene/light.d +++ b/src/dlangui/graphics/scene/light.d @@ -2,6 +2,9 @@ module dlangui.graphics.scene.light; import dlangui.core.math3d; import dlangui.core.types; +import dlangui.graphics.scene.node; + +import std.conv : to; enum LightType : ubyte { directional, @@ -12,12 +15,39 @@ enum LightType : ubyte { /// Reference counted Light object alias LightRef = Ref!Light; -class Light : RefCountedObject { +class Light : RefCountedObject { + + protected Node3d _node; + protected vec3 _color; - protected this(vec3 color) {} - @property vec3 color() { return _color; } + + protected bool _autobind = true; + protected bool _enabled = true; + + protected this(vec3 color) { _color = color; } + + @property vec3 color() const { return _color; } @property Light color(vec3 c) { _color = c; return this; } - @property LightType type() { return LightType.directional; } + @property LightType type() const { return LightType.directional; } + + @property bool autobind() const { return _autobind; } + @property Light autobind(bool flg) { _autobind = flg; return this; } + + @property bool enabled() const { return _enabled; } + @property Light enabled(bool flg) { _enabled = flg; return this; } + + @property Node3d node() { return _node; } + @property Light node(Node3d n) { _node = n; return this; } + + /// direction in world coordinates + @property vec3 direction() { return _node ? _node.forwardVectorWorld : vec3(0, 0, 1); } + /// position in world coordinates + @property vec3 position() { return _node ? _node.translationWorld : vec3(0, 0, 0); } + + @property float range() const { return 1.0; } + @property void range(float v) { assert(false); } + @property float rangeInverse() const { return 1.0; } + /// create new directional light static Light createDirectional(vec3 color) { return new DirectionalLight(color); @@ -29,3 +59,171 @@ protected class DirectionalLight : Light { super(color); } } + +protected class PointLight : Light { + protected float _range = 1; + protected float _rangeInverse = 1; + protected this(vec3 color, float range = 1) { + super(color); + _range = range; + _rangeInverse = 1 / range; + } + + override @property LightType type() const { return LightType.point; } + + override @property float range() const { return _range; } + override @property void range(float v) { + _range = v; + _rangeInverse = 1 / v; + } + override @property float rangeInverse() const { return _rangeInverse; } +} + +alias LightCounts = int[3]; + +/// light collection +struct Lights { + Light[] directional; + Light[] point; + Light[] spot; + void reset() { + directional = null; + point = null; + spot = null; + } + @property bool empty() { return directional.length + point.length + spot.length > 0; } + /// returns point types by type + @property LightCounts counts() const { return [cast(int)directional.length, cast(int)point.length, cast(int)spot.length]; } + @property int directionalCount() const { return cast(int)directional.length; } + @property int pointCount() const { return cast(int)point.length; } + @property int spotCount() const { return cast(int)spot.length; } + /// return light count definition for shaders, e.g. "DIRECTIONAL_LIGHT_COUNT 2;POINT_LIGHT_COUNT 1" + @property string defs() const { + if (!directional.length && !point.length && !spot.length) + return null; + char[] buf; + if (directional.length) { + buf ~= "DIRECTIONAL_LIGHT_COUNT "; + buf ~= directional.length.to!string; + } + if (point.length) { + if (buf) + buf ~= ";"; + buf ~= "POINT_LIGHT_COUNT "; + buf ~= point.length.to!string; + } + if (spot.length) { + if (buf) + buf ~= ";"; + buf ~= "SPOT_LIGHT_COUNT "; + buf ~= spot.length.to!string; + } + return cast(string)buf; + } + void remove(Light light) { + import std.algorithm : remove; + switch(light.type) { + case LightType.directional: + foreach(index, v; directional) + if (v is light) { + directional = directional.remove(index); + return; + } + directional ~= light; + break; + case LightType.point: + foreach(index, v; point) + if (v is light) { + point = point.remove(index); + return; + } + point ~= light; + break; + case LightType.spot: + foreach(index, v; spot) + if (v is light) { + spot = spot.remove(index); + return; + } + spot ~= light; + break; + default: + break; + } + } + /// returns true if light is added (not a duplicate, and enabled) + bool add(Light light) { + switch(light.type) { + case LightType.directional: + foreach(v; directional) + if (v is light) + return false; + directional ~= light; + return true; + case LightType.point: + foreach(v; point) + if (v is light) + return false; + point ~= light; + return true; + case LightType.spot: + foreach(v; spot) + if (v is light) + return false; + spot ~= light; + return true; + default: + return false; + } + } + Lights clone() { + Lights res; + if (directional.length) + res.directional ~= directional; + if (point.length) + res.point ~= point; + if (spot.length) + res.spot ~= spot; + return res; + } +} + +struct LightParams { + Lights _lights; + + /// returns true if light is added (not a duplicate, and enabled) + bool add(Light light) { + if (!light.node || !light.enabled || !_lights.add(light)) + return false; + switch(light.type) { + case LightType.directional: + u_directionalLightDirection ~= light.direction; + u_directionalLightColor ~= light.color; + return true; + case LightType.point: + u_pointLightPosition ~= light.position; + u_pointLightColor ~= light.color; + u_pointLightRangeInverse ~= light.rangeInverse; + return true; + case LightType.spot: + // TODO + return true; + default: + return false; + } + } + + vec3[] u_directionalLightDirection; + vec3[] u_directionalLightColor; + + vec3[] u_pointLightPosition; + vec3[] u_pointLightColor; + float[] u_pointLightRangeInverse; + + vec3[] u_spotLightPosition; + vec3[] u_spotLightDirection; + vec3[] u_spotLightColor; + float[] u_spotLightRangeInverse; + float[] u_spotLightInnerAngleCos; + float[] u_spotLightOuterAngleCos; +} diff --git a/src/dlangui/graphics/scene/material.d b/src/dlangui/graphics/scene/material.d index b6960f0a..c4bcd91b 100644 --- a/src/dlangui/graphics/scene/material.d +++ b/src/dlangui/graphics/scene/material.d @@ -9,6 +9,7 @@ import dlangui.graphics.gldrawbuf; import dlangui.graphics.scene.effect; import dlangui.graphics.scene.node; import dlangui.graphics.scene.mesh; +import dlangui.graphics.scene.light; /// Reference counted Material object alias MaterialRef = Ref!Material; @@ -25,6 +26,8 @@ class Material : RefCountedObject { // colors protected vec4 _diffuseColor = vec4(1, 1, 1, 1); protected vec3 _ambientColor = vec3(1, 1, 1); + protected vec4 _modulateColor = vec4(1, 1, 1, 1); + protected float _modulateAlpha = 1; // TODO: more material properties @@ -40,6 +43,10 @@ class Material : RefCountedObject { @property Material diffuseColor(vec4 color) { _diffuseColor = color; return this; } @property vec3 ambientColor() { return _ambientColor; } @property Material ambientColor(vec3 color) { _ambientColor = color; return this; } + @property vec4 modulateColor() { return _modulateColor; } + @property Material modulateColor(vec4 color) { _modulateColor = color; return this; } + @property float modulateAlpha() { return _modulateAlpha; } + @property Material modulateColor(float a) { _modulateAlpha = a; return this; } @property EffectRef effect() { if (_effect.isNull && !_effectId.empty) @@ -98,6 +105,10 @@ class Material : RefCountedObject { _effect.setUniform(DefaultUniform.u_ambientColor, _ambientColor); if (_effect.hasUniform(DefaultUniform.u_diffuseColor)) _effect.setUniform(DefaultUniform.u_diffuseColor, _diffuseColor); + if (_effect.hasUniform(DefaultUniform.u_modulateColor)) + _effect.setUniform(DefaultUniform.u_modulateColor, _modulateColor); + if (_effect.hasUniform(DefaultUniform.u_modulateAlpha)) + _effect.setUniform(DefaultUniform.u_modulateAlpha, _modulateAlpha); } void drawMesh(Mesh mesh) { diff --git a/src/dlangui/graphics/scene/mesh.d b/src/dlangui/graphics/scene/mesh.d index 2909f285..5745e23c 100644 --- a/src/dlangui/graphics/scene/mesh.d +++ b/src/dlangui/graphics/scene/mesh.d @@ -15,20 +15,44 @@ abstract class GraphicsEffect : RefCountedObject { void setUniform(string uniformName, ref const(mat4) matrix); + void setUniform(string uniformName, const(mat4)[] matrix); + + void setUniform(string uniformName, float v); + + void setUniform(string uniformName, const float v[]); + void setUniform(string uniformName, vec2 vec); + void setUniform(string uniformName, const vec2[] vec); + void setUniform(string uniformName, vec3 vec); + void setUniform(string uniformName, const vec3[] vec); + void setUniform(string uniformName, vec4 vec); + void setUniform(string uniformName, const vec4[] vec); + void setUniform(DefaultUniform id, ref const(mat4) matrix); + void setUniform(DefaultUniform id, const(mat4)[] matrix); + + void setUniform(DefaultUniform id, float v); + + void setUniform(DefaultUniform id, const float[] v); + void setUniform(DefaultUniform id, vec2 vec); + void setUniform(DefaultUniform id, const vec2[] vec); + void setUniform(DefaultUniform id, vec3 vec); + void setUniform(DefaultUniform id, const vec3[] vec); + void setUniform(DefaultUniform id, vec4 vec); + void setUniform(DefaultUniform id, const vec4[] vec); + /// returns true if effect has uniform bool hasUniform(DefaultUniform id); @@ -42,7 +66,13 @@ enum DefaultUniform : int { // colors u_ambientColor, // vec3 u_diffuseColor, // vec4 - u_lightmapTexture, // sampler2D + + // textures + u_diffuseTexture, //uniform sampler2D u_diffuseTexture; + u_lightmapTexture, //uniform sampler2D u_lightmapTexture; + u_normalmapTexture, //uniform sampler2D u_normalmapTexture; + + // lights u_directionalLightColor, //uniform vec3 u_directionalLightColor[DIRECTIONAL_LIGHT_COUNT]; u_directionalLightDirection, //uniform vec3 u_directionalLightDirection[DIRECTIONAL_LIGHT_COUNT]; u_pointLightColor, //uniform vec3 u_pointLightColor[POINT_LIGHT_COUNT]; @@ -52,7 +82,9 @@ enum DefaultUniform : int { u_spotLightRangeInverse, //uniform float u_spotLightRangeInverse[SPOT_LIGHT_COUNT]; u_spotLightInnerAngleCos, //uniform float u_spotLightInnerAngleCos[SPOT_LIGHT_COUNT]; u_spotLightOuterAngleCos, //uniform float u_spotLightOuterAngleCos[SPOT_LIGHT_COUNT]; + u_spotLightPosition, //uniform vec3 u_spotLightPosition[SPOT_LIGHT_COUNT]; u_spotLightDirection, //uniform vec3 u_spotLightDirection[SPOT_LIGHT_COUNT]; + u_specularExponent, //uniform float u_specularExponent; u_modulateColor, //uniform vec4 u_modulateColor; u_modulateAlpha, //uniform float u_modulateAlpha; @@ -60,10 +92,11 @@ enum DefaultUniform : int { // matrix u_worldViewProjectionMatrix, //uniform mat4 u_worldViewProjectionMatrix; u_inverseTransposeWorldViewMatrix, //uniform mat4 u_inverseTransposeWorldViewMatrix; - u_worldViewMatrix, //uniform mat4 u_worldViewMatrix; - u_matrixPalette, //uniform vec4 u_matrixPalette[SKINNING_JOINT_COUNT * 3]; - u_cameraPosition, //uniform vec3 u_cameraPosition; u_worldMatrix, //uniform mat4 u_worldMatrix; + u_worldViewMatrix, //uniform mat4 u_worldViewMatrix; + u_cameraPosition, //uniform vec3 u_cameraPosition; + + u_matrixPalette, //uniform vec4 u_matrixPalette[SKINNING_JOINT_COUNT * 3]; u_clipPlane, //uniform vec4 u_clipPlane; } diff --git a/src/dlangui/graphics/scene/model.d b/src/dlangui/graphics/scene/model.d index ce87f827..f825afd6 100644 --- a/src/dlangui/graphics/scene/model.d +++ b/src/dlangui/graphics/scene/model.d @@ -3,10 +3,13 @@ module dlangui.graphics.scene.model; import dlangui.graphics.scene.drawableobject; import dlangui.graphics.scene.mesh; import dlangui.graphics.scene.material; +import dlangui.graphics.scene.light; class Model : DrawableObject { protected MaterialRef _material; protected MeshRef _mesh; + protected bool _autobindLights = true; + protected Lights _lights; this() { } @@ -19,6 +22,19 @@ class Model : DrawableObject { @property ref MaterialRef material() { return _material; } @property ref MeshRef mesh() { return _mesh; } + @property bool autobindLights() { return _autobindLights; } + @property Model autobindLights(bool flg) { _autobindLights = flg; return this; } + + Model bindLight(Light light) { + _lights.add(light); + return this; + } + + Model unbindLight(Light light) { + _lights.remove(light); + return this; + } + override void draw(Node3d node, bool wireframe) { /// override it _material.bind(node); diff --git a/src/dlangui/graphics/scene/node.d b/src/dlangui/graphics/scene/node.d index 440145cd..8713430b 100644 --- a/src/dlangui/graphics/scene/node.d +++ b/src/dlangui/graphics/scene/node.d @@ -42,6 +42,18 @@ class Node3d : Transform { /// light attached to node @property ref LightRef light() { return _light; } + /// attach light to node + @property Node3d light(Light v) { + if (_light.get is v) + return this; + Node3d oldNode = v.node; + v.node = this; + _light = v; + if (oldNode) + oldNode._light = null; + return this; + } + /// returns scene for node @property Scene3d scene() { if (_scene) @@ -162,4 +174,13 @@ class Node3d : Transform { worldMatrix.getTranslation(translation); return translation; } + + /** + * Returns the forward vector of the Node in world space. + * + * @return The forward vector in world space. + */ + @property vec3 forwardVectorWorld() { + return worldMatrix.forwardVector; + } } diff --git a/src/dlangui/graphics/scene/objimport.d b/src/dlangui/graphics/scene/objimport.d index 94df8eca..b019e59d 100644 --- a/src/dlangui/graphics/scene/objimport.d +++ b/src/dlangui/graphics/scene/objimport.d @@ -166,10 +166,10 @@ struct ObjModelImport { if (!mesh.isNull) return; if (_txCount) { - mesh = new Mesh(VertexFormat(VertexElementType.POSITION, VertexElementType.NORMAL, VertexElementType.COLOR, VertexElementType.TEXCOORD0)); + mesh = new Mesh(VertexFormat(VertexElementType.POSITION, VertexElementType.NORMAL, /*VertexElementType.COLOR, */ VertexElementType.TEXCOORD0)); _meshHasTexture = true; } else { - mesh = new Mesh(VertexFormat(VertexElementType.POSITION, VertexElementType.NORMAL, VertexElementType.COLOR)); + mesh = new Mesh(VertexFormat(VertexElementType.POSITION, VertexElementType.NORMAL /*, VertexElementType.COLOR*/)); _meshHasTexture = false; } } diff --git a/src/dlangui/graphics/scene/scene3d.d b/src/dlangui/graphics/scene/scene3d.d index 6eccef98..c59f46ff 100644 --- a/src/dlangui/graphics/scene/scene3d.d +++ b/src/dlangui/graphics/scene/scene3d.d @@ -3,6 +3,7 @@ module dlangui.graphics.scene.scene3d; import dlangui.core.types; import dlangui.graphics.scene.node; import dlangui.graphics.scene.camera; +import dlangui.graphics.scene.light; public import dlangui.core.math3d; @@ -47,6 +48,7 @@ class Scene3d : Node3d { protected bool _wireframe; void drawScene(bool wireframe) { _wireframe = wireframe; + updateAutoboundLights(); visit(this, &sceneDrawVisitor); } @@ -55,6 +57,23 @@ class Scene3d : Node3d { node.drawable.draw(node, _wireframe); return false; } + + void updateAutoboundLights() { + _lights.reset(); + visit(this, &lightBindingVisitor); + } + + protected bool lightBindingVisitor(Node3d node) { + if (!node.light.isNull && node.light.enabled && node.light.autobind) + _lights.add(node.light); + return false; + } + + protected Lights _lights; + + @property ref const(Lights) boundLights() { + return _lights; + } } /// depth-first recursive node traversion, stops if visitor returns true