From f08c89e327bc11c247caba152e9eaba7e2f4ec72 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Fri, 4 Dec 2020 15:40:27 -0800 Subject: [PATCH] billboarding implementation --- .vscode/launch.json | 8 +- .vscode/tasks.json | 7 +- Kav | 2 +- KavTest/Components/SpotLightComponent.cs | 19 +++++ KavTest/Components/SpriteComponent.cs | 18 +++++ KavTest/Content/Sprites/mushroomguy.png | Bin 0 -> 1176 bytes .../Spawners/BillboardSpriteSpawner.cs | 25 +++++++ KavTest/Extensions/QuaternionExtensions.cs | 32 ++++++++ KavTest/KavTestGame.cs | 39 ++++++---- .../Messages/BillboardSpriteSpawnMessage.cs | 28 +++++++ KavTest/Renderers/SceneRenderer.cs | 70 +++++++++++++++++- 11 files changed, 224 insertions(+), 24 deletions(-) create mode 100644 KavTest/Components/SpotLightComponent.cs create mode 100644 KavTest/Components/SpriteComponent.cs create mode 100644 KavTest/Content/Sprites/mushroomguy.png create mode 100644 KavTest/Engines/Spawners/BillboardSpriteSpawner.cs create mode 100644 KavTest/Extensions/QuaternionExtensions.cs create mode 100644 KavTest/Messages/BillboardSpriteSpawnMessage.cs diff --git a/.vscode/launch.json b/.vscode/launch.json index 3fb9ea8..8b078a1 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -17,13 +17,13 @@ "type": "mono", "request": "launch", "preLaunchTask": "Build: Mono Debug", - "program": "${workspaceFolder}/KavTest/bin/Debug/net48/KavTest.exe", + "program": "${workspaceFolder}/KavTest/bin/x64/Debug/net48/KavTest.exe", "args": [], "env": { - "LD_LIBRARY_PATH": "${workspaceFolder}/KavTest/bin/Debug/net48/lib64", - "DYLD_LIBRARY_PATH": "${workspaceFolder}/KavTest/bin/Debug/net48/osx" + "LD_LIBRARY_PATH": "./lib64", + "DYLD_LIBRARY_PATH": "./osx" }, - "cwd": "${workspaceFolder}", + "cwd": "${workspaceFolder}/KavTest/bin/x64/Debug/net48", "console": "integratedTerminal", "internalConsoleOptions": "neverOpen" }, diff --git a/.vscode/tasks.json b/.vscode/tasks.json index cf4ca99..afc23db 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -46,7 +46,7 @@ "LD_LIBRARY_PATH": "./lib64", "DYLD_LIBRARY_PATH": "./osx" }, - "cwd": "${workspaceFolder}/KavTest/bin/Release/net48" + "cwd": "${workspaceFolder}/KavTest/bin/x64/Release/net48" }, "type": "process", "group": { @@ -93,9 +93,10 @@ "options": { "env": { "LD_LIBRARY_PATH": "./lib64", - "DYLD_LIBRARY_PATH": "./osx" + "DYLD_LIBRARY_PATH": "./osx", + "FNA3D_FORCE_DRIVER": "Vulkan" }, - "cwd": "${workspaceFolder}/KavTest/bin/Debug/net48" + "cwd": "${workspaceFolder}/KavTest/bin/x64/Debug/net48" }, "type": "process", "group": { diff --git a/Kav b/Kav index d83aacd..acaafdc 160000 --- a/Kav +++ b/Kav @@ -1 +1 @@ -Subproject commit d83aacd57f692c9732987fe755cc860fee3c5456 +Subproject commit acaafdcdcd3d1e8c7262879d70e0cf5c7c1f4c78 diff --git a/KavTest/Components/SpotLightComponent.cs b/KavTest/Components/SpotLightComponent.cs new file mode 100644 index 0000000..d5089f6 --- /dev/null +++ b/KavTest/Components/SpotLightComponent.cs @@ -0,0 +1,19 @@ +using Encompass; +using Microsoft.Xna.Framework; + +namespace KavTest.Components +{ + public struct SpotLightComponent : IComponent + { + public Color Color { get; } + public float Intensity { get; } + public float Angle { get; } + + public SpotLightComponent(Color color, float intensity, float angleInRadians) + { + Color = color; + Intensity = intensity; + Angle = angleInRadians; + } + } +} diff --git a/KavTest/Components/SpriteComponent.cs b/KavTest/Components/SpriteComponent.cs new file mode 100644 index 0000000..b444b3a --- /dev/null +++ b/KavTest/Components/SpriteComponent.cs @@ -0,0 +1,18 @@ +using Encompass; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace KavTest +{ + public struct SpriteComponent : IComponent + { + public Texture2D Texture { get; } + public Vector2 Origin { get; } + + public SpriteComponent(Texture2D texture, Vector2 origin) + { + Texture = texture; + Origin = origin; + } + } +} diff --git a/KavTest/Content/Sprites/mushroomguy.png b/KavTest/Content/Sprites/mushroomguy.png new file mode 100644 index 0000000000000000000000000000000000000000..0966f509a03886efd4e41ecc7bbd3322f7052673 GIT binary patch literal 1176 zcmV;J1ZVq+P)trP;^71d$kYG;00DGTPE!Ct=GbNc00byWL_t(| z+U?xWPt-sZ2k_OnDMyN+9<}0KvK&0BY~pn#DV!ytXJRD7aY-aw{{#GQ?Yw=RH~qO~ zyD-!B`wj)#?R+>q=1r$Fj7A6q0)c3aXW=pcY!47c@4`a7oGc>@cL59>cpgR4=+p$a ze#94gPyiePlr|F+VYsiGJ)V|B@=3f-6ac3HcME$_7Vr7qX-$+aNInq-z*nFyC+s#) zOp7N?@|V`p005_ely$!KxV4>zd}=8V02~8U+xP9i`BJjfm4e0us>vb%UI3MkpRCFq zj;~P^o6t2RsvjW&@Bzq@AdXM_B|co*JxBMx0f5Hg z)H0;rhv93b01O%kluC=CngYOp0qH9KF5})PH%0jhz)%6{Aky>?8+1;7!~ysP&}{g*QZyw_l-;6aQPzF%ivEuP1_)3BQE-d* zHRa(KFD6{?`2c`ZAh3sRluuISrPJV8dsG3yJ#cLnl9V3VQhSU?3rGPB1dv7CqtQ-r zSY8D9k0Q>@OSt$+VCDQax7mEBbE=}jFyCDFs0ecG_b?mEvId?9T$){qr0T={8 zCrXJSZF8xg>%7Z1P<9x;18@)6o7rhlX9LtDC?3rVz`xnIPH#W}xCiv#{E?aa+^N)i z8=%RTc`?1N0Qe7R;*04e8cKoew6Sw6==d%ZfKNc(40-A0rJY4Lor*pH9{?Js)z6Tt z8sN^%we0{H5YYHSQwHt%Y$-p81K2X)+7yHH3b5WA5CGl*S9Srg#|1&(^@sqR0=yuB z26eLvz$kz%0H|-U#{dAf9c z007|80X%!)=?TD)0huU`i&Ligx`V#;9)K4>C5z}>z#FS7TQmR+4B#p+^?qy)`73EV qi-HF$0NevDQR*rbC)Gn13H}2MjNRr)$I8+G0000 + { + protected override void Spawn(in BillboardSpriteSpawnMessage message) + { + var entity = CreateEntity(); + + var transform = new Transform3D( + message.Position, + Quaternion.Identity, + new Vector3(message.Scale, 1) + ); + + AddComponent(entity, new Transform3DComponent(transform)); + AddComponent(entity, new SpriteComponent(message.Texture, message.Origin)); + AddComponent(entity, new AngularVelocityComponent(new Vector3(2f, 0, 0))); + } + } +} diff --git a/KavTest/Extensions/QuaternionExtensions.cs b/KavTest/Extensions/QuaternionExtensions.cs new file mode 100644 index 0000000..ed3836f --- /dev/null +++ b/KavTest/Extensions/QuaternionExtensions.cs @@ -0,0 +1,32 @@ +using Microsoft.Xna.Framework; + +namespace KavTest.Extensions +{ + public static class QuaternionExtensions + { + public static Vector3 EulerAngles(this Quaternion q) + { + Vector3 angles; + + double sinr_cosp = 2 * (q.W * q.X + q.Y * q.Z); + double cosr_cosp = 1 - 2 * (q.X * q.X + q.Y * q.Y); + angles.X = (float) System.Math.Atan2(sinr_cosp, cosr_cosp); + + double sinp = 2 * (q.W * q.Y - q.Z * q.X); + if (System.Math.Abs(sinp) >= 1) + { + angles.Y = (float) System.Math.PI / 2 * System.Math.Sign(sinp); + } + else + { + angles.Y = (float) System.Math.Asin(sinp); + } + + double siny_cosp = 2 * (q.W * q.Z + q.X * q.Y); + double cosy_cosp = 1 - 2 * (q.Y * q.Y + q.Z * q.Z); + angles.Z = (float) System.Math.Atan2(siny_cosp, cosy_cosp); + + return angles; + } + } +} diff --git a/KavTest/KavTestGame.cs b/KavTest/KavTestGame.cs index cbba1a5..dd8529b 100644 --- a/KavTest/KavTestGame.cs +++ b/KavTest/KavTestGame.cs @@ -30,7 +30,7 @@ namespace KavTest Window.AllowUserResizing = true; IsMouseVisible = true; - + Microsoft.Xna.Framework.Input.Mouse.IsRelativeMouseModeEXT = true; } @@ -51,7 +51,7 @@ namespace KavTest var rustyBallModel = Kav.ModelLoader.Load( GraphicsDevice, Smuggler.Importer.ImportGLB( - GraphicsDevice, + GraphicsDevice, File.OpenRead("Content/rustysphere.glb") ) ); @@ -169,6 +169,8 @@ namespace KavTest skybox.SetData(CubeMapFace.PositiveY, topPixels); skybox.SetData(CubeMapFace.NegativeY, bottomPixels); + var mushroomGuyTexture = Texture2D.FromStream(GraphicsDevice, new FileStream("Content/Sprites/mushroomguy.png", FileMode.Open)); + WorldBuilder.AddEngine(new InputEngine(this)); WorldBuilder.AddEngine(new AngularVelocityEngine()); WorldBuilder.AddEngine(new MoveAlongCurve3DEngine()); @@ -178,6 +180,7 @@ namespace KavTest WorldBuilder.AddEngine(new LightBulbSpawner(lightBulbModel)); WorldBuilder.AddEngine(new StaticModelSpawner()); WorldBuilder.AddEngine(new DirectionalLightSpawner()); + WorldBuilder.AddEngine(new BillboardSpriteSpawner()); WorldBuilder.AddGeneralRenderer(new SceneRenderer(GraphicsDevice), 0); // WorldBuilder.SendMessage(new RustyBallSpawnMessage( @@ -207,11 +210,11 @@ namespace KavTest WorldBuilder.SendMessage(new StaticModelSpawnMessage( new Transform3D( - new Vector3(0, 1, 0), + new Vector3(0, 1, 0), Quaternion.CreateFromAxisAngle( - Vector3.Right, + Vector3.Right, -Microsoft.Xna.Framework.MathHelper.PiOver2 - ), + ), new Vector3(1f, 1f, 1f) ), redCylinderModel @@ -219,11 +222,11 @@ namespace KavTest WorldBuilder.SendMessage(new StaticModelSpawnMessage( new Transform3D( - new Vector3(-3, 1, 0), + new Vector3(-3, 1, 0), Quaternion.CreateFromAxisAngle( - Vector3.Right, + Vector3.Right, -Microsoft.Xna.Framework.MathHelper.PiOver2 - ), + ), new Vector3(1f, 1f, 1f) ), blueTorusModel @@ -231,16 +234,24 @@ namespace KavTest WorldBuilder.SendMessage(new StaticModelSpawnMessage( new Transform3D( - new Vector3(3, 1, 0), + new Vector3(3, 1, 0), Quaternion.CreateFromAxisAngle( - Vector3.Right, + Vector3.Right, -Microsoft.Xna.Framework.MathHelper.PiOver2 - ), + ), new Vector3(1f, 1f, 1f) ), cubeModel )); - + + WorldBuilder.SendMessage(new BillboardSpriteSpawnMessage( + mushroomGuyTexture, + new Vector3(3, 1, 10), + Vector2.Zero, + 0, + Vector2.One + )); + // WorldBuilder.SendMessage(new StaticModelSpawnMessage( // Transform3D.Identity, // toonShadeRuinsModel @@ -251,8 +262,8 @@ namespace KavTest // lightEntity, // new Transform3DComponent( // new Transform3D( - // new Vector3(0, 3, 3), - // Quaternion.Identity, + // new Vector3(0, 3, 3), + // Quaternion.Identity, // new Vector3(0.1f, 0.1f, 0.1f) // ) // ) diff --git a/KavTest/Messages/BillboardSpriteSpawnMessage.cs b/KavTest/Messages/BillboardSpriteSpawnMessage.cs new file mode 100644 index 0000000..cf3574a --- /dev/null +++ b/KavTest/Messages/BillboardSpriteSpawnMessage.cs @@ -0,0 +1,28 @@ +using Encompass; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace KavTest.Messages +{ + public struct BillboardSpriteSpawnMessage : IMessage + { + public Texture2D Texture { get; } + public Vector3 Position { get; } + public Vector2 Origin { get; } + public float Rotation { get; } + public Vector2 Scale { get; } + + public BillboardSpriteSpawnMessage( + Texture2D texture, + Vector3 position, + float rotation, + Vector2 scale + ) { + Texture = texture; + Origin = new Vector2(texture.Width / 2, texture.Height / 2); + Position = position; + Rotation = rotation; + Scale = scale; + } + } +} diff --git a/KavTest/Renderers/SceneRenderer.cs b/KavTest/Renderers/SceneRenderer.cs index 9384304..1f9c547 100644 --- a/KavTest/Renderers/SceneRenderer.cs +++ b/KavTest/Renderers/SceneRenderer.cs @@ -11,7 +11,12 @@ namespace KavTest.Renderers { public class SceneRenderer : GeneralRenderer { + private GraphicsDevice GraphicsDevice { get; } + private SpriteBatch SpriteBatch { get; } + private Kav.Renderer Renderer { get; } + private RenderTarget2D DeferredTarget { get; } + private RenderTarget2D BillboardTarget { get; } private IEnumerable<(Kav.Model, Matrix)> ModelTransforms { @@ -86,15 +91,62 @@ namespace KavTest.Renderers ); } + private IEnumerable Sprites() + { + foreach (var entity in ReadEntitiesAsEnumerable()) + { + var transformComponent = GetComponent(entity); + var spriteComponent = GetComponent(entity); + + var angles = transformComponent.Transform.Orientation.EulerAngles(); + + yield return new Sprite( + spriteComponent.Texture, + transformComponent.Transform.Position, + spriteComponent.Origin, + angles.X, + new Vector2( + transformComponent.Transform.Scale.X, + transformComponent.Transform.Scale.Y + ) + ); + } + } + public SceneRenderer(GraphicsDevice graphicsDevice) { Renderer = new Kav.Renderer( - graphicsDevice, - graphicsDevice.PresentationParameters.BackBufferWidth, + graphicsDevice, + graphicsDevice.PresentationParameters.BackBufferWidth, graphicsDevice.PresentationParameters.BackBufferHeight, 4, 4096 ); + + DeferredTarget = new RenderTarget2D( + graphicsDevice, + graphicsDevice.PresentationParameters.BackBufferWidth, + graphicsDevice.PresentationParameters.BackBufferHeight, + false, + SurfaceFormat.Color, + DepthFormat.Depth24Stencil8, + 0, + RenderTargetUsage.PreserveContents + ); + + BillboardTarget = new RenderTarget2D( + graphicsDevice, + graphicsDevice.PresentationParameters.BackBufferWidth, + graphicsDevice.PresentationParameters.BackBufferHeight, + false, + SurfaceFormat.Color, + DepthFormat.Depth24Stencil8, + 0, + RenderTargetUsage.PreserveContents + ); + + GraphicsDevice = graphicsDevice; + SpriteBatch = new SpriteBatch(GraphicsDevice); } public override void Render() @@ -140,6 +192,7 @@ namespace KavTest.Renderers // ); Renderer.DeferredToonRender( + DeferredTarget, camera, ModelTransforms, AmbientLight, @@ -148,6 +201,19 @@ namespace KavTest.Renderers ReadComponent().Skybox ); + Renderer.BillboardSpriteRender( + BillboardTarget, + camera, + ModelTransforms, + Sprites() + ); + + GraphicsDevice.SetRenderTarget(null); + SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, null, null, null, null); + SpriteBatch.Draw(DeferredTarget, Vector2.Zero, Color.White); + SpriteBatch.Draw(BillboardTarget, Vector2.Zero, Color.White); + SpriteBatch.End(); + // foreach (var directionalLight in DirectionalLights) // { // Renderer.DepthRender(