diff --git a/Render.Test/tests/Rendering.Test.cs b/Render.Test/tests/Rendering.Test.cs index a6366af..335b94f 100644 --- a/Render.Test/tests/Rendering.Test.cs +++ b/Render.Test/tests/Rendering.Test.cs @@ -23,7 +23,7 @@ public class RenderingTest { // Create object var mesh = new Geometry.Primitives.Cube(size: 1, centre: Vec3.Zero); - var obj = new Renderable(mesh: mesh, uv: UV.Spherical(mesh), material: new Wireframe(Color.Red)); + var obj = new MeshRenderer(mesh: mesh, uv: UV.Spherical(mesh), material: new Wireframe(Color.Red)); scene.Add(obj); // Render @@ -42,9 +42,9 @@ public class RenderingTest { var bblock = Transformation.Scale(new Vec3(0.2, 1, 0.2)) * new Cube(size: 1, centre: Vec3.Zero); var ball = new Sphere(radius: 0.2, centre: Vec3.Zero); - var aobj = new Renderable(mesh: ablock, material: new UnlitColour(Color.Red)); - var bobj = new Renderable(mesh: bblock, material: new UnlitColour(Color.Blue)); - var ballobj = new Renderable(mesh: ball, material: new UnlitColour(Color.Yellow)); + var aobj = new MeshRenderer(mesh: ablock, material: new UnlitColour(Color.Red)); + var bobj = new MeshRenderer(mesh: bblock, material: new UnlitColour(Color.Blue)); + var ballobj = new MeshRenderer(mesh: ball, material: new UnlitColour(Color.Yellow)); aobj.Transform = Transformation.Offset(new Vec3(-1, 0, 0)); bobj.Transform = Transformation.Offset(new Vec3(1, 0, 0)); @@ -68,6 +68,8 @@ public class RenderingTest { camera.Skybox = new GradientSkybox(Color.FromArgb (255, 58, 58, 82), Color.FromArgb (255, 2, 1, 17)); // Create objects + var helper = new AxisHelper(); + var planet = new Geometry.Primitives.Sphere(radius: 1, centre: Vec3.Zero, horizontalResolution: 32, verticalResolution: 32); var planetTexture = LoadImage("assets/planet.png"); @@ -77,13 +79,19 @@ public class RenderingTest { var hierarchy = new SceneNode(); scene.Add(hierarchy); - var planetObj = new Renderable(mesh: planet, uv: UV.Spherical(planet), material: new UnlitTexture(planetTexture)); + var planetObj = new MeshRenderer(mesh: planet, uv: UV.Spherical(planet), material: new UnlitTexture(planetTexture)); hierarchy.Add(planetObj); - var satelliteObj = new Renderable(mesh: satellite, uv: UV.Spherical(satellite), material: new UnlitTexture(satelliteTexture)); + var satelliteObj = new MeshRenderer(mesh: satellite, uv: UV.Spherical(satellite), material: new UnlitTexture(satelliteTexture)); satelliteObj.Transform = Transformation.Offset(new Vec3(-1.6, 1.6, 0.1)); hierarchy.Add(satelliteObj); + helper.Transform = Transformation.Scale(Vec3.One * 1.3); + helper.XLabel = "X"; + helper.YLabel = "Y"; + helper.ZLabel = "Z"; + hierarchy.Add(helper); + // Render SaveAnimation("render.complex", SpinAnimation(camera, hierarchy, frames: 64)); } diff --git a/Render/Render.csproj b/Render/Render.csproj index 8b84ea3..c260595 100644 --- a/Render/Render.csproj +++ b/Render/Render.csproj @@ -11,7 +11,7 @@ Qkmaxware.Rendering - 1.0.1 + 1.0.2 Colin Halseth softrendering LICENSE.md @@ -26,7 +26,7 @@ - + diff --git a/Render/src/AxisHelper.cs b/Render/src/AxisHelper.cs new file mode 100644 index 0000000..76e7d85 --- /dev/null +++ b/Render/src/AxisHelper.cs @@ -0,0 +1,104 @@ +using System.Drawing; +using Qkmaxware.Geometry; +using Qkmaxware.Geometry.Primitives; + +namespace Qkmaxware.Rendering { + +/// +/// XYZ axis helper +/// +public class AxisHelper : SceneNode { + + private MeshRenderer XObject; + private MeshRenderer XLabelObject; + private MeshRenderer YObject; + private MeshRenderer YLabelObject; + private MeshRenderer ZObject; + private MeshRenderer ZLabelObject; + + /// + /// Create a new axis helper + /// + public AxisHelper() { + var rootX = CreateAxis(new UnlitColour(Color.Red), out XObject, out XLabelObject); + var rootY = CreateAxis(new UnlitColour(Color.Green), out YObject, out YLabelObject); + var rootZ = CreateAxis(new UnlitColour(Color.Blue), out ZObject, out ZLabelObject); + + rootX.Transform = Transformation.Ry(-90 * Angle.Deg2Rad); + rootY.Transform = Transformation.Rx(-90 * Angle.Deg2Rad); + } + + private string? xLabel; + + /// + /// The label used for the x-Axis + /// + /// the label + public string? XLabel { + get { + return xLabel; + } + set { + this.xLabel = value; + if (value != null) { + var mesh = new TextMesh(value); + XLabelObject.Mesh = mesh; + XLabelObject.Material = XObject.Material; + } + } + } + + private string? yLabel; + /// + /// The label used for the y-Axis + /// + /// the label + public string? YLabel { + get { + return yLabel; + } + set { + this.yLabel = value; + if (value != null) { + var mesh = new TextMesh (value); + YLabelObject.Mesh = mesh; + YLabelObject.Material = YObject.Material; + } + } + } + + private string? zLabel; + /// + /// The label used for the z-Axis + /// + /// the label + public string? ZLabel { + get { + return zLabel; + } + set { + this.zLabel = value; + if (value != null) { + var mesh = new TextMesh(value); + ZLabelObject.Mesh = mesh; + ZLabelObject.Material = ZObject.Material; + } + } + } + + private SceneNode CreateAxis(Material mat, out MeshRenderer arrow, out MeshRenderer label) { + var arrowMesh = new Arrow(length: 1); + arrow = new MeshRenderer(mesh: arrowMesh, material: mat); + label = new MeshRenderer(); + label.Transform = Transformation.Offset(new Vec3(0, 0, 1.1)) * Transformation.Ry(90 * Angle.Deg2Rad); + + var root = new SceneNode(); + root.Add(arrow); + root.Add(label); + + this.Add(root); + return root; + } +} + +} \ No newline at end of file diff --git a/Render/src/BaseCamera.cs b/Render/src/BaseCamera.cs index a539673..647693e 100644 --- a/Render/src/BaseCamera.cs +++ b/Render/src/BaseCamera.cs @@ -99,10 +99,10 @@ public abstract class BaseCamera : SceneNode { vars.LightSources = scene.OfType().ToList().AsReadOnly(); // Loop over all models - foreach (var renderable in scene.OfType()) { + foreach (var renderable in scene.OfType()) { vars.ModelToWorld = renderable.LocalToWorldMatrix; if (renderable.Mesh != null && renderable.Material != null) { - Render(ref vars, renderable.Mesh, renderable.UVs, renderable.Material); + Render(ref vars, renderable.Mesh, renderable.UVs, (Material)renderable.Material); } } } @@ -220,7 +220,7 @@ public abstract class BaseCamera : SceneNode { private void SetPixel(Vec3 pixel, Color c) { var x = (int)Math.Floor(pixel.X); - var y = (int)Math.Floor(pixel.Y); + var y = Size.Height - (int)Math.Floor(pixel.Y); var depth = pixel.Z; if (x >= 0 && y >= 0 && x < Size.Width && y < Size.Height) { diff --git a/Render/src/IRenderable.cs b/Render/src/IRenderable.cs new file mode 100644 index 0000000..ef40183 --- /dev/null +++ b/Render/src/IRenderable.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using Qkmaxware.Geometry; + +namespace Qkmaxware.Rendering { + +/// +/// Interface for any renderable 3d objects +/// +public interface IRenderable { + /// + /// Mesh to render + /// + IEnumerable? Mesh {get;} + /// + /// Vertex UV coordinates + /// + IUvMap? UVs {get;} + /// + /// Render material + /// + Material? Material {get;} + // + /// Matrix to convert from local to world space + /// + Transformation LocalToWorldMatrix {get;} +} + +} \ No newline at end of file diff --git a/Render/src/Renderable.cs b/Render/src/MeshRenderer.cs similarity index 65% rename from Render/src/Renderable.cs rename to Render/src/MeshRenderer.cs index 6b2b636..19e2e64 100644 --- a/Render/src/Renderable.cs +++ b/Render/src/MeshRenderer.cs @@ -7,30 +7,33 @@ namespace Qkmaxware.Rendering { /// /// Object that can be rendered by the camera /// -public class Renderable : SceneNode { +public class MeshRenderer : SceneNode, IRenderable { /// /// Mesh to render /// - public IEnumerable? Mesh; + /// mesh + public IEnumerable? Mesh {get; set;} /// /// Vertex UV coordinates /// - public IUvMap? UVs; + /// uv map + public IUvMap? UVs {get; set;} /// /// Render material /// - public Material Material = new UnlitColour(Color.White); + /// material + public Material? Material {get; set;} = new UnlitColour(Color.White); /// /// Create empty renderable /// - public Renderable() {} + public MeshRenderer() {} /// /// Create renderable with geometry /// /// geometry - public Renderable(IEnumerable mesh) { + public MeshRenderer(IEnumerable mesh) { this.Mesh = mesh; } @@ -40,7 +43,7 @@ public class Renderable : SceneNode { /// geometry /// material /// uv map - public Renderable(IEnumerable mesh, Material material, IUvMap? uv = null) { + public MeshRenderer(IEnumerable mesh, Material material, IUvMap? uv = null) { this.Mesh = mesh; this.UVs = uv; this.Material = material; diff --git a/Render/src/Skybox.cs b/Render/src/Skybox.cs index 817fbfb..ae5ac14 100644 --- a/Render/src/Skybox.cs +++ b/Render/src/Skybox.cs @@ -71,7 +71,7 @@ public class GradientSkybox : Skybox { } else { var vangle = Math.Acos(z / length); var interpolation_factor = vangle / Math.PI; // angles are 0deg to 180deg (0 to Pi radians) - return Blend(top, bottom, interpolation_factor); + return Blend(bottom, top, interpolation_factor); } } } diff --git a/docs/using.md b/docs/using.md index 44148f9..ca2b9ec 100644 --- a/docs/using.md +++ b/docs/using.md @@ -7,13 +7,13 @@ All scene management start off from the `Scene` class which acts as the root of Scene scene = new Scene(); ``` -We can attach `SceneNode` objects to the scene and to other scene nodes in order to fill out the scene hierarchy. Currently there are 3 types of scene nodes. The base `SceneNode` class is used purely to construct the hierarchy and provides no additional functionality. The `Renderable` class is used to render a geometric object to the camera. This object can be provided with a UV map which is used to map geometry vertices to texture coordinates as well as a material which defines how the object is coloured. Additionally the `Animator` class is used with an `AnimatedScene` to apply actions to nodes between animation frames which is covered the section [Animated Scenes](#animated-scenes). +We can attach `SceneNode` objects to the scene and to other scene nodes in order to fill out the scene hierarchy. Currently there are 3 types of scene nodes. The base `SceneNode` class is used purely to construct the hierarchy and provides no additional functionality. The `MeshRenderer` class is used to render a geometric object to the camera. This object can be provided with a UV map which is used to map geometry vertices to texture coordinates as well as a material which defines how the object is coloured. Additionally the `Animator` class is used with an `AnimatedScene` to apply actions to nodes between animation frames which is covered the section [Animated Scenes](#animated-scenes). ```cs SceneNode emptyNode = new SceneNode(); scene.Add(emptyNode); -Renderable mesh = new Renderable( +MeshRenderer mesh = new MeshRenderer( mesh: new Sphere(radius: 1, centre: Vec3.Zero), uv: null, material: new Wireframe(Color.Red)