billboarding implementation

main
cosmonaut 2020-12-04 15:40:27 -08:00
parent 1c679b8323
commit f08c89e327
11 changed files with 224 additions and 24 deletions

8
.vscode/launch.json vendored
View File

@ -17,13 +17,13 @@
"type": "mono", "type": "mono",
"request": "launch", "request": "launch",
"preLaunchTask": "Build: Mono Debug", "preLaunchTask": "Build: Mono Debug",
"program": "${workspaceFolder}/KavTest/bin/Debug/net48/KavTest.exe", "program": "${workspaceFolder}/KavTest/bin/x64/Debug/net48/KavTest.exe",
"args": [], "args": [],
"env": { "env": {
"LD_LIBRARY_PATH": "${workspaceFolder}/KavTest/bin/Debug/net48/lib64", "LD_LIBRARY_PATH": "./lib64",
"DYLD_LIBRARY_PATH": "${workspaceFolder}/KavTest/bin/Debug/net48/osx" "DYLD_LIBRARY_PATH": "./osx"
}, },
"cwd": "${workspaceFolder}", "cwd": "${workspaceFolder}/KavTest/bin/x64/Debug/net48",
"console": "integratedTerminal", "console": "integratedTerminal",
"internalConsoleOptions": "neverOpen" "internalConsoleOptions": "neverOpen"
}, },

7
.vscode/tasks.json vendored
View File

@ -46,7 +46,7 @@
"LD_LIBRARY_PATH": "./lib64", "LD_LIBRARY_PATH": "./lib64",
"DYLD_LIBRARY_PATH": "./osx" "DYLD_LIBRARY_PATH": "./osx"
}, },
"cwd": "${workspaceFolder}/KavTest/bin/Release/net48" "cwd": "${workspaceFolder}/KavTest/bin/x64/Release/net48"
}, },
"type": "process", "type": "process",
"group": { "group": {
@ -93,9 +93,10 @@
"options": { "options": {
"env": { "env": {
"LD_LIBRARY_PATH": "./lib64", "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", "type": "process",
"group": { "group": {

2
Kav

@ -1 +1 @@
Subproject commit d83aacd57f692c9732987fe755cc860fee3c5456 Subproject commit acaafdcdcd3d1e8c7262879d70e0cf5c7c1f4c78

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,25 @@
using Encompass;
using KavTest.Components;
using KavTest.Messages;
using Microsoft.Xna.Framework;
namespace KavTest.Spawners
{
public class BillboardSpriteSpawner : Spawner<BillboardSpriteSpawnMessage>
{
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)));
}
}
}

View File

@ -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;
}
}
}

View File

@ -30,7 +30,7 @@ namespace KavTest
Window.AllowUserResizing = true; Window.AllowUserResizing = true;
IsMouseVisible = true; IsMouseVisible = true;
Microsoft.Xna.Framework.Input.Mouse.IsRelativeMouseModeEXT = true; Microsoft.Xna.Framework.Input.Mouse.IsRelativeMouseModeEXT = true;
} }
@ -51,7 +51,7 @@ namespace KavTest
var rustyBallModel = Kav.ModelLoader.Load( var rustyBallModel = Kav.ModelLoader.Load(
GraphicsDevice, GraphicsDevice,
Smuggler.Importer.ImportGLB( Smuggler.Importer.ImportGLB(
GraphicsDevice, GraphicsDevice,
File.OpenRead("Content/rustysphere.glb") File.OpenRead("Content/rustysphere.glb")
) )
); );
@ -169,6 +169,8 @@ namespace KavTest
skybox.SetData(CubeMapFace.PositiveY, topPixels); skybox.SetData(CubeMapFace.PositiveY, topPixels);
skybox.SetData(CubeMapFace.NegativeY, bottomPixels); 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 InputEngine(this));
WorldBuilder.AddEngine(new AngularVelocityEngine()); WorldBuilder.AddEngine(new AngularVelocityEngine());
WorldBuilder.AddEngine(new MoveAlongCurve3DEngine()); WorldBuilder.AddEngine(new MoveAlongCurve3DEngine());
@ -178,6 +180,7 @@ namespace KavTest
WorldBuilder.AddEngine(new LightBulbSpawner(lightBulbModel)); WorldBuilder.AddEngine(new LightBulbSpawner(lightBulbModel));
WorldBuilder.AddEngine(new StaticModelSpawner()); WorldBuilder.AddEngine(new StaticModelSpawner());
WorldBuilder.AddEngine(new DirectionalLightSpawner()); WorldBuilder.AddEngine(new DirectionalLightSpawner());
WorldBuilder.AddEngine(new BillboardSpriteSpawner());
WorldBuilder.AddGeneralRenderer(new SceneRenderer(GraphicsDevice), 0); WorldBuilder.AddGeneralRenderer(new SceneRenderer(GraphicsDevice), 0);
// WorldBuilder.SendMessage(new RustyBallSpawnMessage( // WorldBuilder.SendMessage(new RustyBallSpawnMessage(
@ -207,11 +210,11 @@ namespace KavTest
WorldBuilder.SendMessage(new StaticModelSpawnMessage( WorldBuilder.SendMessage(new StaticModelSpawnMessage(
new Transform3D( new Transform3D(
new Vector3(0, 1, 0), new Vector3(0, 1, 0),
Quaternion.CreateFromAxisAngle( Quaternion.CreateFromAxisAngle(
Vector3.Right, Vector3.Right,
-Microsoft.Xna.Framework.MathHelper.PiOver2 -Microsoft.Xna.Framework.MathHelper.PiOver2
), ),
new Vector3(1f, 1f, 1f) new Vector3(1f, 1f, 1f)
), ),
redCylinderModel redCylinderModel
@ -219,11 +222,11 @@ namespace KavTest
WorldBuilder.SendMessage(new StaticModelSpawnMessage( WorldBuilder.SendMessage(new StaticModelSpawnMessage(
new Transform3D( new Transform3D(
new Vector3(-3, 1, 0), new Vector3(-3, 1, 0),
Quaternion.CreateFromAxisAngle( Quaternion.CreateFromAxisAngle(
Vector3.Right, Vector3.Right,
-Microsoft.Xna.Framework.MathHelper.PiOver2 -Microsoft.Xna.Framework.MathHelper.PiOver2
), ),
new Vector3(1f, 1f, 1f) new Vector3(1f, 1f, 1f)
), ),
blueTorusModel blueTorusModel
@ -231,16 +234,24 @@ namespace KavTest
WorldBuilder.SendMessage(new StaticModelSpawnMessage( WorldBuilder.SendMessage(new StaticModelSpawnMessage(
new Transform3D( new Transform3D(
new Vector3(3, 1, 0), new Vector3(3, 1, 0),
Quaternion.CreateFromAxisAngle( Quaternion.CreateFromAxisAngle(
Vector3.Right, Vector3.Right,
-Microsoft.Xna.Framework.MathHelper.PiOver2 -Microsoft.Xna.Framework.MathHelper.PiOver2
), ),
new Vector3(1f, 1f, 1f) new Vector3(1f, 1f, 1f)
), ),
cubeModel cubeModel
)); ));
WorldBuilder.SendMessage(new BillboardSpriteSpawnMessage(
mushroomGuyTexture,
new Vector3(3, 1, 10),
Vector2.Zero,
0,
Vector2.One
));
// WorldBuilder.SendMessage(new StaticModelSpawnMessage( // WorldBuilder.SendMessage(new StaticModelSpawnMessage(
// Transform3D.Identity, // Transform3D.Identity,
// toonShadeRuinsModel // toonShadeRuinsModel
@ -251,8 +262,8 @@ namespace KavTest
// lightEntity, // lightEntity,
// new Transform3DComponent( // new Transform3DComponent(
// new Transform3D( // new Transform3D(
// new Vector3(0, 3, 3), // new Vector3(0, 3, 3),
// Quaternion.Identity, // Quaternion.Identity,
// new Vector3(0.1f, 0.1f, 0.1f) // new Vector3(0.1f, 0.1f, 0.1f)
// ) // )
// ) // )

View File

@ -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;
}
}
}

View File

@ -11,7 +11,12 @@ namespace KavTest.Renderers
{ {
public class SceneRenderer : GeneralRenderer public class SceneRenderer : GeneralRenderer
{ {
private GraphicsDevice GraphicsDevice { get; }
private SpriteBatch SpriteBatch { get; }
private Kav.Renderer Renderer { get; } private Kav.Renderer Renderer { get; }
private RenderTarget2D DeferredTarget { get; }
private RenderTarget2D BillboardTarget { get; }
private IEnumerable<(Kav.Model, Matrix)> ModelTransforms private IEnumerable<(Kav.Model, Matrix)> ModelTransforms
{ {
@ -86,15 +91,62 @@ namespace KavTest.Renderers
); );
} }
private IEnumerable<Sprite> Sprites()
{
foreach (var entity in ReadEntitiesAsEnumerable<SpriteComponent>())
{
var transformComponent = GetComponent<Transform3DComponent>(entity);
var spriteComponent = GetComponent<SpriteComponent>(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) public SceneRenderer(GraphicsDevice graphicsDevice)
{ {
Renderer = new Kav.Renderer( Renderer = new Kav.Renderer(
graphicsDevice, graphicsDevice,
graphicsDevice.PresentationParameters.BackBufferWidth, graphicsDevice.PresentationParameters.BackBufferWidth,
graphicsDevice.PresentationParameters.BackBufferHeight, graphicsDevice.PresentationParameters.BackBufferHeight,
4, 4,
4096 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() public override void Render()
@ -140,6 +192,7 @@ namespace KavTest.Renderers
// ); // );
Renderer.DeferredToonRender( Renderer.DeferredToonRender(
DeferredTarget,
camera, camera,
ModelTransforms, ModelTransforms,
AmbientLight, AmbientLight,
@ -148,6 +201,19 @@ namespace KavTest.Renderers
ReadComponent<SkyboxComponent>().Skybox ReadComponent<SkyboxComponent>().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) // foreach (var directionalLight in DirectionalLights)
// { // {
// Renderer.DepthRender( // Renderer.DepthRender(