add GraphicsDevice class and all other classes necessary to support it

based on ported code
This commit is contained in:
Gered 2013-08-17 22:43:29 -04:00
parent 63132f52cd
commit 044bf67f46
57 changed files with 7378 additions and 1 deletions

View file

@ -87,6 +87,43 @@
<Compile Include="IPlatformConfiguration.cs" />
<Compile Include="IApplication.cs" />
<Compile Include="BaseApplication.cs" />
<Compile Include="Graphics\GraphicsDevice.cs" />
<Compile Include="Graphics\GraphicsContextResource.cs" />
<Compile Include="Graphics\BufferObject.cs" />
<Compile Include="Graphics\Texture.cs" />
<Compile Include="Graphics\TextureParameters.cs" />
<Compile Include="Graphics\RenderState.cs" />
<Compile Include="Graphics\BlendState.cs" />
<Compile Include="Graphics\VertexAttributes.cs" />
<Compile Include="Graphics\VertexBuffer.cs" />
<Compile Include="Graphics\IndexBuffer.cs" />
<Compile Include="Graphics\Framebuffer.cs" />
<Compile Include="Graphics\Renderbuffer.cs" />
<Compile Include="Graphics\Camera.cs" />
<Compile Include="Graphics\Frustum.cs" />
<Compile Include="Graphics\ViewContext.cs" />
<Compile Include="Graphics\Shader.cs" />
<Compile Include="Graphics\TextureAtlas.cs" />
<Compile Include="Graphics\TextureRegion.cs" />
<Compile Include="Graphics\CustomTextureAtlas.cs" />
<Compile Include="Graphics\AutoGridTextureAtlas.cs" />
<Compile Include="Graphics\SpriteFont.cs" />
<Compile Include="Graphics\SpriteFontTrueTypeLoader.cs" />
<Compile Include="Resources\ResourceUtils.cs" />
<Compile Include="Graphics\StandardShader.cs" />
<Compile Include="Graphics\SpriteShader.cs" />
<Compile Include="Graphics\VertexSkinningShader.cs" />
<Compile Include="Graphics\VertexLerpShader.cs" />
<Compile Include="Graphics\GeometryDebugRenderer.cs" />
<Compile Include="Graphics\BuiltinShaders\DebugShader.cs" />
<Compile Include="Graphics\BuiltinShaders\SimpleColorShader.cs" />
<Compile Include="Graphics\BuiltinShaders\SimpleColorTextureShader.cs" />
<Compile Include="Graphics\BuiltinShaders\SimpleTextureShader.cs" />
<Compile Include="Graphics\BuiltinShaders\SimpleTextureVertexLerpShader.cs" />
<Compile Include="Graphics\BuiltinShaders\SimpleTextureVertexSkinningShader.cs" />
<Compile Include="Graphics\BuiltinShaders\Sprite2DShader.cs" />
<Compile Include="Graphics\BuiltinShaders\Sprite3DShader.cs" />
<Compile Include="Support\StringExtensions.cs" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
<ItemGroup>
@ -94,5 +131,30 @@
<Folder Include="IO\" />
<Folder Include="Math\" />
<Folder Include="Graphics\" />
<Folder Include="Resources\" />
<Folder Include="Resources\Fonts\" />
<Folder Include="Resources\Shaders\" />
<Folder Include="Graphics\BuiltinShaders\" />
<Folder Include="Support\" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\Fonts\Vera.ttf" />
<EmbeddedResource Include="Resources\Fonts\VeraMono.ttf" />
<EmbeddedResource Include="Resources\Shaders\debug.frag.glsl" />
<EmbeddedResource Include="Resources\Shaders\debug.vert.glsl" />
<EmbeddedResource Include="Resources\Shaders\simple_color.frag.glsl" />
<EmbeddedResource Include="Resources\Shaders\simple_color.vert.glsl" />
<EmbeddedResource Include="Resources\Shaders\simple_color_texture.frag.glsl" />
<EmbeddedResource Include="Resources\Shaders\simple_color_texture.vert.glsl" />
<EmbeddedResource Include="Resources\Shaders\simple_texture.frag.glsl" />
<EmbeddedResource Include="Resources\Shaders\simple_texture.vert.glsl" />
<EmbeddedResource Include="Resources\Shaders\sprite2d.frag.glsl" />
<EmbeddedResource Include="Resources\Shaders\sprite2d.vert.glsl" />
<EmbeddedResource Include="Resources\Shaders\sprite3d.frag.glsl" />
<EmbeddedResource Include="Resources\Shaders\sprite3d.vert.glsl" />
<EmbeddedResource Include="Resources\Shaders\vertexlerp_texture.frag.glsl" />
<EmbeddedResource Include="Resources\Shaders\vertexlerp_texture.vert.glsl" />
<EmbeddedResource Include="Resources\Shaders\vertexskinning_texture.frag.glsl" />
<EmbeddedResource Include="Resources\Shaders\vertexskinning_texture.vert.glsl" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,63 @@
using System;
namespace Blarg.GameFramework.Graphics
{
public class AutoGridTextureAtlas : TextureAtlas
{
public int TileWidth { get; private set; }
public int TileHeight { get; private set; }
public AutoGridTextureAtlas(int textureWidth, int textureHeight, int tileWidth, int tileHeight, int tileBorder = 0, float texCoordEdgeOffset = TexCoordEdgeBleedOffset)
: base(textureWidth, textureHeight, texCoordEdgeOffset)
{
Generate(tileWidth, tileHeight, tileBorder);
}
public AutoGridTextureAtlas(Texture texture, int tileHeight, int tileWidth, int tileBorder = 0, float texCoordEdgeOffset = TexCoordEdgeBleedOffset)
: base(texture, texCoordEdgeOffset)
{
Generate(tileWidth, tileHeight, tileBorder);
}
private void Generate(int tileWidth, int tileHeight, int tileBorder)
{
TileWidth = tileWidth;
TileHeight = tileHeight;
tileWidth += tileBorder;
tileHeight += tileBorder;
int tilesX = (Width - tileBorder) / (TileWidth + tileBorder);
int tilesY = (Height - tileBorder) / (TileHeight + tileBorder);
for (int y = 0; y < tilesY; ++y)
{
for (int x = 0; x < tilesX; ++x)
{
var tile = new TextureRegion();
// set pixel location/dimensions
tile.Dimensions.Left = tileBorder + x * tileWidth;
tile.Dimensions.Top = tileBorder + y * tileHeight;
tile.Dimensions.Right = tile.Dimensions.Left + tileWidth - tileBorder;
tile.Dimensions.Bottom = tile.Dimensions.Top + tileHeight - tileBorder;
// set texture coordinates
// HACK: subtract TexCoordEdgeOffset from the bottom right edges to
// get around floating point rounding errors (adjacent tiles will
// slightly bleed in otherwise)
tile.TexCoords.Left = (tile.Dimensions.Left - tileBorder + TexCoordEdgeOffset) / (float)Width;
tile.TexCoords.Top = (tile.Dimensions.Top - tileBorder + TexCoordEdgeOffset) / (float)Height;
tile.TexCoords.Right = ((float)tile.Dimensions.Right + tileBorder - TexCoordEdgeOffset) / (float)Width;
tile.TexCoords.Bottom = ((float)tile.Dimensions.Bottom + tileBorder - TexCoordEdgeOffset) / (float)Height;
// with the particular order that our for loops are nested in, this will
// be the same as if we were using ((y * tilesX) + x) to manually set an
// index each loop iteration (but using a List<> doesn't let us preallocate
// in such a way that allows us to avoid using Add() ...)
Tiles.Add(tile);
}
}
}
}
}

View file

@ -0,0 +1,120 @@
using System;
using PortableGL;
namespace Blarg.GameFramework.Graphics
{
public enum BlendFactor
{
Zero,
One,
SrcColor,
InverseSrcColor,
DstColor,
InverseDstColor,
SrcAlpha,
InverseSrcAlpha,
DstAlpha,
InverseDstAlpha,
SrcAlphaSaturation,
ConstantColor,
ConstantAlpha
}
public class BlendState
{
public static readonly BlendState Default;
public static readonly BlendState Opaque;
public static readonly BlendState AlphaBlend;
public bool Blending { get; set; }
public BlendFactor SourceBlendFactor { get; set; }
public BlendFactor DestinationBlendFactor { get; set; }
static BlendState()
{
Default = new BlendState();
Opaque = new BlendState();
AlphaBlend = new BlendState(BlendFactor.SrcAlpha, BlendFactor.InverseSrcAlpha);
}
public BlendState()
{
Init();
}
public BlendState(BlendFactor sourceFactor, BlendFactor destinationFactor)
{
Init();
Blending = true;
SourceBlendFactor = sourceFactor;
DestinationBlendFactor = destinationFactor;
}
public void Apply()
{
if (Blending)
{
Platform.GL.glEnable(GL20.GL_BLEND);
var source = GL20.GL_ONE;
var dest = GL20.GL_ZERO;
// OpenTK is missing enum values for these combos (maybe they're not valid in OpenGL ??)
System.Diagnostics.Debug.Assert(SourceBlendFactor != BlendFactor.SrcColor);
System.Diagnostics.Debug.Assert(SourceBlendFactor != BlendFactor.InverseSrcColor);
System.Diagnostics.Debug.Assert(DestinationBlendFactor != BlendFactor.SrcAlphaSaturation);
switch (SourceBlendFactor)
{
case BlendFactor.Zero: source = GL20.GL_ZERO; break;
case BlendFactor.One: source = GL20.GL_ONE; break;
case BlendFactor.DstColor: source = GL20.GL_DST_COLOR; break;
case BlendFactor.InverseDstColor: source = GL20.GL_ONE_MINUS_DST_COLOR; break;
case BlendFactor.SrcAlpha: source = GL20.GL_SRC_ALPHA; break;
case BlendFactor.InverseSrcAlpha: source = GL20.GL_ONE_MINUS_SRC_ALPHA; break;
case BlendFactor.DstAlpha: source = GL20.GL_DST_ALPHA; break;
case BlendFactor.InverseDstAlpha: source = GL20.GL_ONE_MINUS_DST_ALPHA; break;
case BlendFactor.ConstantAlpha: source = GL20.GL_CONSTANT_ALPHA; break;
case BlendFactor.ConstantColor: source = GL20.GL_CONSTANT_COLOR; break;
case BlendFactor.SrcAlphaSaturation: source = GL20.GL_SRC_ALPHA_SATURATE; break;
}
switch (DestinationBlendFactor)
{
case BlendFactor.Zero: dest = GL20.GL_ZERO; break;
case BlendFactor.One: dest = GL20.GL_ONE; break;
case BlendFactor.SrcColor: dest = GL20.GL_SRC_COLOR; break;
case BlendFactor.InverseSrcColor: dest = GL20.GL_ONE_MINUS_SRC_COLOR; break;
case BlendFactor.DstColor: dest = GL20.GL_DST_COLOR; break;
case BlendFactor.InverseDstColor: dest = GL20.GL_ONE_MINUS_DST_COLOR; break;
case BlendFactor.SrcAlpha: dest = GL20.GL_SRC_ALPHA; break;
case BlendFactor.InverseSrcAlpha: dest = GL20.GL_ONE_MINUS_SRC_ALPHA; break;
case BlendFactor.DstAlpha: dest = GL20.GL_DST_ALPHA; break;
case BlendFactor.InverseDstAlpha: dest = GL20.GL_ONE_MINUS_DST_ALPHA; break;
case BlendFactor.ConstantAlpha: dest = GL20.GL_CONSTANT_ALPHA; break;
case BlendFactor.ConstantColor: dest = GL20.GL_CONSTANT_COLOR; break;
}
Platform.GL.glBlendFunc(source, dest);
}
else
Platform.GL.glDisable(GL20.GL_BLEND);
}
private void Init()
{
Blending = false;
SourceBlendFactor = BlendFactor.One;
DestinationBlendFactor = BlendFactor.Zero;
}
public BlendState Clone()
{
var clone = new BlendState();
clone.Blending = Blending;
clone.DestinationBlendFactor = DestinationBlendFactor;
clone.SourceBlendFactor = SourceBlendFactor;
return clone;
}
}
}

View file

@ -0,0 +1,233 @@
using System;
using PortableGL;
namespace Blarg.GameFramework.Graphics
{
public enum BufferObjectType
{
Vertex,
Index
}
public enum BufferObjectUsage
{
Static,
Stream,
Dynamic
}
public abstract class BufferObject<T> : GraphicsContextResource where T : struct
{
public int ID { get; private set; }
public bool IsClientSide { get; private set; }
public bool IsDirty { get; protected set; }
public BufferObjectType Type { get; private set; }
public BufferObjectUsage Usage { get; private set; }
public int SizeInBytes { get; private set; }
public bool IsInvalidated
{
get { return ID == -1; }
}
public abstract int NumElements { get; }
public abstract int ElementWidthInBytes { get; }
public abstract T[] Data { get; }
public BufferObject(BufferObjectType type, BufferObjectUsage usage)
: base()
{
Initialize(type, usage);
}
public BufferObject(GraphicsDevice graphicsDevice, BufferObjectType type, BufferObjectUsage usage)
: base(graphicsDevice)
{
Initialize(type, usage);
}
private void Initialize(BufferObjectType type, BufferObjectUsage usage)
{
Type = type;
Usage = usage;
ID = -1;
IsClientSide = true;
IsDirty = false;
}
protected void CreateOnGpu()
{
if (GraphicsDevice == null)
throw new InvalidOperationException("Buffer object was not created with a GraphicsDevice object.");
if (!IsInvalidated)
throw new InvalidOperationException();
CreateBufferObject();
}
protected void RecreateOnGpu()
{
if (GraphicsDevice == null)
throw new InvalidOperationException("Buffer object was not created with a GraphicsDevice object.");
if (IsInvalidated)
throw new InvalidOperationException();
FreeBufferObject();
CreateBufferObject();
}
protected void FreeFromGpu()
{
if (GraphicsDevice == null)
throw new InvalidOperationException("Buffer object was not created with a GraphicsDevice object.");
if (IsInvalidated)
throw new InvalidOperationException();
FreeBufferObject();
}
public void Update()
{
if (!IsDirty)
return;
if (IsClientSide)
{
// pretend we updated! (I guess this is pointless anyway)
IsDirty = false;
return;
}
if (IsInvalidated)
throw new InvalidOperationException();
if (NumElements <= 0)
throw new InvalidOperationException();
if (ElementWidthInBytes <= 0)
throw new InvalidOperationException();
int currentSizeInBytes = NumElements * ElementWidthInBytes;
var usage = GLUsageHint;
var target = GLTarget;
Platform.GL.glBindBuffer(target, ID);
if (SizeInBytes != currentSizeInBytes)
{
// means that the buffer object hasn't been allocated. So let's allocate and update at the same time
// figure out the size...
SizeInBytes = currentSizeInBytes;
// and then allocate + update
Platform.GL.glBufferData<T>(target, SizeInBytes, Data, usage);
}
else
{
// possible performance enhancement? passing a NULL pointer to
// glBufferData tells the driver that we don't care about the buffer's
// previous contents allowing it to do some extra optimizations which is
// fine since our glBufferSubData call is going to completely replace
// the contents anyway
Platform.GL.glBufferData(target, SizeInBytes, IntPtr.Zero, usage);
Platform.GL.glBufferSubData<T>(target, 0, SizeInBytes, Data);
}
Platform.GL.glBindBuffer(target, 0);
IsDirty = false;
}
protected void CreateBufferObject()
{
ID = Platform.GL.glGenBuffers();
SizeBufferObject();
IsDirty = true;
IsClientSide = false;
}
protected void FreeBufferObject()
{
if (IsInvalidated)
throw new InvalidOperationException();
Platform.GL.glDeleteBuffers(ID);
ID = -1;
IsClientSide = true;
IsDirty = false;
SizeInBytes = 0;
}
protected void SizeBufferObject()
{
if (IsInvalidated)
throw new InvalidOperationException();
if (NumElements <= 0)
throw new InvalidOperationException();
if (ElementWidthInBytes <= 0)
throw new InvalidOperationException();
var usage = GLUsageHint;
var target = GLTarget;
SizeInBytes = NumElements * ElementWidthInBytes;
// resize the buffer object without initializing it's data
Platform.GL.glBindBuffer(target, ID);
Platform.GL.glBufferData(target, SizeInBytes, IntPtr.Zero, usage);
Platform.GL.glBindBuffer(target, 0);
IsDirty = true;
}
private int GLUsageHint
{
get
{
if (Usage == BufferObjectUsage.Static)
return GL20.GL_STATIC_DRAW;
else if (Usage == BufferObjectUsage.Stream)
return GL20.GL_STREAM_DRAW;
else if (Usage == BufferObjectUsage.Dynamic)
return GL20.GL_DYNAMIC_DRAW;
else
throw new InvalidOperationException();
}
}
private int GLTarget
{
get
{
if (Type == BufferObjectType.Index)
return GL20.GL_ELEMENT_ARRAY_BUFFER;
else if (Type == BufferObjectType.Vertex)
return GL20.GL_ARRAY_BUFFER;
else
throw new InvalidOperationException();
}
}
#region GraphicsContextResource
public override void OnNewContext()
{
RecreateOnGpu();
}
public override void OnLostContext()
{
}
protected override bool ReleaseResource()
{
if (!IsInvalidated && !IsClientSide)
{
FreeFromGpu();
ID = -1;
}
return true;
}
#endregion
}
}

View file

@ -0,0 +1,20 @@
using System;
using Blarg.GameFramework.Resources;
namespace Blarg.GameFramework.Graphics.BuiltinShaders
{
public class DebugShader : StandardShader
{
public DebugShader(GraphicsDevice graphicsDevice)
: base(graphicsDevice)
{
var vertexSources = ResourceUtils.GetTextResource("Blarg.GameFramework.Resources.Shaders.debug.vert.glsl");
var fragmentSources = ResourceUtils.GetTextResource("Blarg.GameFramework.Resources.Shaders.debug.frag.glsl");
LoadCompileAndLinkInlineSources(vertexSources, fragmentSources);
MapToVBOStandardAttribute("a_position", VertexStandardAttributes.Position3D);
MapToVBOStandardAttribute("a_color", VertexStandardAttributes.Color);
}
}
}

View file

@ -0,0 +1,20 @@
using System;
using Blarg.GameFramework.Resources;
namespace Blarg.GameFramework.Graphics.BuiltinShaders
{
public class SimpleColorShader : StandardShader
{
public SimpleColorShader(GraphicsDevice graphicsDevice)
: base(graphicsDevice)
{
var vertexSources = ResourceUtils.GetTextResource("Blarg.GameFramework.Resources.Shaders.simple_color.vert.glsl");
var fragmentSources = ResourceUtils.GetTextResource("Blarg.GameFramework.Resources.Shaders.simple_color.frag.glsl");
LoadCompileAndLinkInlineSources(vertexSources, fragmentSources);
MapToVBOStandardAttribute("a_position", VertexStandardAttributes.Position3D);
MapToVBOStandardAttribute("a_color", VertexStandardAttributes.Color);
}
}
}

View file

@ -0,0 +1,21 @@
using System;
using Blarg.GameFramework.Resources;
namespace Blarg.GameFramework.Graphics.BuiltinShaders
{
public class SimpleColorTextureShader : StandardShader
{
public SimpleColorTextureShader(GraphicsDevice graphicsDevice)
: base(graphicsDevice)
{
var vertexSources = ResourceUtils.GetTextResource("Blarg.GameFramework.Resources.Shaders.simple_color_texture.vert.glsl");
var fragmentSources = ResourceUtils.GetTextResource("Blarg.GameFramework.Resources.Shaders.simple_color_texture.frag.glsl");
LoadCompileAndLinkInlineSources(vertexSources, fragmentSources);
MapToVBOStandardAttribute("a_position", VertexStandardAttributes.Position3D);
MapToVBOStandardAttribute("a_color", VertexStandardAttributes.Color);
MapToVBOStandardAttribute("a_texcoord0", VertexStandardAttributes.TexCoord);
}
}
}

View file

@ -0,0 +1,20 @@
using System;
using Blarg.GameFramework.Resources;
namespace Blarg.GameFramework.Graphics.BuiltinShaders
{
public class SimpleTextureShader : StandardShader
{
public SimpleTextureShader(GraphicsDevice graphicsDevice)
: base(graphicsDevice)
{
var vertexSources = ResourceUtils.GetTextResource("Blarg.GameFramework.Resources.Shaders.simple_texture.vert.glsl");
var fragmentSources = ResourceUtils.GetTextResource("Blarg.GameFramework.Resources.Shaders.simple_texture.frag.glsl");
LoadCompileAndLinkInlineSources(vertexSources, fragmentSources);
MapToVBOStandardAttribute("a_position", VertexStandardAttributes.Position3D);
MapToVBOStandardAttribute("a_texcoord0", VertexStandardAttributes.TexCoord);
}
}
}

View file

@ -0,0 +1,21 @@
using System;
using Blarg.GameFramework.Resources;
namespace Blarg.GameFramework.Graphics.BuiltinShaders
{
public class SimpleTextureVertexLerpShader : VertexLerpShader
{
public SimpleTextureVertexLerpShader(GraphicsDevice graphicsDevice)
: base(graphicsDevice)
{
var vertexSources = ResourceUtils.GetTextResource("Blarg.GameFramework.Resources.Shaders.vertexlerp_texture.vert.glsl");
var fragmentSources = ResourceUtils.GetTextResource("Blarg.GameFramework.Resources.Shaders.vertexlerp_texture.frag.glsl");
LoadCompileAndLinkInlineSources(vertexSources, fragmentSources);
MapToVBOAttribute("a_position1", 0);
MapToVBOAttribute("a_position2", 0);
MapToVBOStandardAttribute("a_texcoord0", VertexStandardAttributes.TexCoord);
}
}
}

View file

@ -0,0 +1,21 @@
using System;
using Blarg.GameFramework.Resources;
namespace Blarg.GameFramework.Graphics.BuiltinShaders
{
public class SimpleTextureVertexSkinningShader : VertexSkinningShader
{
public SimpleTextureVertexSkinningShader(GraphicsDevice graphicsDevice)
: base(graphicsDevice)
{
var vertexSources = ResourceUtils.GetTextResource("Blarg.GameFramework.Resources.Shaders.vertexskinning_texture.vert.glsl");
var fragmentSources = ResourceUtils.GetTextResource("Blarg.GameFramework.Resources.Shaders.vertexskinning_texture.frag.glsl");
LoadCompileAndLinkInlineSources(vertexSources, fragmentSources);
MapToVBOAttribute("a_jointIndex", 0);
MapToVBOStandardAttribute("a_position", VertexStandardAttributes.Position3D);
MapToVBOStandardAttribute("a_texcoord0", VertexStandardAttributes.TexCoord);
}
}
}

View file

@ -0,0 +1,21 @@
using System;
using Blarg.GameFramework.Resources;
namespace Blarg.GameFramework.Graphics.BuiltinShaders
{
public class Sprite2DShader : SpriteShader
{
public Sprite2DShader(GraphicsDevice graphicsDevice)
: base(graphicsDevice)
{
var vertexSources = ResourceUtils.GetTextResource("Blarg.GameFramework.Resources.Shaders.sprite2d.vert.glsl");
var fragmentSources = ResourceUtils.GetTextResource("Blarg.GameFramework.Resources.Shaders.sprite2d.frag.glsl");
LoadCompileAndLinkInlineSources(vertexSources, fragmentSources);
MapToVBOStandardAttribute("a_position", VertexStandardAttributes.Position2D);
MapToVBOStandardAttribute("a_color", VertexStandardAttributes.Color);
MapToVBOStandardAttribute("a_texcoord0", VertexStandardAttributes.TexCoord);
}
}
}

View file

@ -0,0 +1,21 @@
using System;
using Blarg.GameFramework.Resources;
namespace Blarg.GameFramework.Graphics.BuiltinShaders
{
public class Sprite3DShader : SpriteShader
{
public Sprite3DShader(GraphicsDevice graphicsDevice)
: base(graphicsDevice)
{
var vertexSources = ResourceUtils.GetTextResource("Blarg.GameFramework.Resources.Shaders.sprite3d.vert.glsl");
var fragmentSources = ResourceUtils.GetTextResource("Blarg.GameFramework.Resources.Shaders.sprite3d.frag.glsl");
LoadCompileAndLinkInlineSources(vertexSources, fragmentSources);
MapToVBOStandardAttribute("a_position", VertexStandardAttributes.Position3D);
MapToVBOStandardAttribute("a_color", VertexStandardAttributes.Color);
MapToVBOStandardAttribute("a_texcoord0", VertexStandardAttributes.TexCoord);
}
}
}

View file

@ -0,0 +1,180 @@
using System;
namespace Blarg.GameFramework.Graphics
{
public class Camera
{
private ViewContext _viewContext;
private float _nearHeight;
private float _nearWidth;
public Frustum Frustum { get; private set; }
public Matrix4x4 LookAt { get; private set; }
public Matrix4x4 Projection { get; private set; }
public Vector3 Forward { get; private set; }
public Vector3 Up { get; private set; }
public Vector3 Orientation { get; set; }
public Vector3 Position { get; set; }
public int ViewportWidth { get; private set; }
public int ViewportHeight { get; private set; }
public float AspectRatio { get; private set; }
public float Near { get; private set; }
public float Far { get; private set; }
public float FieldOfViewAngle { get; private set; }
public Camera(ViewContext viewContext, float near = 1.0f, float far = 50.0f, float fieldOfView = MathConstants.Radians60)
{
if (viewContext == null)
throw new ArgumentNullException("viewContext");
_viewContext = viewContext;
Frustum = new Frustum(_viewContext);
Position = Vector3.Zero;
Orientation = Vector3.Zero;
Forward = Vector3.Zero;
Up = Vector3.Up;
LookAt = Matrix4x4.Identity;
FieldOfViewAngle = fieldOfView;
Near = near;
Far = far;
CalculateDefaultProjection(
_viewContext.ViewportLeft,
_viewContext.ViewportTop,
_viewContext.ViewportRight,
_viewContext.ViewportBottom
);
}
public virtual void OnUpdate(float delta)
{
}
public virtual void OnRender(float delta)
{
Vector3 movement = Vector3.Zero;
UpdateLookAtMatrix(ref movement);
_viewContext.ModelViewMatrix = LookAt;
Frustum.Calculate();
}
public virtual void OnResize(ref Rect size)
{
CalculateDefaultProjection(size.Left, size.Top, size.Right, size.Bottom);
_viewContext.ProjectionMatrix = Projection;
}
public virtual void UpdateProjectionMatrix()
{
CalculateDefaultProjection(
_viewContext.ViewportLeft,
_viewContext.ViewportTop,
_viewContext.ViewportRight,
_viewContext.ViewportBottom
);
}
public virtual void UpdateLookAtMatrix(ref Vector3 movement)
{
CalculateDefaultLookAt(ref movement);
}
public Ray Pick(int screenX, int screenY)
{
float nx = 2.0f * ((float)(screenX - (_viewContext.ViewportWidth / 2))) / ((float)_viewContext.ViewportWidth);
float ny = 2.0f * -((float)(screenY - (_viewContext.ViewportHeight / 2))) / ((float)_viewContext.ViewportHeight);
// pick ray calculation method copied from http://code.google.com/p/libgdx/
Vector3 vz = Vector3.Normalize(Forward * -1.0f);
Vector3 vx = Vector3.Normalize(Vector3.Cross(Vector3.Up, vz));
Vector3 vy = Vector3.Normalize(Vector3.Cross(vz, vx));
Vector3 near_center = Position - (vz * Near);
Vector3 a = (vx * _nearWidth) * nx;
Vector3 b = (vy * _nearHeight) * ny;
Vector3 near_point = a + b + near_center;
Vector3 dir = Vector3.Normalize(near_point - Position);
return new Ray(Position, dir);
}
public Point2 Project(ref Vector3 objectPosition)
{
Matrix4x4 modelview = _viewContext.ModelViewMatrix;
Matrix4x4 projection = _viewContext.ProjectionMatrix;
return Project(ref objectPosition, ref modelview, ref projection);
}
public Point2 Project(ref Vector3 objectPosition, ref Matrix4x4 modelView, ref Matrix4x4 projection)
{
// transform object position by modelview matrix (vector transform, w = 1)
float tempX = objectPosition.X * modelView.M11 + objectPosition.Y * modelView.M12 + objectPosition.Z * modelView.M13 + modelView.M14;
float tempY = objectPosition.X * modelView.M21 + objectPosition.Y * modelView.M22 + objectPosition.Z * modelView.M23 + modelView.M24;
float tempZ = objectPosition.X * modelView.M31 + objectPosition.Y * modelView.M32 + objectPosition.Z * modelView.M33 + modelView.M34;
float tempW = objectPosition.X * modelView.M41 + objectPosition.Y * modelView.M42 + objectPosition.Z * modelView.M43 + modelView.M44;
// transform the above by the projection matrix (optimized for bottom row of the projection matrix always being [0, 0, -1, 0])
float transformedX = tempX * projection.M11 + tempY * projection.M12 + tempZ * projection.M13 + tempW * projection.M14;
float transformedY = tempX * projection.M21 + tempY * projection.M22 + tempZ * projection.M23 + tempW * projection.M24;
float transformedZ = tempX * projection.M31 + tempY * projection.M32 + tempZ * projection.M33 + tempW * projection.M34;
float transformedW = -tempZ;
// w normalizes between -1 and 1
// TODO: shouldn't really handle this using an assert... however, I'd like to know when/if this happens
System.Diagnostics.Debug.Assert(transformedW != 0.0f);
transformedW = 1.0f / transformedW;
// perspective division
transformedX *= transformedW;
transformedY *= transformedW;
transformedZ *= transformedW;
// map to 2D viewport coordinates (ignoring Z)
Point2 result;
result.X = (int)(((transformedX * 0.5f) + 0.5f) * (float)_viewContext.ViewportWidth + (float)_viewContext.ViewportLeft);
result.Y = (int)(((transformedY * 0.5f) + 0.5f) * (float)_viewContext.ViewportHeight + (float)_viewContext.ViewportTop);
// float z = (1.0f + transformedZ) * 0.5f; // would be between 0.0f and 1.0f
// adjust Y coordinate so that 0 is at the top of the screen instead of the bottom
result.Y = (int)_viewContext.ViewportHeight - result.Y;
return result;
}
protected void CalculateDefaultProjection(int left, int top, int right, int bottom)
{
ViewportWidth = right - left;
ViewportHeight = bottom - top;
AspectRatio = (float)ViewportWidth / (float)ViewportHeight;
_nearHeight = Near * (float)Math.Tan(FieldOfViewAngle / 2.0f);
_nearWidth = _nearHeight * AspectRatio;
Projection = Matrix4x4.CreatePerspectiveFieldOfView(FieldOfViewAngle, AspectRatio, Near, Far);
}
protected void CalculateDefaultLookAt(ref Vector3 movement)
{
// final camera orientation. angles must be negative (or rather, inverted) for the camera matrix. also the matrix concatenation order is important!
Matrix4x4 rotation = Matrix4x4.CreateRotationY(-Orientation.Y) * Matrix4x4.CreateRotationX(-Orientation.X);
// apply orientation to forward, movement and up vectors so they're pointing in the right direction
Forward = Matrix4x4.Transform(rotation, Vector3.Forward);
Up = Matrix4x4.Transform(rotation, Vector3.Up);
Vector3 orientedMovement = Matrix4x4.Transform(rotation, movement);
// move the camera position
Position += orientedMovement;
Vector3 target = Forward + Position;
LookAt = Matrix4x4.CreateLookAt(Position, target, Vector3.Up);
}
}
}

View file

@ -0,0 +1,63 @@
using System;
namespace Blarg.GameFramework.Graphics
{
public class CustomTextureAtlas : TextureAtlas
{
public CustomTextureAtlas(int textureWidth, int textureHeight, float texCoordEdgeOffset = TexCoordEdgeBleedOffset)
: base(textureWidth, textureHeight, texCoordEdgeOffset)
{
}
public CustomTextureAtlas(Texture texture, float texCoordEdgeOffset = TexCoordEdgeBleedOffset)
: base(texture, texCoordEdgeOffset)
{
}
public int Add(ref Rect region)
{
if (region.Left >= region.Right)
throw new InvalidOperationException();
if (region.Top >= region.Bottom)
throw new InvalidOperationException();
if (region.Right >= Width)
throw new InvalidOperationException();
if (region.Bottom >= Height)
throw new InvalidOperationException();
TextureRegion tile;
// pixel location/dimensions
tile.Dimensions = region;
// texture coordinates
// HACK: subtract TexCoordEdgeOffset from the bottom right edges to
// get around floating point rounding errors (adjacent tiles will
// slightly bleed in otherwise)
tile.TexCoords.Left = ((float)region.Left + TexCoordEdgeOffset) / (float)Width;
tile.TexCoords.Top = ((float)region.Top + TexCoordEdgeOffset) / (float)Height;
tile.TexCoords.Right = ((float)region.Right - TexCoordEdgeOffset) / (float)Width;
tile.TexCoords.Bottom = ((float)region.Bottom - TexCoordEdgeOffset) / (float)Height;
Tiles.Add(tile);
return Tiles.Count - 1;
}
public int Add(Rect region)
{
return Add(ref region);
}
public int Add(int left, int top, int right, int bottom)
{
var region = new Rect(left, top, right, bottom);
return Add(ref region);
}
public void Reset()
{
Tiles.Clear();
}
}
}

View file

@ -0,0 +1,488 @@
using System;
using System.Collections.Generic;
using PortableGL;
namespace Blarg.GameFramework.Graphics
{
public enum FramebufferTextureFormat
{
RGB = 1,
RGBA,
Depth
}
public enum FramebufferRenderbufferFormat
{
RGB = 1,
RGBA,
Depth,
Stencil
}
public class Framebuffer : GraphicsContextResource
{
private int _fixedWidth;
private int _fixedHeight;
private IDictionary<FramebufferTextureFormat, Texture> _attachedTextures;
private IDictionary<FramebufferRenderbufferFormat, Renderbuffer> _attachedRenderbuffers;
private ViewContext _attachedViewContext;
public int ID { get; private set; }
public bool IsInvalidated
{
get { return ID == -1; }
}
public bool IsUsingFixedDimensions
{
get { return (_fixedWidth != 0 && _fixedHeight != 0); }
}
public ViewContext AttachedViewContext
{
get { return _attachedViewContext; }
}
public Renderbuffer GetAttachedRenderbuffer(FramebufferRenderbufferFormat format)
{
Renderbuffer result;
_attachedRenderbuffers.TryGetValue(format, out result);
return result;
}
public Texture GetAttachedTexture(FramebufferTextureFormat format)
{
Texture result;
_attachedTextures.TryGetValue(format, out result);
return result;
}
public Framebuffer(GraphicsDevice graphicsDevice)
: base(graphicsDevice)
{
ID = -1;
_attachedRenderbuffers = new Dictionary<FramebufferRenderbufferFormat, Renderbuffer>();
_attachedTextures = new Dictionary<FramebufferTextureFormat, Texture>();
_attachedViewContext = null;
Create(0, 0);
}
public Framebuffer(GraphicsDevice graphicsDevice, int width, int height)
: base(graphicsDevice)
{
if (width < 1)
throw new ArgumentOutOfRangeException("width");
if (height < 1)
throw new ArgumentOutOfRangeException("height");
ID = -1;
_attachedRenderbuffers = new Dictionary<FramebufferRenderbufferFormat, Renderbuffer>();
_attachedTextures = new Dictionary<FramebufferTextureFormat, Texture>();
_attachedViewContext = null;
Create(width, height);
}
private void Create(int width, int height)
{
if (!IsInvalidated)
throw new InvalidOperationException();
ID = Platform.GL.glGenFramebuffers();
_fixedWidth = width;
_fixedHeight = height;
}
private void Release()
{
if (!IsInvalidated)
{
foreach (var renderbuffer in _attachedRenderbuffers)
renderbuffer.Value.Dispose();
_attachedRenderbuffers.Clear();
foreach (var texture in _attachedTextures)
texture.Value.Dispose();
_attachedTextures.Clear();
Platform.GL.glDeleteFramebuffers(ID);
ID = -1;
}
if (GraphicsDevice.ViewContext == _attachedViewContext)
{
GraphicsDevice.ViewContext = null;
_attachedViewContext = null;
}
_fixedWidth = 0;
_fixedHeight = 0;
}
#region Adding attachments
public void AttachViewContext()
{
if (IsInvalidated)
throw new InvalidOperationException();
if (_attachedViewContext != null)
throw new InvalidOperationException("Existing ViewContext attachment.");
if (IsUsingFixedDimensions)
_attachedViewContext = new ViewContext(GraphicsDevice, new Rect(0, 0, _fixedWidth, _fixedHeight));
else
_attachedViewContext = new ViewContext(GraphicsDevice);
}
public void AttachTexture(FramebufferTextureFormat format)
{
if (IsInvalidated)
throw new InvalidOperationException();
var existingTexture = GetAttachedTexture(format);
if (existingTexture != null)
throw new InvalidOperationException("Texture attachment already exists for this format.");
// also need to make sure a renderbuffer isn't already attached with the same format
var renderbufferFormatToCheck = (FramebufferRenderbufferFormat)format;
var existingRenderbuffer = GetAttachedRenderbuffer(renderbufferFormatToCheck);
if (existingRenderbuffer != null)
throw new InvalidOperationException("Renderbuffer attachment already exists with this same texture format.");
// determine opengl format stuff equivalent to the format passed in
TextureFormat textureFormat;
int attachmentType;
switch (format)
{
case FramebufferTextureFormat.RGB:
textureFormat = TextureFormat.RGB;
attachmentType = GL20.GL_COLOR_ATTACHMENT0;
break;
case FramebufferTextureFormat.RGBA:
textureFormat = TextureFormat.RGBA;
attachmentType = GL20.GL_COLOR_ATTACHMENT0;
break;
case FramebufferTextureFormat.Depth:
textureFormat = TextureFormat.Depth;
attachmentType = GL20.GL_DEPTH_ATTACHMENT;
break;
default:
throw new InvalidOperationException();
}
int width;
int height;
GetDimensionsForAttachment(out width, out height);
// pixelated == unfiltered
var texture = new Texture(GraphicsDevice, width, height, textureFormat, (TextureParameters)TextureParameters.Pixelated.Clone());
// don't have the GraphicsDevice automatically restore this texture!
// since it's dependant on this Framebuffer object, we should let
// the Framebuffer object do the restore itself
GraphicsDevice.UnregisterManagedResource(texture);
GraphicsDevice.BindFramebuffer(this);
Platform.GL.glFramebufferTexture2D(GL20.GL_FRAMEBUFFER, attachmentType, GL20.GL_TEXTURE_2D, texture.ID, 0);
GraphicsDevice.UnbindFramebuffer(this);
_attachedTextures.Add(format, texture);
}
public void AttachRenderbuffer(FramebufferRenderbufferFormat format)
{
if (IsInvalidated)
throw new InvalidOperationException();
var existingRenderbuffer = GetAttachedRenderbuffer(format);
if (existingRenderbuffer != null)
throw new InvalidOperationException("Renderbuffer attachment already exists for this format.");
// also need to make sure a texture isn't already attached with the same format
var textureFormatToCheck = (FramebufferTextureFormat)format;
var existingTexture = GetAttachedTexture(textureFormatToCheck);
if (existingTexture != null)
throw new InvalidOperationException("Texture attachment already exists with this same renderbuffer format.");
// determine opengl format stuff equivalent to the format passed in
RenderbufferFormat renderbufferFormat;
int attachmentType;
switch (format)
{
case FramebufferRenderbufferFormat.RGB:
renderbufferFormat = RenderbufferFormat.RGB;
attachmentType = GL20.GL_COLOR_ATTACHMENT0;
break;
case FramebufferRenderbufferFormat.RGBA:
renderbufferFormat = RenderbufferFormat.RGBA;
attachmentType = GL20.GL_COLOR_ATTACHMENT0;
break;
case FramebufferRenderbufferFormat.Depth:
renderbufferFormat = RenderbufferFormat.Depth;
attachmentType = GL20.GL_DEPTH_ATTACHMENT;
break;
case FramebufferRenderbufferFormat.Stencil:
renderbufferFormat = RenderbufferFormat.Stencil;
attachmentType = GL20.GL_STENCIL_ATTACHMENT;
break;
default:
throw new InvalidOperationException();
}
int width;
int height;
GetDimensionsForAttachment(out width, out height);
var renderbuffer = new Renderbuffer(GraphicsDevice, width, height, renderbufferFormat);
// don't have the GraphicsDevice automatically restore this renderbuffer!
// since it's dependant on this Framebuffer object, we should let
// the Framebuffer object do the restore itself
GraphicsDevice.UnregisterManagedResource(renderbuffer);
GraphicsDevice.BindFramebuffer(this);
Platform.GL.glFramebufferRenderbuffer(GL20.GL_FRAMEBUFFER, attachmentType,GL20.GL_RENDERBUFFER, renderbuffer.ID);
GraphicsDevice.UnbindFramebuffer(this);
_attachedRenderbuffers.Add(format, renderbuffer);
}
#endregion
#region Removing attachments
public void RemoveViewContext()
{
if (IsInvalidated)
throw new InvalidOperationException();
if (_attachedViewContext == null)
return;
if (GraphicsDevice.ViewContext == _attachedViewContext)
GraphicsDevice.ViewContext = null;
_attachedViewContext = null;
}
public void RemoveTexture(FramebufferTextureFormat format)
{
if (IsInvalidated)
throw new InvalidOperationException();
Texture existing;
_attachedTextures.TryGetValue(format, out existing);
if (existing == null)
return;
int attachmentType;
switch (existing.Format)
{
case TextureFormat.RGB: attachmentType = GL20.GL_COLOR_ATTACHMENT0; break;
case TextureFormat.RGBA: attachmentType = GL20.GL_COLOR_ATTACHMENT0; break;
case TextureFormat.Depth: attachmentType = GL20.GL_DEPTH_ATTACHMENT; break;
default:
throw new InvalidOperationException();
}
GraphicsDevice.BindFramebuffer(this);
Platform.GL.glFramebufferTexture2D(GL20.GL_FRAMEBUFFER, attachmentType, GL20.GL_TEXTURE_2D, 0, 0);
GraphicsDevice.UnbindFramebuffer(this);
_attachedTextures.Remove(format);
existing.Dispose();
}
public void RemoveRenderbuffer(FramebufferRenderbufferFormat format)
{
if (IsInvalidated)
throw new InvalidOperationException();
Renderbuffer existing;
_attachedRenderbuffers.TryGetValue(format, out existing);
if (existing == null)
return;
int attachmentType;
switch (existing.Format)
{
case RenderbufferFormat.RGB: attachmentType = GL20.GL_COLOR_ATTACHMENT0; break;
case RenderbufferFormat.RGBA: attachmentType = GL20.GL_COLOR_ATTACHMENT0; break;
case RenderbufferFormat.Depth: attachmentType = GL20.GL_DEPTH_ATTACHMENT; break;
case RenderbufferFormat.Stencil: attachmentType = GL20.GL_STENCIL_ATTACHMENT; break;
default:
throw new InvalidOperationException();
}
GraphicsDevice.BindFramebuffer(this);
Platform.GL.glFramebufferRenderbuffer(GL20.GL_FRAMEBUFFER, attachmentType, GL20.GL_RENDERBUFFER, 0);
GraphicsDevice.UnbindFramebuffer(this);
_attachedRenderbuffers.Remove(format);
existing.Dispose();
}
#endregion
#region Re-attaching existing attachments
private void RecreateAndAttach(FramebufferTextureFormat key)
{
var existing = _attachedTextures[key];
int attachmentType;
switch (existing.Format)
{
case TextureFormat.RGB: attachmentType = GL20.GL_COLOR_ATTACHMENT0; break;
case TextureFormat.RGBA: attachmentType = GL20.GL_COLOR_ATTACHMENT0; break;
case TextureFormat.Depth: attachmentType = GL20.GL_DEPTH_ATTACHMENT; break;
default:
throw new InvalidOperationException();
}
var format = existing.Format;
int width;
int height;
GetDimensionsForAttachment(out width, out height);
// this will essentially do nothing if we're recreating due to a new context
// (existing.IsInvalidated will be true, so Dispose() won't release anything)
existing.Dispose();
// note that we recreate the texture instead of just calling it's OnNewContext()
// method because OnNewContext() will recreate it using it's initial size and we
// may be recreating+attaching due to a viewport resize where this framebuffer is
// to be sized the same as the viewport (non-fixed size)
// pixelated == unfiltered
var newTexture = new Texture(GraphicsDevice, width, height, format, (TextureParameters)TextureParameters.Pixelated.Clone());
// don't have the GraphicsDevice automatically restore this texture!
// since it's dependant on this Framebuffer object, we should let
// the Framebuffer object do the restore itself
GraphicsDevice.UnregisterManagedResource(newTexture);
GraphicsDevice.BindFramebuffer(this);
Platform.GL.glFramebufferTexture2D(GL20.GL_FRAMEBUFFER, attachmentType, GL20.GL_TEXTURE_2D, newTexture.ID, 0);
GraphicsDevice.UnbindFramebuffer(this);
_attachedTextures[key] = newTexture;
}
private void RecreateAndAttach(FramebufferRenderbufferFormat key)
{
var existing = _attachedRenderbuffers[key];
int attachmentType;
switch (existing.Format)
{
case RenderbufferFormat.RGB: attachmentType = GL20.GL_COLOR_ATTACHMENT0; break;
case RenderbufferFormat.RGBA: attachmentType = GL20.GL_COLOR_ATTACHMENT0; break;
case RenderbufferFormat.Depth: attachmentType = GL20.GL_DEPTH_ATTACHMENT; break;
case RenderbufferFormat.Stencil: attachmentType = GL20.GL_STENCIL_ATTACHMENT; break;
default:
throw new InvalidOperationException();
}
var format = existing.Format;
int width;
int height;
GetDimensionsForAttachment(out width, out height);
// this will essentially do nothing if we're recreating due to a new context
// (existing.IsInvalidated will be true, so Dispose() won't release anything)
existing.Dispose();
var newRenderbuffer = new Renderbuffer(GraphicsDevice, width, height, format);
// don't have the GraphicsDevice automatically restore this renderbuffer!
// since it's dependant on this Framebuffer object, we should let
// the Framebuffer object do the restore itself
GraphicsDevice.UnregisterManagedResource(newRenderbuffer);
GraphicsDevice.BindFramebuffer(this);
Platform.GL.glFramebufferRenderbuffer(GL20.GL_FRAMEBUFFER, attachmentType, GL20.GL_RENDERBUFFER, newRenderbuffer.ID);
GraphicsDevice.UnbindFramebuffer(this);
_attachedRenderbuffers[key] = newRenderbuffer;
}
#endregion
private void GetDimensionsForAttachment(out int width, out int height)
{
if (IsUsingFixedDimensions)
{
width = _fixedWidth;
height = _fixedHeight;
}
else
{
ViewContext currentViewContext = (_attachedViewContext == null ? GraphicsDevice.ViewContext : _attachedViewContext);
width = currentViewContext.ViewportWidth;
height = currentViewContext.ViewportHeight;
}
}
public void OnResize()
{
if (IsInvalidated)
return;
// TODO: check that the check for GraphicsDevice.ViewContext != _attachedViewContext is actually needed
if (_attachedViewContext != null && GraphicsDevice.ViewContext != _attachedViewContext)
{
Rect r = Platform.Application.Window.ClientRectangle;
_attachedViewContext.OnResize(ref r, GraphicsDevice.ScreenOrientation);
}
// now recreate & reattach all the attachment points that were set
foreach (var texture in _attachedTextures)
RecreateAndAttach(texture.Key);
foreach (var renderbuffer in _attachedRenderbuffers)
RecreateAndAttach(renderbuffer.Key);
}
#region GraphicsContextResource
public override void OnNewContext()
{
// recreate using the same settings
Create(_fixedWidth, _fixedHeight);
// TODO: check that the check for GraphicsDevice.ViewContext != _attachedViewContext is actually needed
if (_attachedViewContext != null && GraphicsDevice.ViewContext != _attachedViewContext)
_attachedViewContext.OnNewContext();
// now recreate & reattach all the attachment points that were set
foreach (var texture in _attachedTextures)
RecreateAndAttach(texture.Key);
foreach (var renderbuffer in _attachedRenderbuffers)
RecreateAndAttach(renderbuffer.Key);
}
public override void OnLostContext()
{
ID = -1;
// TODO: check that the check for GraphicsDevice.ViewContext != _attachedViewContext is actually needed
if (_attachedViewContext != null && GraphicsDevice.ViewContext != _attachedViewContext)
_attachedViewContext.OnLostContext();
foreach (var texture in _attachedTextures)
texture.Value.OnLostContext();
foreach (var renderbuffer in _attachedRenderbuffers)
renderbuffer.Value.OnLostContext();
}
protected override bool ReleaseResource()
{
if (!IsInvalidated)
{
Release();
ID = -1;
}
return true;
}
#endregion
}
}

View file

@ -0,0 +1,150 @@
using System;
namespace Blarg.GameFramework.Graphics
{
public enum FrustumSides
{
Right = 0,
Left = 1,
Bottom = 2,
Top = 3,
Back = 4,
Front = 5
}
public class Frustum
{
private ViewContext _viewContext;
private Plane[] _planes = new Plane[6];
public Frustum(ViewContext viewContext)
{
if (viewContext == null)
throw new ArgumentNullException("viewContext");
_viewContext = viewContext;
Calculate();
}
public void Calculate()
{
Matrix4x4 combined = _viewContext.ProjectionMatrix * _viewContext.ModelViewMatrix;
// Extract the sides of each of the 6 planes from this to get our viewing frustum
_planes[(int)FrustumSides.Right].Normal.X = combined.M41 - combined.M11;
_planes[(int)FrustumSides.Right].Normal.Y = combined.M42 - combined.M12;
_planes[(int)FrustumSides.Right].Normal.Z = combined.M43 - combined.M13;
_planes[(int)FrustumSides.Right].D = combined.M44 - combined.M14;
_planes[(int)FrustumSides.Left].Normal.X = combined.M41 + combined.M11;
_planes[(int)FrustumSides.Left].Normal.Y = combined.M42 + combined.M12;
_planes[(int)FrustumSides.Left].Normal.Z = combined.M43 + combined.M13;
_planes[(int)FrustumSides.Left].D = combined.M44 + combined.M14;
_planes[(int)FrustumSides.Bottom].Normal.X = combined.M41 + combined.M21;
_planes[(int)FrustumSides.Bottom].Normal.Y = combined.M42 + combined.M22;
_planes[(int)FrustumSides.Bottom].Normal.Z = combined.M43 + combined.M23;
_planes[(int)FrustumSides.Bottom].D = combined.M44 + combined.M24;
_planes[(int)FrustumSides.Top].Normal.X = combined.M41 - combined.M21;
_planes[(int)FrustumSides.Top].Normal.Y = combined.M42 - combined.M22;
_planes[(int)FrustumSides.Top].Normal.Z = combined.M43 - combined.M23;
_planes[(int)FrustumSides.Top].D = combined.M44 - combined.M24;
_planes[(int)FrustumSides.Back].Normal.X = combined.M41 - combined.M31;
_planes[(int)FrustumSides.Back].Normal.Y = combined.M42 - combined.M32;
_planes[(int)FrustumSides.Back].Normal.Z = combined.M43 - combined.M33;
_planes[(int)FrustumSides.Back].D = combined.M44 - combined.M34;
_planes[(int)FrustumSides.Front].Normal.X = combined.M41 + combined.M31;
_planes[(int)FrustumSides.Front].Normal.Y = combined.M42 + combined.M32;
_planes[(int)FrustumSides.Front].Normal.Z = combined.M43 + combined.M33;
_planes[(int)FrustumSides.Front].D = combined.M44 + combined.M34;
Plane.Normalize(ref _planes[(int)FrustumSides.Right], out _planes[(int)FrustumSides.Right]);
Plane.Normalize(ref _planes[(int)FrustumSides.Left], out _planes[(int)FrustumSides.Left]);
Plane.Normalize(ref _planes[(int)FrustumSides.Bottom], out _planes[(int)FrustumSides.Bottom]);
Plane.Normalize(ref _planes[(int)FrustumSides.Top], out _planes[(int)FrustumSides.Top]);
Plane.Normalize(ref _planes[(int)FrustumSides.Back], out _planes[(int)FrustumSides.Back]);
Plane.Normalize(ref _planes[(int)FrustumSides.Front], out _planes[(int)FrustumSides.Front]);
}
public bool Test(ref Vector3 point)
{
for (int p = 0; p < 6; ++p)
{
if (Plane.ClassifyPoint(ref _planes[p], ref point) == PlanePointClassify.Behind)
return false;
}
return true;
}
public bool Test(ref BoundingBox box)
{
if (!TestPlaneAgainstBox(ref _planes[(int)FrustumSides.Right], box.Min.X, box.Min.Y, box.Min.Z, box.Width, box.Height, box.Depth))
return false;
if (!TestPlaneAgainstBox(ref _planes[(int)FrustumSides.Left], box.Min.X, box.Min.Y, box.Min.Z, box.Width, box.Height, box.Depth))
return false;
if (!TestPlaneAgainstBox(ref _planes[(int)FrustumSides.Bottom], box.Min.X, box.Min.Y, box.Min.Z, box.Width, box.Height, box.Depth))
return false;
if (!TestPlaneAgainstBox(ref _planes[(int)FrustumSides.Top], box.Min.X, box.Min.Y, box.Min.Z, box.Width, box.Height, box.Depth))
return false;
if (!TestPlaneAgainstBox(ref _planes[(int)FrustumSides.Back], box.Min.X, box.Min.Y, box.Min.Z, box.Width, box.Height, box.Depth))
return false;
if (!TestPlaneAgainstBox(ref _planes[(int)FrustumSides.Front], box.Min.X, box.Min.Y, box.Min.Z, box.Width, box.Height, box.Depth))
return false;
return true;
}
public bool Test(ref BoundingSphere sphere)
{
if (!TestPlaneAgainstSphere(ref _planes[(int)FrustumSides.Right], ref sphere.Center, sphere.Radius))
return false;
if (!TestPlaneAgainstSphere(ref _planes[(int)FrustumSides.Left], ref sphere.Center, sphere.Radius))
return false;
if (!TestPlaneAgainstSphere(ref _planes[(int)FrustumSides.Bottom], ref sphere.Center, sphere.Radius))
return false;
if (!TestPlaneAgainstSphere(ref _planes[(int)FrustumSides.Top], ref sphere.Center, sphere.Radius))
return false;
if (!TestPlaneAgainstSphere(ref _planes[(int)FrustumSides.Back], ref sphere.Center, sphere.Radius))
return false;
if (!TestPlaneAgainstSphere(ref _planes[(int)FrustumSides.Front], ref sphere.Center, sphere.Radius))
return false;
return true;
}
private bool TestPlaneAgainstBox(ref Plane plane, float minX, float minY, float minZ, float width, float height, float depth)
{
if (Plane.ClassifyPoint(ref plane, minX, minY, minZ) != PlanePointClassify.Behind)
return true;
if (Plane.ClassifyPoint(ref plane, minX, minY, minZ + depth) != PlanePointClassify.Behind)
return true;
if (Plane.ClassifyPoint(ref plane, minX + width, minY, minZ + depth) != PlanePointClassify.Behind)
return true;
if (Plane.ClassifyPoint(ref plane, minX + width, minY, minZ) != PlanePointClassify.Behind)
return true;
if (Plane.ClassifyPoint(ref plane, minX, minY + height, minZ) != PlanePointClassify.Behind)
return true;
if (Plane.ClassifyPoint(ref plane, minX, minY + height, minZ + depth) != PlanePointClassify.Behind)
return true;
if (Plane.ClassifyPoint(ref plane, minX + width, minY + height, minZ + depth) != PlanePointClassify.Behind)
return true;
if (Plane.ClassifyPoint(ref plane, minX + width, minY + height, minZ) != PlanePointClassify.Behind)
return true;
return false;
}
private bool TestPlaneAgainstSphere(ref Plane plane, ref Vector3 center, float radius)
{
float distance = Plane.DistanceBetween(ref plane, ref center);
if (distance <= -radius)
return false;
else
return true;
}
}
}

View file

@ -0,0 +1,395 @@
using System;
namespace Blarg.GameFramework.Graphics
{
public class GeometryDebugRenderer
{
private const int DefaultVerticesAmount = 4096;
private VertexBuffer _vertices;
private RenderState _renderState;
private Color _color1;
private Color _color2;
private bool _hasBegunRendering;
public GraphicsDevice GraphicsDevice { get; private set; }
public GeometryDebugRenderer(GraphicsDevice graphicsDevice)
{
if (graphicsDevice == null)
throw new ArgumentNullException("graphicsDevice");
GraphicsDevice = graphicsDevice;
_vertices = new VertexBuffer(GraphicsDevice, VertexAttributeDeclarations.ColorPosition3D, DefaultVerticesAmount, BufferObjectUsage.Stream);
_color1 = new Color(1.0f, 1.0f, 0.0f);
_color2 = new Color(1.0f, 0.0f, 0.0f);
_renderState = (RenderState)RenderState.Default.Clone();
_renderState.LineWidth = 2.0f;
_hasBegunRendering = false;
}
#region Begin/End
public void Begin(bool depthTesting = true)
{
if (_hasBegunRendering)
throw new InvalidOperationException();
_vertices.MoveToStart();
_renderState.DepthTesting = depthTesting;
_hasBegunRendering = true;
}
public void End()
{
if (!_hasBegunRendering)
throw new InvalidOperationException();
if (_vertices.CurrentPosition == 0)
{
// nothing to render!
_hasBegunRendering = false;
return;
}
int numPointsToRender = _vertices.CurrentPosition;
int numLinesToRender = numPointsToRender / 2;
var shader = GraphicsDevice.DebugShader;
var modelView = GraphicsDevice.ViewContext.ModelViewMatrix;
var projection = GraphicsDevice.ViewContext.ProjectionMatrix;
GraphicsDevice.BindShader(shader);
shader.SetModelViewMatrix(ref modelView);
shader.SetProjectionMatrix(ref projection);
_renderState.Apply();
GraphicsDevice.BindVertexBuffer(_vertices);
GraphicsDevice.RenderLines(0, numLinesToRender);
GraphicsDevice.RenderPoints(0, numPointsToRender);
GraphicsDevice.UnbindVertexBuffer();
GraphicsDevice.UnbindShader();
_hasBegunRendering = false;
}
#endregion
#region Primitive/Shape Rendering
public void Render(ref BoundingBox box)
{
Render(ref box, ref _color1);
}
public void Render(ref BoundingBox box, ref Color color)
{
const int NumVerticesForBox = 24;
if (!_hasBegunRendering)
throw new InvalidOperationException();
EnsureSpaceFor(NumVerticesForBox);
int i = _vertices.CurrentPosition;
// removed lines which are duplicated by more then one face
// left and right faces don't need to be drawn at all (entirely duplicated lines)
// top
_vertices.SetPosition3D(i++, box.Min.X, box.Max.Y, box.Min.Z);
_vertices.SetPosition3D(i++, box.Max.X, box.Max.Y, box.Min.Z);
_vertices.SetPosition3D(i++, box.Max.X, box.Max.Y, box.Min.Z);
_vertices.SetPosition3D(i++, box.Max.X, box.Max.Y, box.Max.Z);
_vertices.SetPosition3D(i++, box.Max.X, box.Max.Y, box.Max.Z);
_vertices.SetPosition3D(i++, box.Min.X, box.Max.Y, box.Max.Z);
_vertices.SetPosition3D(i++, box.Min.X, box.Max.Y, box.Max.Z);
_vertices.SetPosition3D(i++, box.Min.X, box.Max.Y, box.Min.Z);
// back
_vertices.SetPosition3D(i++, box.Min.X, box.Min.Y, box.Min.Z);
_vertices.SetPosition3D(i++, box.Max.X, box.Min.Y, box.Min.Z);
_vertices.SetPosition3D(i++, box.Max.X, box.Min.Y, box.Min.Z);
_vertices.SetPosition3D(i++, box.Max.X, box.Max.Y, box.Min.Z);
_vertices.SetPosition3D(i++, box.Min.X, box.Max.Y, box.Min.Z);
_vertices.SetPosition3D(i++, box.Min.X, box.Min.Y, box.Min.Z);
// front
_vertices.SetPosition3D(i++, box.Min.X, box.Min.Y, box.Max.Z);
_vertices.SetPosition3D(i++, box.Max.X, box.Min.Y, box.Max.Z);
_vertices.SetPosition3D(i++, box.Max.X, box.Min.Y, box.Max.Z);
_vertices.SetPosition3D(i++, box.Max.X, box.Max.Y, box.Max.Z);
_vertices.SetPosition3D(i++, box.Min.X, box.Max.Y, box.Max.Z);
_vertices.SetPosition3D(i++, box.Min.X, box.Min.Y, box.Max.Z);
// bottom
_vertices.SetPosition3D(i++, box.Max.X, box.Min.Y, box.Min.Z);
_vertices.SetPosition3D(i++, box.Max.X, box.Min.Y, box.Max.Z);
_vertices.SetPosition3D(i++, box.Min.X, box.Min.Y, box.Max.Z);
_vertices.SetPosition3D(i++, box.Min.X, box.Min.Y, box.Min.Z);
// fill in all the colours
for (int j = _vertices.CurrentPosition; j < i; ++j)
_vertices.SetColor(j, ref color);
_vertices.Move(NumVerticesForBox);
}
public void Render(ref Point3 boxMin, ref Point3 boxMax)
{
Render(ref boxMin, ref boxMax, ref _color1);
}
public void Render(ref Point3 boxMin, ref Point3 boxMax, ref Color color)
{
var box = new BoundingBox();
box.Min.Set(ref boxMin);
box.Max.Set(ref boxMax);
Render(ref box, ref color);
}
public void Render(ref BoundingSphere sphere)
{
Render(ref sphere, ref _color1);
}
public void Render(ref BoundingSphere sphere, ref Color color)
{
const int NumVerticesForSphere = 615;
if (!_hasBegunRendering)
throw new InvalidOperationException();
EnsureSpaceFor(NumVerticesForSphere);
int p = _vertices.CurrentPosition;
float ax, ay, az;
float bx, by, bz;
float cx = 0.0f, cy = 0.0f, cz = 0.0f;
float dx = 0.0f, dy = 0.0f, dz = 0.0f;
float theta1, theta2, theta3;
int n = 12;
for (int j = 0; j < n / 2; ++j)
{
theta1 = j * MathConstants.Pi * 2 / n - MathConstants.Pi / 2;
theta2 = (j + 1) * MathConstants.Pi * 2 / n - MathConstants.Pi / 2;
for (int i = 0; i <= n; ++i)
{
theta3 = i * MathConstants.Pi * 2 / n;
ax = sphere.Center.X + sphere.Radius * (float)Math.Cos(theta2) * (float)Math.Cos(theta3);
ay = sphere.Center.Y + sphere.Radius * (float)Math.Sin(theta2);
az = sphere.Center.Z + sphere.Radius * (float)Math.Cos(theta2) * (float)Math.Sin(theta3);
bx = sphere.Center.X + sphere.Radius * (float)Math.Cos(theta1) * (float)Math.Cos(theta3);
by = sphere.Center.Y + sphere.Radius * (float)Math.Sin(theta1);
bz = sphere.Center.Z + sphere.Radius * (float)Math.Cos(theta1) * (float)Math.Sin(theta3);
if (j > 0 || i > 0)
{
_vertices.SetPosition3D(p++, ax, ay, az);
_vertices.SetPosition3D(p++, bx, by, bz);
_vertices.SetPosition3D(p++, bx, by, bz);
_vertices.SetPosition3D(p++, dx, dy, dz);
_vertices.SetPosition3D(p++, dx, dy, dz);
_vertices.SetPosition3D(p++, cx, cy, cz);
_vertices.SetPosition3D(p++, cx, cy, cz);
_vertices.SetPosition3D(p++, ax, ay, az);
}
cx = ax;
cy = ay;
cz = az;
dx = bx;
dy = by;
dz = bz;
}
}
// fill in all the colours
for (int i = _vertices.CurrentPosition; i < p; ++i)
_vertices.SetColor(i, ref color);
_vertices.Move(NumVerticesForSphere);
}
public void Render(ref Ray ray, float length)
{
Render(ref ray, length, ref _color1, ref _color2);
}
public void Render(ref Ray ray, float length, ref Color originColor, ref Color endColor)
{
if (!_hasBegunRendering)
throw new InvalidOperationException();
EnsureSpaceFor(2);
Vector3 endPoint = ray.GetPositionAt(length);
_vertices.SetCurrentPosition3D(ref ray.Position);
_vertices.SetCurrentColor(ref originColor);
_vertices.MoveNext();
_vertices.SetCurrentPosition3D(ref endPoint);
_vertices.SetCurrentColor(ref endColor);
_vertices.MoveNext();
}
public void Render(ref LineSegment line)
{
Render(ref line, ref _color1);
}
public void Render(ref LineSegment line, ref Color color)
{
if (!_hasBegunRendering)
throw new InvalidOperationException();
EnsureSpaceFor(2);
_vertices.SetCurrentPosition3D(ref line.A);
_vertices.SetCurrentColor(ref color);
_vertices.MoveNext();
_vertices.SetCurrentPosition3D(ref line.B);
_vertices.SetCurrentColor(ref color);
_vertices.MoveNext();
}
public void Render(ref Vector3 a, ref Vector3 b)
{
Render(ref a, ref b, ref _color1);
}
public void Render(ref Vector3 a, ref Vector3 b, ref Color color)
{
if (!_hasBegunRendering)
throw new InvalidOperationException();
EnsureSpaceFor(2);
_vertices.SetCurrentPosition3D(ref a);
_vertices.SetCurrentColor(ref color);
_vertices.MoveNext();
_vertices.SetCurrentPosition3D(ref b);
_vertices.SetCurrentColor(ref color);
_vertices.MoveNext();
}
public void Render(ref Vector3 a, ref Vector3 b, ref Vector3 c)
{
Render(ref a, ref b, ref c, ref _color1);
}
public void Render(ref Vector3 a, ref Vector3 b, ref Vector3 c, ref Color color)
{
if (!_hasBegunRendering)
throw new InvalidOperationException();
EnsureSpaceFor(6);
// A -> B
_vertices.SetCurrentPosition3D(ref a);
_vertices.SetCurrentColor(ref color);
_vertices.MoveNext();
_vertices.SetCurrentPosition3D(ref b);
_vertices.SetCurrentColor(ref color);
_vertices.MoveNext();
// A -> C
_vertices.SetCurrentPosition3D(ref a);
_vertices.SetCurrentColor(ref color);
_vertices.MoveNext();
_vertices.SetCurrentPosition3D(ref c);
_vertices.SetCurrentColor(ref color);
_vertices.MoveNext();
// B -> C
_vertices.SetCurrentPosition3D(ref b);
_vertices.SetCurrentColor(ref color);
_vertices.MoveNext();
_vertices.SetCurrentPosition3D(ref c);
_vertices.SetCurrentColor(ref color);
_vertices.MoveNext();
}
public void Render(ref Vector3 a, ref Vector3 b, ref Vector3 c, ref Vector3 d)
{
Render(ref a, ref b, ref c, ref d, ref _color1);
}
public void Render(ref Vector3 a, ref Vector3 b, ref Vector3 c, ref Vector3 d, ref Color color)
{
if (!_hasBegunRendering)
throw new InvalidOperationException();
EnsureSpaceFor(8);
// A -> B
_vertices.SetCurrentPosition3D(ref a);
_vertices.SetCurrentColor(ref color);
_vertices.MoveNext();
_vertices.SetCurrentPosition3D(ref b);
_vertices.SetCurrentColor(ref color);
_vertices.MoveNext();
// A -> C
_vertices.SetCurrentPosition3D(ref a);
_vertices.SetCurrentColor(ref color);
_vertices.MoveNext();
_vertices.SetCurrentPosition3D(ref c);
_vertices.SetCurrentColor(ref color);
_vertices.MoveNext();
// C -> D
_vertices.SetCurrentPosition3D(ref c);
_vertices.SetCurrentColor(ref color);
_vertices.MoveNext();
_vertices.SetCurrentPosition3D(ref d);
_vertices.SetCurrentColor(ref color);
_vertices.MoveNext();
// B -> D
_vertices.SetCurrentPosition3D(ref b);
_vertices.SetCurrentColor(ref color);
_vertices.MoveNext();
_vertices.SetCurrentPosition3D(ref d);
_vertices.SetCurrentColor(ref color);
_vertices.MoveNext();
}
#endregion
private void EnsureSpaceFor(int numVertices)
{
if (_vertices.RemainingElements >= numVertices)
return;
int numVerticesNeeded = numVertices - _vertices.RemainingElements;
_vertices.Extend(numVerticesNeeded);
}
}
}

View file

@ -0,0 +1,58 @@
using System;
namespace Blarg.GameFramework.Graphics
{
public abstract class GraphicsContextResource : IDisposable
{
public GraphicsDevice GraphicsDevice { get; private set; }
public bool IsReleased { get; private set; }
public GraphicsContextResource()
{
GraphicsDevice = null;
}
public GraphicsContextResource(GraphicsDevice graphicsDevice)
{
if (graphicsDevice == null)
throw new ArgumentNullException("graphicsDevice");
GraphicsDevice = graphicsDevice;
GraphicsDevice.RegisterManagedResource(this);
}
public virtual void OnNewContext()
{
}
public virtual void OnLostContext()
{
}
~GraphicsContextResource()
{
if (!IsReleased)
{
ReleaseResource();
if (GraphicsDevice != null)
GraphicsDevice.UnregisterManagedResource(this);
}
}
protected virtual bool ReleaseResource()
{
return true;
}
public void Dispose()
{
if (!IsReleased)
{
IsReleased = ReleaseResource();
if (GraphicsDevice != null)
GraphicsDevice.UnregisterManagedResource(this);
}
GC.SuppressFinalize(this);
}
}
}

View file

@ -0,0 +1,927 @@
using System;
using System.Collections.Generic;
using PortableGL;
using Blarg.GameFramework;
using Blarg.GameFramework.Graphics.BuiltinShaders;
using Blarg.GameFramework.Resources;
namespace Blarg.GameFramework.Graphics
{
public class GraphicsDevice : IDisposable
{
private const int MaxTextureUnits = 8;
private const int MaxGpuAttributeSlots = 8;
private const int SolidColorTextureWidth = 8;
private const int SolidColorTextureHeight = 8;
private IList<GraphicsContextResource> _managedResources;
private IDictionary<int, Texture> _solidColorTextures;
private VertexBuffer _boundVertexBuffer;
private IndexBuffer _boundIndexBuffer;
private Texture[] _boundTextures;
private Renderbuffer _boundRenderbuffer;
private Framebuffer _boundFramebuffer;
private Shader _boundShader;
private bool _isShaderVertexAttribsSet;
private Stack<int> _enabledVertexAttribIndices;
private ViewContext _defaultViewContext;
private ViewContext _activeViewContext;
private TextureParameters _currentTextureParams;
private TextureParameters _solidColorTextureParams;
public ScreenOrientation ScreenOrientation { get; private set; }
public bool IsNonPowerOfTwoTextureSupported { get; private set; }
public bool IsDepthTextureSupported { get; private set; }
public StandardShader DebugShader { get; private set; }
public StandardShader SimpleColorShader { get; private set; }
public StandardShader SimpleColorTextureShader { get; private set; }
public StandardShader SimpleTextureShader { get; private set; }
public VertexLerpShader SimpleTextureVertexLerpShader { get; private set; }
public VertexSkinningShader SimpleTextureVertexSkinningShader { get; private set; }
public SpriteShader Sprite2DShader { get; private set; }
public SpriteShader Sprite3DShader { get; private set; }
public SpriteFont SansSerifFont { get; private set; }
public SpriteFont MonospaceFont { get; private set; }
public GeometryDebugRenderer DebugRenderer { get; private set; }
public ViewContext ViewContext
{
get
{
return _activeViewContext;
}
set
{
if (value == _activeViewContext)
return;
if (value == null)
_activeViewContext = _defaultViewContext;
else
_activeViewContext = value;
Rect r = Platform.Application.Window.ClientRectangle;
_activeViewContext.OnApply(ref r, ScreenOrientation);
}
}
public GraphicsDevice()
{
ScreenOrientation = ScreenOrientation.Rotation0;
_boundTextures = new Texture[MaxTextureUnits];
_enabledVertexAttribIndices = new Stack<int>(MaxGpuAttributeSlots);
string vendor = Platform.GL.glGetString(GL20.GL_VENDOR);
string renderer = Platform.GL.glGetString(GL20.GL_RENDERER);
string version = Platform.GL.glGetString(GL20.GL_VERSION);
string extensions = Platform.GL.glGetString(GL20.GL_EXTENSIONS);
string shadingLangVersion = Platform.GL.glGetString(GL20.GL_SHADING_LANGUAGE_VERSION);
Platform.Logger.Info("Graphics", "GL_VENDOR = {0}", vendor);
Platform.Logger.Info("Graphics", "GL_RENDERER = {0}", renderer);
Platform.Logger.Info("Graphics", "GL_VERSION = {0}", version);
Platform.Logger.Info("Graphics", "GL_EXTENSIONS = {0}", extensions);
Platform.Logger.Info("Graphics", "GL_SHADING_LANGUAGE_VERSION = {0}", shadingLangVersion);
if (Platform.Type == PlatformType.Mobile)
{
IsNonPowerOfTwoTextureSupported = extensions.Contains("OES_texture_npot");
IsDepthTextureSupported = extensions.Contains("OES_depth_texture");
}
else
{
IsNonPowerOfTwoTextureSupported = extensions.Contains("ARB_texture_non_power_of_two");
IsDepthTextureSupported = extensions.Contains("ARB_depth_texture");
}
_defaultViewContext = new ViewContext(this);
_activeViewContext = _defaultViewContext;
_currentTextureParams = TextureParameters.Default;
_solidColorTextureParams = TextureParameters.Pixelated; // no filtering
_managedResources = new List<GraphicsContextResource>();
_solidColorTextures = new Dictionary<int, Texture>();
LoadStandardShaders();
LoadStandardFonts();
DebugRenderer = new GeometryDebugRenderer(this);
}
private void LoadStandardShaders()
{
DebugShader = new DebugShader(this);
SimpleColorShader = new SimpleColorShader(this);
SimpleColorTextureShader = new SimpleColorTextureShader(this);
SimpleTextureShader = new SimpleTextureShader(this);
SimpleTextureVertexLerpShader = new SimpleTextureVertexLerpShader(this);
SimpleTextureVertexSkinningShader = new SimpleTextureVertexSkinningShader(this);
Sprite2DShader = new Sprite2DShader(this);
Sprite3DShader = new Sprite3DShader(this);
}
private void LoadStandardFonts()
{
var sansSerifFontStream = ResourceUtils.GetResource("Blarg.GameFramework.Resources.Fonts.Vera.ttf");
SansSerifFont = SpriteFontTrueTypeLoader.Load(this, sansSerifFontStream, 16, SansSerifFont);
var monospaceFontStream = ResourceUtils.GetResource("Blarg.GameFramework.Resources.Fonts.VeraMono.ttf");
MonospaceFont = SpriteFontTrueTypeLoader.Load(this, monospaceFontStream, 16, MonospaceFont);
}
public void OnLostContext()
{
Platform.Logger.Info("Graphics", "Cleaning up objects/state specific to the lost OpenGL context.");
_activeViewContext.OnLostContext();
Platform.Logger.Info("Graphics", "Invoking OnLostContext callback for managed resources.");
foreach (var resource in _managedResources)
resource.OnLostContext();
Platform.Logger.Info("Graphics", "Finished cleaning up lost managed resources.");
}
public void OnNewContext()
{
Platform.Logger.Info("Graphics", "Initializing default state for new OpenGL context.");
_activeViewContext.OnNewContext();
RenderState.Default.Apply();
BlendState.Default.Apply();
UnbindVertexBuffer();
UnbindIndexBuffer();
for (int i = 0; i < MaxTextureUnits; ++i)
UnbindTexture(i);
UnbindShader();
UnbindRenderbuffer();
UnbindFramebuffer();
Platform.Logger.Info("Graphics", "Invoking OnNewContext callback for managed resources.");
foreach (var resource in _managedResources)
resource.OnNewContext();
Platform.Logger.Info("Graphics", "Finished restoring managed resources.");
Platform.Logger.Info("Graphics", "Restoring image data for solid color texture cache.");
foreach (var texture in _solidColorTextures)
{
Color color = Color.FromInt(texture.Key);
FillSolidColorTexture(texture.Value, ref color);
}
Platform.Logger.Info("Graphics", "Restoring standard fonts.");
LoadStandardFonts();
}
public void OnUnload()
{
Platform.Logger.Info("Graphics", "Unloading managed resources.");
while (_managedResources.Count > 0)
{
var resource = _managedResources[0];
resource.Dispose();
}
_managedResources.Clear();
}
public void OnRender(float delta)
{
int error = Platform.GL.glGetError();
System.Diagnostics.Debug.Assert(error == GL20.GL_NO_ERROR);
if (error != GL20.GL_NO_ERROR)
{
Platform.Logger.Error("OpenGL", "OpenGL error \"{0}\"", error.ToString());
// keep checking for and reporting errors until there are no more left
while ((error = Platform.GL.glGetError()) != GL20.GL_NO_ERROR)
Platform.Logger.Error("OpenGL", "OpenGL error \"{0}\"", error.ToString());
}
_activeViewContext.OnRender(delta);
}
public void OnResize(ref Rect rect, ScreenOrientation orientation)
{
Platform.Logger.Info("Graphics", "Window resized ({0}, {1}) - ({2}, {3}).", rect.Left, rect.Top, rect.Width, rect.Height);
if (orientation != ScreenOrientation.Rotation0)
Platform.Logger.Info("Graphics", "Screen is rotated (angle = {0}).", (int)orientation);
ScreenOrientation = orientation;
_activeViewContext.OnResize(ref rect, orientation);
}
public void Clear(float r, float g, float b, float a = Color.AlphaOpaque)
{
var color = new Color(r, g, b, a);
Clear(ref color);
Platform.GL.glClearColor(r, g, b, a);
}
public void Clear(Color color)
{
Clear(ref color);
}
public void Clear(ref Color color)
{
Platform.GL.glClearColor(color.R, color.G, color.B, color.A);
Platform.GL.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
}
public void SetTextureParameters(TextureParameters parameters)
{
if (parameters == null)
throw new ArgumentNullException("parameters");
_currentTextureParams = (TextureParameters)parameters.Clone();
}
internal TextureParameters GetCopyOfTextureParameters()
{
return (TextureParameters)_currentTextureParams.Clone();
}
#region Solid Color Textures
public Texture GetSolidColorTexture(Color color)
{
return GetSolidColorTexture(ref color);
}
public Texture GetSolidColorTexture(ref Color color)
{
int rgba = color.RGBA;
Texture result;
_solidColorTextures.TryGetValue(rgba, out result);
if (result == null)
{
result = CreateSolidColorTexture(ref color);
_solidColorTextures.Add(rgba, result);
}
return result;
}
private Texture CreateSolidColorTexture(ref Color color)
{
Platform.Logger.Info("Graphics", "Creating texture for solid color 0x{0:x}.", color.RGBA);
var solidColorImage = new Image(SolidColorTextureWidth, SolidColorTextureHeight, ImageFormat.RGBA);
solidColorImage.Clear(ref color);
var texture = new Texture(this, solidColorImage, _solidColorTextureParams);
return texture;
}
private void FillSolidColorTexture(Texture texture, ref Color color)
{
Platform.Logger.Info("Graphics", "Filling image data for solid color texture using color 0x{0:x}.", color.RGBA);
if (texture == null || texture.IsInvalidated || texture.Width != SolidColorTextureWidth || texture.Height != SolidColorTextureHeight)
throw new ArgumentException("Invalid texture.");
var solidColorImage = new Image(SolidColorTextureWidth, SolidColorTextureHeight, ImageFormat.RGBA);
solidColorImage.Clear(ref color);
texture.Update(solidColorImage);
}
#endregion
#region Binding
public void BindTexture(Texture texture, int unit = 0)
{
if (unit < 0 || unit >= MaxTextureUnits)
throw new ArgumentOutOfRangeException("unit");
if (texture == null || texture.IsInvalidated)
throw new ArgumentException("Invalid texture.");
if (texture != _boundTextures[unit])
{
Platform.GL.glActiveTexture(GL20.GL_TEXTURE0 + unit);
Platform.GL.glBindTexture(GL20.GL_TEXTURE_2D, texture.ID);
_boundTextures[unit] = texture;
}
}
public void UnbindTexture(int unit = 0)
{
if (unit < 0 || unit >= MaxTextureUnits)
throw new ArgumentOutOfRangeException("unit");
Platform.GL.glActiveTexture(GL20.GL_TEXTURE0 + unit);
Platform.GL.glBindTexture(GL20.GL_TEXTURE_2D, 0);
_boundTextures[unit] = null;
}
public void UnbindTexture(Texture texture)
{
if (texture == null || texture.IsInvalidated)
throw new ArgumentException("Invalid texture.");
for (int i = 0; i < MaxTextureUnits; ++i)
{
if (_boundTextures[i] == texture)
UnbindTexture(i);
}
}
public void BindRenderbuffer(Renderbuffer renderbuffer)
{
if (renderbuffer == null || renderbuffer.IsInvalidated)
throw new ArgumentException("Invalid renderbuffer.");
if (_boundRenderbuffer != renderbuffer)
{
Platform.GL.glBindRenderbuffer(GL20.GL_RENDERBUFFER, renderbuffer.ID);
_boundRenderbuffer = renderbuffer;
}
}
public void UnbindRenderbuffer()
{
Platform.GL.glBindRenderbuffer(GL20.GL_RENDERBUFFER, 0);
_boundRenderbuffer = null;
}
public void UnbindRenderbuffer(Renderbuffer renderbuffer)
{
if (renderbuffer == null)
throw new ArgumentNullException("renderbuffer");
if (renderbuffer == _boundRenderbuffer)
UnbindRenderbuffer();
}
public void BindFramebuffer(Framebuffer framebuffer)
{
if (framebuffer == null || framebuffer.IsInvalidated)
throw new ArgumentException("Invalid framebuffer.");
if (_boundFramebuffer != framebuffer)
{
Platform.GL.glBindFramebuffer(GL20.GL_FRAMEBUFFER, framebuffer.ID);
_boundFramebuffer = framebuffer;
}
}
public void UnbindFramebuffer()
{
Platform.GL.glBindFramebuffer(GL20.GL_FRAMEBUFFER, 0);
_boundFramebuffer = null;
}
public void UnbindFramebuffer(Framebuffer framebuffer)
{
if (framebuffer == null)
throw new ArgumentNullException("framebuffer");
if (framebuffer == _boundFramebuffer)
UnbindFramebuffer();
}
public void BindVertexBuffer(VertexBuffer buffer)
{
if (buffer == null)
throw new ArgumentNullException("buffer");
if (buffer.NumElements <= 0)
throw new InvalidOperationException();
if (_boundVertexBuffer == buffer)
return;
if (buffer.IsClientSide)
BindVertexClientArrays(buffer);
else
BindVBO(buffer);
_boundVertexBuffer = buffer;
if (_isShaderVertexAttribsSet)
ClearSetShaderVertexAttributes();
}
public void UnbindVertexBuffer()
{
Platform.GL.glBindBuffer(GL20.GL_ARRAY_BUFFER, 0);
_boundVertexBuffer = null;
if (_isShaderVertexAttribsSet)
ClearSetShaderVertexAttributes();
}
private void BindVBO(VertexBuffer buffer)
{
if (buffer.IsDirty)
buffer.Update();
Platform.GL.glBindBuffer(GL20.GL_ARRAY_BUFFER, buffer.ID);
}
private void BindVertexClientArrays(VertexBuffer buffer)
{
Platform.GL.glBindBuffer(GL20.GL_ARRAY_BUFFER, 0);
}
public void BindIndexBuffer(IndexBuffer buffer)
{
if (buffer == null)
throw new ArgumentNullException("buffer");
if (buffer.NumElements <= 0)
throw new InvalidOperationException();
if (_boundIndexBuffer == buffer)
return;
if (buffer.IsClientSide)
BindIndexClientArray(buffer);
else
BindIBO(buffer);
_boundIndexBuffer = buffer;
}
public void UnbindIndexBuffer()
{
Platform.GL.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, 0);
_boundIndexBuffer = null;
}
private void BindIBO(IndexBuffer buffer)
{
if (buffer.IsDirty)
buffer.Update();
Platform.GL.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, buffer.ID);
}
private void BindIndexClientArray(IndexBuffer buffer)
{
Platform.GL.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, 0);
}
public void BindShader(Shader shader)
{
if (shader == null)
throw new ArgumentNullException("shader");
if (!shader.IsReadyForUse)
throw new InvalidOperationException("Shader hasn't been compiled and linked.");
Platform.GL.glUseProgram(shader.ProgramID);
_boundShader = shader;
if (_isShaderVertexAttribsSet)
ClearSetShaderVertexAttributes();
_boundShader.OnBind();
}
public void UnbindShader()
{
Platform.GL.glUseProgram(0);
if (_boundShader != null)
_boundShader.OnUnbind();
_boundShader = null;
if (_isShaderVertexAttribsSet)
ClearSetShaderVertexAttributes();
}
private void SetShaderVertexAttributes()
{
if (_boundVertexBuffer == null)
throw new InvalidOperationException("No bound vertex buffer.");
if (_boundShader == null)
throw new InvalidOperationException("No bound shader.");
if (_boundVertexBuffer.NumAttributes < _boundShader.NumAttributes)
throw new InvalidOperationException("The bound vertex buffer does not have enough attributes to use the bound shader.");
for (int i = 0; i < _boundShader.NumAttributes; ++i)
{
int bufferAttribIndex;
int offset;
int size;
if (_boundShader.IsMappedToVBOStandardAttribute(i))
{
// "automatic attribute index discovery" by mapping via standard types
var standardType = _boundShader.GetMappedVBOStandardAttribute(i);
bufferAttribIndex = _boundVertexBuffer.GetIndexOfStandardAttribute(standardType);
if (bufferAttribIndex == -1)
throw new InvalidOperationException("Standard attribute type not present in bound vertex buffer.");
}
else
bufferAttribIndex = _boundShader.GetMappedVBOAttributeIndexFor(i);
// offset value will be in terms of floats (not bytes)
offset = _boundVertexBuffer.GetAttributeOffset(bufferAttribIndex);
size = _boundVertexBuffer.GetAttributeSize(bufferAttribIndex);
Platform.GL.glEnableVertexAttribArray(i);
if (_boundVertexBuffer.IsClientSide)
{
// pass reference to the first vertex and the first float within that vertex that is for this attribute
unsafe
{
fixed (float *p = _boundVertexBuffer.Data)
{
float *src = p + offset;
Platform.GL.glVertexAttribPointer(i, size, GL20.GL_FLOAT, false, _boundVertexBuffer.ElementWidthInBytes, new IntPtr((long)src));
}
}
}
else
// pass offset in bytes (starting from zero) that corresponds with the start of this attribute
Platform.GL.glVertexAttribPointer(i, size, GL20.GL_FLOAT, false, _boundVertexBuffer.ElementWidthInBytes, (IntPtr)(offset * sizeof(float)));
_enabledVertexAttribIndices.Push(i);
}
_isShaderVertexAttribsSet = true;
}
private void ClearSetShaderVertexAttributes()
{
while (_enabledVertexAttribIndices.Count > 0)
{
int index = _enabledVertexAttribIndices.Pop();
Platform.GL.glDisableVertexAttribArray(index);
}
_isShaderVertexAttribsSet = false;
}
private bool IsReadyToRender
{
get
{
if (_boundShader != null && _boundVertexBuffer != null && _isShaderVertexAttribsSet)
return true;
else
return false;
}
}
#endregion
#region Rendering
public void RenderTriangles(IndexBuffer buffer)
{
if (buffer == null)
throw new ArgumentNullException("buffer");
if (!buffer.IsClientSide)
throw new InvalidOperationException("GPU index buffers must be bound first.");
if (_boundVertexBuffer == null)
throw new InvalidOperationException("No bound vertex buffer.");
if (_boundIndexBuffer != null)
throw new InvalidOperationException("GPU index buffer currently bound.");
if (!_isShaderVertexAttribsSet)
SetShaderVertexAttributes();
int numVertices = buffer.NumElements;
if (numVertices % 3 != 0)
throw new InvalidOperationException("Number of elements in index buffer do not perfectly make up a set of triangles.");
Platform.GL.glDrawElements(GL20.GL_TRIANGLES, numVertices, GL20.GL_UNSIGNED_SHORT, buffer.Data);
}
public void RenderTriangles()
{
if (_boundVertexBuffer == null)
throw new InvalidOperationException("No bound vertex buffer.");
if (!_isShaderVertexAttribsSet)
SetShaderVertexAttributes();
if (_boundIndexBuffer != null)
{
// using bound index buffer
int numVertices = _boundIndexBuffer.NumElements;
if (numVertices % 3 != 0)
throw new InvalidOperationException("Number of elements in bound index buffer do not perfectly make up a set of triangles.");
if (_boundIndexBuffer.IsClientSide)
Platform.GL.glDrawElements(GL20.GL_TRIANGLES, numVertices, GL20.GL_UNSIGNED_SHORT, _boundIndexBuffer.Data);
else
Platform.GL.glDrawElements(GL20.GL_TRIANGLES, numVertices, GL20.GL_UNSIGNED_SHORT, (IntPtr)0);
}
else
{
// no index buffer, just render the whole vertex buffer
int numVertices = _boundVertexBuffer.NumElements;
if (numVertices % 3 != 0)
throw new InvalidOperationException("Number of vertices in bound vertex buffer do not perfectly make up a set of triangles.");
Platform.GL.glDrawArrays(GL20.GL_TRIANGLES, 0, numVertices);
}
}
public void RenderTriangles(int startVertex, int numTriangles)
{
if (_boundVertexBuffer == null)
throw new InvalidOperationException("No bound vertex buffer.");
if (!_isShaderVertexAttribsSet)
SetShaderVertexAttributes();
int numVertices = numTriangles * 3;
if (_boundIndexBuffer != null)
{
// using bound index buffer
if ((_boundIndexBuffer.NumElements - startVertex) < numVertices)
throw new InvalidOperationException("Bound index buffer does not contain enough elements.");
if (_boundIndexBuffer.IsClientSide)
{
unsafe
{
fixed (ushort *p = _boundIndexBuffer.Data)
{
ushort *src = p + startVertex;
Platform.GL.glDrawElements(GL20.GL_TRIANGLES, numVertices, GL20.GL_UNSIGNED_SHORT, new IntPtr((long)src));
}
}
}
else
{
// this offset needs to be in terms of bytes
int offset = startVertex * _boundIndexBuffer.ElementWidthInBytes;
Platform.GL.glDrawElements(GL20.GL_TRIANGLES, numVertices, GL20.GL_UNSIGNED_SHORT, (IntPtr)offset);
}
}
else
{
// no index buffer, just render the whole vertex buffer
if ((_boundVertexBuffer.NumElements - startVertex) < numVertices)
throw new InvalidOperationException("Bound vertex buffer does not contain enough vertices.");
Platform.GL.glDrawArrays(GL20.GL_TRIANGLES, startVertex, numVertices);
}
}
public void RenderLines(IndexBuffer buffer)
{
if (buffer == null)
throw new ArgumentNullException("buffer");
if (!buffer.IsClientSide)
throw new InvalidOperationException("GPU index buffers must be bound first.");
if (_boundVertexBuffer == null)
throw new InvalidOperationException("No bound vertex buffer.");
if (_boundIndexBuffer != null)
throw new InvalidOperationException("GPU index buffer currently bound.");
if (!_isShaderVertexAttribsSet)
SetShaderVertexAttributes();
int numVertices = buffer.NumElements;
if (numVertices % 2 != 0)
throw new InvalidOperationException("Number of elements in index buffer do not perfectly make up a set of lines.");
Platform.GL.glDrawElements(GL20.GL_LINES, numVertices, GL20.GL_UNSIGNED_SHORT, buffer.Data);
}
public void RenderLines()
{
if (_boundVertexBuffer == null)
throw new InvalidOperationException("No bound vertex buffer.");
if (!_isShaderVertexAttribsSet)
SetShaderVertexAttributes();
if (_boundIndexBuffer != null)
{
// using bound index buffer
int numVertices = _boundIndexBuffer.NumElements;
if (numVertices % 2 != 0)
throw new InvalidOperationException("Number of elements in bound index buffer do not perfectly make up a set of lines.");
if (_boundIndexBuffer.IsClientSide)
Platform.GL.glDrawElements(GL20.GL_LINES, numVertices, GL20.GL_UNSIGNED_SHORT, _boundIndexBuffer.Data);
else
Platform.GL.glDrawElements(GL20.GL_LINES, numVertices, GL20.GL_UNSIGNED_SHORT, (IntPtr)0);
}
else
{
// no index buffer, just render the whole vertex buffer
int numVertices = _boundVertexBuffer.NumElements;
if (numVertices % 2 != 0)
throw new InvalidOperationException("Number of vertices in bound vertex buffer do not perfectly make up a set of lines.");
Platform.GL.glDrawArrays(GL20.GL_LINES, 0, numVertices);
}
}
public void RenderLines(int startVertex, int numLines)
{
if (_boundVertexBuffer == null)
throw new InvalidOperationException("No bound vertex buffer.");
if (!_isShaderVertexAttribsSet)
SetShaderVertexAttributes();
int numVertices = numLines * 2;
if (_boundIndexBuffer != null)
{
// using bound index buffer
if ((_boundIndexBuffer.NumElements - startVertex) < numVertices)
throw new InvalidOperationException("Bound index buffer does not contain enough elements.");
if (_boundIndexBuffer.IsClientSide)
{
unsafe
{
fixed (ushort *p = _boundIndexBuffer.Data)
{
ushort *src = p + startVertex;
Platform.GL.glDrawElements(GL20.GL_LINES, numVertices, GL20.GL_UNSIGNED_SHORT, new IntPtr((long)src));
}
}
}
else
{
// this offset needs to be in terms of bytes
int offset = startVertex * _boundIndexBuffer.ElementWidthInBytes;
Platform.GL.glDrawElements(GL20.GL_LINES, numVertices, GL20.GL_UNSIGNED_SHORT, (IntPtr)offset);
}
}
else
{
// no index buffer, just render the whole vertex buffer
if ((_boundVertexBuffer.NumElements - startVertex) < numVertices)
throw new InvalidOperationException("Bound vertex buffer does not contain enough vertices.");
Platform.GL.glDrawArrays(GL20.GL_LINES, startVertex, numVertices);
}
}
public void RenderPoints(IndexBuffer buffer)
{
if (buffer == null)
throw new ArgumentNullException("buffer");
if (!buffer.IsClientSide)
throw new InvalidOperationException("GPU index buffers must be bound first.");
if (_boundVertexBuffer == null)
throw new InvalidOperationException("No bound vertex buffer.");
if (_boundIndexBuffer != null)
throw new InvalidOperationException("GPU index buffer currently bound.");
if (!_isShaderVertexAttribsSet)
SetShaderVertexAttributes();
int numVertices = buffer.NumElements;
Platform.GL.glDrawElements(GL20.GL_POINTS, numVertices, GL20.GL_UNSIGNED_SHORT, buffer.Data);
}
public void RenderPoints()
{
if (_boundVertexBuffer == null)
throw new InvalidOperationException("No bound vertex buffer.");
if (!_isShaderVertexAttribsSet)
SetShaderVertexAttributes();
if (_boundIndexBuffer != null)
{
// using bound index buffer
int numVertices = _boundIndexBuffer.NumElements;
if (_boundIndexBuffer.IsClientSide)
Platform.GL.glDrawElements(GL20.GL_POINTS, numVertices, GL20.GL_UNSIGNED_SHORT, _boundIndexBuffer.Data);
else
Platform.GL.glDrawElements(GL20.GL_POINTS, numVertices, GL20.GL_UNSIGNED_SHORT, (IntPtr)0);
}
else
{
// no index buffer, just render the whole vertex buffer
int numVertices = _boundVertexBuffer.NumElements;
Platform.GL.glDrawArrays(GL20.GL_POINTS, 0, numVertices);
}
}
public void RenderPoints(int startVertex, int numPoints)
{
if (_boundVertexBuffer == null)
throw new InvalidOperationException("No bound vertex buffer.");
if (!_isShaderVertexAttribsSet)
SetShaderVertexAttributes();
if (_boundIndexBuffer != null)
{
// using bound index buffer
if ((_boundIndexBuffer.NumElements - startVertex) < numPoints)
throw new InvalidOperationException("Bound index buffer does not contain enough elements.");
if (_boundIndexBuffer.IsClientSide)
{
unsafe
{
fixed (ushort *p = _boundIndexBuffer.Data)
{
ushort *src = p + startVertex;
Platform.GL.glDrawElements(GL20.GL_POINTS, numPoints, GL20.GL_UNSIGNED_SHORT, new IntPtr((long)src));
}
}
}
else
{
// this offset needs to be in terms of bytes
int offset = startVertex * _boundIndexBuffer.ElementWidthInBytes;
Platform.GL.glDrawElements(GL20.GL_POINTS, numPoints, GL20.GL_UNSIGNED_SHORT, (IntPtr)offset);
}
}
else
{
// no index buffer, just render the whole vertex buffer
if ((_boundVertexBuffer.NumElements - startVertex) < numPoints)
throw new InvalidOperationException("Bound vertex buffer does not contain enough vertices.");
Platform.GL.glDrawArrays(GL20.GL_POINTS, startVertex, numPoints);
}
}
#endregion
#region OpenGL Resource Management
public void RegisterManagedResource(GraphicsContextResource resource)
{
if (resource == null)
throw new ArgumentNullException("resource");
if (resource.IsReleased)
throw new ArgumentException("Resource has been released already.");
if (resource.GraphicsDevice == null)
throw new ArgumentException("Resource is not tied to the graphics context (GraphicsDevice is null).");
_managedResources.Add(resource);
}
public void UnregisterManagedResource(GraphicsContextResource resource)
{
if (resource == null)
throw new ArgumentNullException("resource");
_managedResources.Remove(resource);
}
public void UnregisterAllManagedResources()
{
_managedResources.Clear();
}
private bool _isGlResourcesReleased = false;
private void ReleaseGlResources()
{
if (_isGlResourcesReleased)
return;
while (_managedResources.Count > 0)
{
var resource = _managedResources[0];
resource.Dispose();
}
_managedResources.Clear();
_isGlResourcesReleased = true;
Platform.Logger.Info("Graphics", "Managed graphics context resources cleaned up.");
}
~GraphicsDevice()
{
ReleaseGlResources();
}
public void Dispose()
{
ReleaseGlResources();
GC.SuppressFinalize(this);
}
#endregion
}
}

View file

@ -346,4 +346,3 @@ namespace Blarg.GameFramework.Graphics
}
}
}

View file

@ -0,0 +1,168 @@
using System;
namespace Blarg.GameFramework.Graphics
{
public class IndexBuffer : BufferObject<ushort>
{
private ushort[] _buffer;
public int CurrentPosition { get; private set; }
public int RemainingElements
{
get { return (NumElements - 1) - CurrentPosition; }
}
public override int NumElements
{
get { return _buffer.Length; }
}
public override int ElementWidthInBytes
{
get { return sizeof(ushort); }
}
public override ushort[] Data
{
get { return _buffer; }
}
public IndexBuffer(int numIndices, BufferObjectUsage usage)
: base(BufferObjectType.Index, usage)
{
Initialize(numIndices);
}
public IndexBuffer(GraphicsDevice graphicsDevice, int numIndices, BufferObjectUsage usage)
: base(graphicsDevice, BufferObjectType.Index, usage)
{
Initialize(numIndices);
CreateOnGpu();
}
public IndexBuffer(IndexBuffer source)
: base(BufferObjectType.Index, source.Usage)
{
Initialize(source);
}
public IndexBuffer(GraphicsDevice graphicsDevice, IndexBuffer source)
: base(graphicsDevice, BufferObjectType.Index, source.Usage)
{
Initialize(source);
CreateOnGpu();
}
private void Initialize(int numIndices)
{
Resize(numIndices);
}
private void Initialize(IndexBuffer source)
{
if (source == null)
throw new ArgumentNullException("source");
if (source.NumElements <= 0)
throw new InvalidOperationException();
Resize(source.NumElements);
Array.Copy(source.Data, 0, _buffer, 0, NumElements);
}
public void Set(ushort[] indices)
{
if (indices == null)
throw new ArgumentNullException("indices");
if (indices.Length == 0)
throw new InvalidOperationException();
if (indices.Length > _buffer.Length)
throw new InvalidOperationException();
Array.Copy(indices, 0, _buffer, 0, indices.Length);
}
public void Set(int index, ushort value)
{
_buffer[index] = value;
}
public bool MoveNext()
{
++CurrentPosition;
if (CurrentPosition >= NumElements)
{
--CurrentPosition;
return false;
}
else
return true;
}
public bool MovePrevious()
{
if (CurrentPosition == 0)
return false;
else
{
--CurrentPosition;
return true;
}
}
public void Move(int amount)
{
CurrentPosition += amount;
if (CurrentPosition < 0)
CurrentPosition = 0;
else if (CurrentPosition >= NumElements)
CurrentPosition = NumElements - 1;
}
public void MoveToStart()
{
CurrentPosition = 0;
}
public void MoveToEnd()
{
CurrentPosition = NumElements - 1;
}
public void MoveTo(int index)
{
CurrentPosition = index;
}
public void SetCurrent(ushort value)
{
Set(CurrentPosition, value);
}
public void Resize(int numIndices)
{
if (numIndices <= 0)
throw new ArgumentOutOfRangeException("numIndices");
if (_buffer == null)
_buffer = new ushort[numIndices];
else
Array.Resize(ref _buffer, numIndices);
if (!IsClientSide)
SizeBufferObject();
if (CurrentPosition >= NumElements)
CurrentPosition = NumElements - 1;
}
public void Extend(int amount)
{
int newSize = NumElements + amount;
if (newSize <= 0)
throw new ArgumentOutOfRangeException("amount");
Resize(newSize);
}
}
}

View file

@ -0,0 +1,113 @@
using System;
using PortableGL;
namespace Blarg.GameFramework.Graphics
{
public enum CullMode
{
Back,
Front,
FrontAndBack
}
public enum DepthFunc
{
Never,
Less,
Equal,
LessOrEqual,
Greater,
NotEqual,
GreaterOrEqual,
Always
}
public class RenderState
{
public static readonly RenderState Default;
public static readonly RenderState NoDepthTesting;
public static readonly RenderState NoCulling;
static RenderState()
{
Default = new RenderState();
NoDepthTesting = new RenderState();
NoDepthTesting.DepthTesting = false;
NoCulling = new RenderState();
NoCulling.FaceCulling = false;
}
public bool DepthTesting { get; set; }
public DepthFunc DepthFunc { get; set; }
public bool FaceCulling { get; set; }
public CullMode FaceCullingMode { get; set; }
public float LineWidth { get; set; }
public RenderState()
{
Init();
}
public void Apply()
{
if (DepthTesting)
{
Platform.GL.glEnable(GL20.GL_DEPTH_TEST);
int depthFunc = GL20.GL_LESS;
switch (DepthFunc)
{
case DepthFunc.Never: depthFunc = GL20.GL_NEVER; break;
case DepthFunc.Less: depthFunc = GL20.GL_LESS; break;
case DepthFunc.Equal: depthFunc = GL20.GL_EQUAL; break;
case DepthFunc.LessOrEqual: depthFunc = GL20.GL_LEQUAL; break;
case DepthFunc.Greater: depthFunc = GL20.GL_GREATER; break;;
case DepthFunc.NotEqual: depthFunc = GL20.GL_NOTEQUAL; break;
case DepthFunc.GreaterOrEqual: depthFunc = GL20.GL_GEQUAL; break;
case DepthFunc.Always: depthFunc = GL20.GL_ALWAYS; break;
}
Platform.GL.glDepthFunc(depthFunc);
}
else
Platform.GL.glDisable(GL20.GL_DEPTH_TEST);
if (FaceCulling)
{
Platform.GL.glEnable(GL20.GL_CULL_FACE);
if (FaceCullingMode == CullMode.FrontAndBack)
Platform.GL.glCullFace(GL20.GL_FRONT_AND_BACK);
else if (FaceCullingMode == CullMode.Front)
Platform.GL.glCullFace(GL20.GL_FRONT);
else
Platform.GL.glCullFace(GL20.GL_BACK);
}
else
Platform.GL.glDisable(GL20.GL_CULL_FACE);
Platform.GL.glLineWidth(LineWidth);
}
private void Init()
{
DepthTesting = true;
DepthFunc = DepthFunc.Less;
FaceCulling = true;
FaceCullingMode = CullMode.Back;
LineWidth = 1.0f;
}
public RenderState Clone()
{
var clone = new RenderState();
clone.DepthFunc = DepthFunc;
clone.DepthTesting = DepthTesting;
clone.FaceCulling = FaceCulling;
clone.FaceCullingMode = FaceCullingMode;
clone.LineWidth = LineWidth;
return clone;
}
}
}

View file

@ -0,0 +1,109 @@
using System;
using PortableGL;
namespace Blarg.GameFramework.Graphics
{
public enum RenderbufferFormat
{
RGB,
RGBA,
Depth,
Stencil
}
public class Renderbuffer : GraphicsContextResource
{
public int ID { get; private set; }
public int Width { get; private set; }
public int Height { get; private set; }
public RenderbufferFormat Format { get; private set; }
public bool IsInvalidated
{
get { return ID == -1; }
}
public Renderbuffer(GraphicsDevice graphicsDevice, int width, int height, RenderbufferFormat format)
: base(graphicsDevice)
{
ID = -1;
Create(width, height, format);
}
private void Create(int width, int height, RenderbufferFormat format)
{
if (width < 1)
throw new ArgumentOutOfRangeException("width");
if (height < 1)
throw new ArgumentOutOfRangeException("height");
if (!IsInvalidated)
throw new InvalidOperationException();
int glFormat = 0;
if (Platform.Type == PlatformType.Mobile)
{
switch (format)
{
case RenderbufferFormat.RGB: glFormat = GL20.GL_RGB565; break;
case RenderbufferFormat.RGBA: glFormat = GL20.GL_RGBA4; break;
case RenderbufferFormat.Depth: glFormat = GL20.GL_DEPTH_COMPONENT16; break;
case RenderbufferFormat.Stencil: glFormat = GL20.GL_STENCIL_INDEX8; break;
}
}
else
{
switch (format)
{
case RenderbufferFormat.RGB: glFormat = GL20.GL_RGB; break;
case RenderbufferFormat.RGBA: glFormat = GL20.GL_RGBA; break;
case RenderbufferFormat.Depth: glFormat = GL20.GL_DEPTH_COMPONENT; break;
case RenderbufferFormat.Stencil: glFormat = GL20.GL_STENCIL_INDEX; break;
}
}
if (glFormat == 0)
throw new InvalidOperationException();
ID = Platform.GL.glGenRenderbuffers();
Width = width;
Height = height;
Format = format;
GraphicsDevice.BindRenderbuffer(this);
Platform.GL.glRenderbufferStorage(GL20.GL_RENDERBUFFER, glFormat, Width, Height);
GraphicsDevice.UnbindRenderbuffer(this);
Platform.Logger.Info("Graphics", "Created renderbuffer. ID = {0}, format = {1}, size = {2}x{3}.", ID, Format.ToString(), Width, Height);
}
#region GraphicsContextResource
public override void OnNewContext()
{
Create(Width, Height, Format);
}
public override void OnLostContext()
{
ID = -1;
}
protected override bool ReleaseResource()
{
if (!IsInvalidated)
{
GraphicsDevice.UnbindRenderbuffer(this);
Platform.GL.glDeleteRenderbuffers(ID);
Platform.Logger.Info("Graphics", "Deleted Renderbuffer ID = {0}.", ID);
ID = -1;
}
return true;
}
#endregion
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,168 @@
using System;
using System.Text;
namespace Blarg.GameFramework.Graphics
{
public class SpriteFont : GraphicsContextResource
{
private static StringBuilder _buffer = new StringBuilder(8192);
public const int LowGlyphAscii = 32;
public const int HighGlyphAscii = 127;
private TextureAtlas _glyphs;
public int Size { get; private set; }
public int LetterHeight { get; private set; }
public Texture Texture { get; private set; }
public bool IsInvalidated { get; private set; }
#region Instantiation, (Re-)loading, new/lost context
// this class is designed to be instantiated and have it's lifecycle maintained
// by a sprite font content loader *only*
// therefore, the following methods are all internal
internal SpriteFont(GraphicsDevice graphicsDevice, int fontSize, Texture texture, TextureAtlas glyphs)
: base(graphicsDevice)
{
if (fontSize < 1)
throw new ArgumentOutOfRangeException("fontSize");
if (texture == null)
throw new ArgumentNullException("texture");
if (texture.IsInvalidated)
throw new ArgumentException("Texture is invalidated.");
if (glyphs == null)
throw new ArgumentNullException("glyphs");
if (glyphs.NumTiles == 0)
throw new ArgumentException("TextureAtlas is empty.");
Size = fontSize;
Texture = texture;
_glyphs = glyphs;
Rect charSize = new Rect();
_glyphs.GetTileDimensions(GetIndexOfChar(' '), out charSize);
LetterHeight = charSize.Height;
IsInvalidated = false;
}
internal void Reload(Image fontBitmap)
{
if (!IsInvalidated)
throw new InvalidOperationException();
if (fontBitmap == null)
throw new ArgumentNullException("fontBitmap");
if (fontBitmap.Width != Texture.Width || fontBitmap.Height != Texture.Height)
throw new ArgumentException("Font bitmap dimensions do not match texture dimensions.");
Texture.Update(fontBitmap);
IsInvalidated = false;
}
public override void OnNewContext()
{
}
public override void OnLostContext()
{
IsInvalidated = true;
}
#endregion
private int GetIndexOfChar(char c)
{
if ((int)c < LowGlyphAscii || (int)c > HighGlyphAscii)
return HighGlyphAscii - LowGlyphAscii;
else
return (int)c - LowGlyphAscii;
}
public void GetCharDimensions(char c, out Rect dimensions)
{
int index = GetIndexOfChar(c);
_glyphs.GetTileDimensions(index, out dimensions);
}
public void GetCharTexCoords(char c, out RectF texCoords)
{
int index = GetIndexOfChar(c);
_glyphs.GetTileTexCoords(index, out texCoords);
}
public void MeasureString(out int width, out int height, string format, params object[] args)
{
_buffer.Clear();
_buffer.AppendFormat(format, args);
int textLength = _buffer.Length;
int currentMaxWidth = 0;
int left = 0;
int numLines = 1;
for (int i = 0; i < textLength; ++i)
{
char c = _buffer[i];
if (c == '\n')
{
// new line
left = 0;
++numLines;
}
else
{
Rect charSize = new Rect();
GetCharDimensions(c, out charSize);
left += charSize.Width;
}
currentMaxWidth = Math.Max(left, currentMaxWidth);
}
width = currentMaxWidth;
height = numLines * LetterHeight;
}
public void MeasureString(out int width, out int height, float scale, string format, params object[] args)
{
_buffer.Clear();
_buffer.AppendFormat(format, args);
int textLength = _buffer.Length;
float scaledLetterHeight = (float)LetterHeight * scale;
float currentMaxWidth = 0.0f;
float left = 0.0f;
int numLines = 1;
for (int i = 0; i < textLength; ++i)
{
char c = _buffer[i];
if (c == '\n')
{
// new line
left = 0.0f;
++numLines;
}
else
{
Rect charSize = new Rect();
GetCharDimensions(c, out charSize);
left += (float)charSize.Width * scale;
}
currentMaxWidth = Math.Max(left, currentMaxWidth);
}
width = (int)Math.Ceiling(currentMaxWidth);
height = (int)(numLines * scaledLetterHeight);
}
}
}

View file

@ -0,0 +1,196 @@
using System;
using System.IO;
using TrueTypeSharp;
namespace Blarg.GameFramework.Graphics
{
internal static class SpriteFontTrueTypeLoader
{
private struct SpriteFontGlyphMetrics
{
public uint Index;
public float Scale;
public Rect Dimensions;
public int Ascent;
public int Descent;
public int LineGap;
public int Advance;
public int LetterWidth;
public int LetterHeight;
}
public static SpriteFont Load(GraphicsDevice graphicsDevice, Stream file, int size, SpriteFont existingFont = null)
{
const int FontBitmapWidth = 512;
const int FontBitmapHeight = 512;
const int NumGlyphs = SpriteFont.HighGlyphAscii - SpriteFont.LowGlyphAscii;
if (graphicsDevice == null)
throw new ArgumentNullException("graphicsDevice");
if (file == null)
throw new ArgumentNullException("file");
var binaryReader = new BinaryReader(file);
var fontBytes = binaryReader.ReadBytes((int)file.Length);
var ttf = new TrueTypeFont(fontBytes, 0);
// get glyph metrics for the "maximum size" glyph as an indicator of the
// general size of each glyph in this font. Uppercase 'W' seems to be a
// pretty good glyph to represent this "maximum size".
var maxMetrics = new SpriteFontGlyphMetrics();
if (!GetGlyphMetrics(ttf, 'W', size, ref maxMetrics))
throw new Exception("Failed getting initial 'large glyph' metrics.");
var fontBitmap = new Image(FontBitmapWidth, FontBitmapHeight, ImageFormat.A);
fontBitmap.Clear();
CustomTextureAtlas glyphs = null;
// if an existing font was provided, then we're just rebuilding the bitmap
// and the font will continue using the texture atlas is was provided on
// initial creation again
if (existingFont == null)
glyphs = new CustomTextureAtlas(fontBitmap.Width, fontBitmap.Height);
// NOTE TO SELF: a lot of this feels "hackish" and that some weird font isn't going to play nice with this. clean this up at some point!
// current position to draw the glyph to on the bitmap
int x = 0;
int y = 0;
// total line height for each row of glyphs on the bitmap. this is not really the font height.
// it is likely slightly larger then that. this adds the font's descent a second time to make
// vertical room for a few of the glyphs (e.g. '{}' or '()', or even '$') which go off the
// top/bottom by a slight bit
int lineHeight = (maxMetrics.Ascent - maxMetrics.Descent) + maxMetrics.LineGap - maxMetrics.Descent;
var metrics = new SpriteFontGlyphMetrics();
var position = new Rect();
// temporary bitmap to hold each rendered glyph (since TrueTypeSharp can't render into
// our the pixel buffer in the fontBitmap object directly)
// setting the initial size like this is slightly overkill for what will actually be used
// but it seems to be big enough for even the largest glyphs (minimal-to-no resizing needed
// in the render loop below)
var glyphBitmap = new FontBitmap(lineHeight, lineHeight);
for (int i = 0; i < NumGlyphs; ++i)
{
char c = (char)(i + SpriteFont.LowGlyphAscii);
if (!GetGlyphMetrics(ttf, c, size, ref metrics))
throw new Exception(String.Format("Failed getting metrics for glyph \"{0}\".", c));
// adjust each glyph's rect so that it has it's own space that doesn't
// collide with any of it's neighbour glyphs (neighbour as seen on the
// font bitmap)
if (metrics.Dimensions.Left < 0)
{
// bump the glyph over to the right by the same amount it was over to the left
metrics.Advance += -metrics.Dimensions.Left;
metrics.Dimensions.Left = 0;
}
// do we need to move to the next row?
if ((x + metrics.Advance + metrics.Dimensions.Left) >= fontBitmap.Width)
{
// yes
x = 0;
y += lineHeight;
System.Diagnostics.Debug.Assert((y + lineHeight) < fontBitmap.Height);
}
// the destination bitmap pixel coordinates of this glyph. these are the
// pixel coordinates that will be stored in the font's texture atlas
// which will be used by the texture atlas to build texture coords
position.Left = x;
position.Top = y;
position.Right = x + metrics.Advance;
position.Bottom = y + lineHeight;
// top-left coords and dimensions to have stb_truetype draw the glyph at in the font bitmap
int drawX = position.Left + metrics.Dimensions.Left;
int drawY = (position.Bottom + metrics.Descent) + metrics.Descent - metrics.Dimensions.Bottom + maxMetrics.LineGap;
int drawWidth = position.Width;
int drawHeight = position.Height;
// resize the glyph bitmap only when necessary
if (drawWidth > glyphBitmap.Width || drawHeight > glyphBitmap.Height)
glyphBitmap = new FontBitmap(drawWidth, drawHeight);
// render glyph
ttf.MakeGlyphBitmap(metrics.Index, metrics.Scale, metrics.Scale, glyphBitmap);
// copy from the temp bitmap to our full font bitmap (unfortunately TrueTypeSharp doesn't give us
// a way to do this directly out of the box)
fontBitmap.Copy(glyphBitmap.Buffer, glyphBitmap.Width, glyphBitmap.Height, 8, 0, 0, drawWidth, drawHeight, drawX, drawY);
if (glyphs != null)
{
// add the glyph position to the texture atlas (which will calc the texture coords for us)
int newIndex = glyphs.Add(position);
System.Diagnostics.Debug.Assert(newIndex == ((int)c - SpriteFont.LowGlyphAscii));
}
// move to the next glyph's position in the bitmap
x += maxMetrics.Advance;
}
SpriteFont font;
if (existingFont == null)
{
var texture = new Texture(graphicsDevice, fontBitmap, TextureParameters.Pixelated);
font = new SpriteFont(graphicsDevice, size, texture, glyphs);
}
else
{
font = existingFont;
// texture will have been reallocated already by SpriteFont, just need to provide the bitmap
// and the Reload method will handle updating the texture itself
font.Reload(fontBitmap);
}
return font;
}
private static bool GetGlyphMetrics(TrueTypeFont font, char glyph, int size, ref SpriteFontGlyphMetrics metrics)
{
uint index = font.FindGlyphIndex(glyph);
if (index == 0)
return false;
metrics.Scale = font.GetScaleForPixelHeight((float)size);
metrics.LetterHeight = size;
metrics.Index = index;
font.GetGlyphBox(index, out metrics.Dimensions.Left, out metrics.Dimensions.Top, out metrics.Dimensions.Right, out metrics.Dimensions.Bottom);
font.GetFontVMetrics(out metrics.Ascent, out metrics.Descent, out metrics.LineGap);
int leftSideBearing;
font.GetGlyphHMetrics(index, out metrics.Advance, out leftSideBearing);
// adjust all the metrics we got by the font size scale value
// (I guess this puts them from whatever units they were in to pixel units)
metrics.Dimensions.Left = (int)Math.Ceiling((double)metrics.Dimensions.Left * metrics.Scale);
metrics.Dimensions.Top = (int)Math.Ceiling((double)metrics.Dimensions.Top * metrics.Scale);
metrics.Dimensions.Right = (int)Math.Ceiling((double)metrics.Dimensions.Right * metrics.Scale);
metrics.Dimensions.Bottom = (int)Math.Ceiling((double)metrics.Dimensions.Bottom * metrics.Scale);
metrics.Ascent = (int)Math.Ceiling((double)metrics.Ascent * metrics.Scale);
metrics.Descent = (int)Math.Ceiling((double)metrics.Descent * metrics.Scale);
metrics.LineGap = (int)Math.Ceiling((double)metrics.LineGap * metrics.Scale);
metrics.Advance = (int)Math.Ceiling((double)metrics.Advance * metrics.Scale);
metrics.LetterWidth = metrics.Dimensions.Right - metrics.Dimensions.Left;
// seen some pixel/bitmap fonts that have the total ascent/descent calculated height
// greater then the pixel height. this just figures out this difference, if present,
// and sets an appropriate line gap equal to it (in these cases, linegap was 0)
int calculatedHeight = metrics.Ascent - metrics.Descent;
int heightDifference = Math.Abs(calculatedHeight - metrics.LetterHeight);
if (heightDifference != metrics.LineGap && metrics.LineGap == 0)
metrics.LineGap = heightDifference;
return true;
}
}
}

View file

@ -0,0 +1,40 @@
using System;
using System.IO;
namespace Blarg.GameFramework.Graphics
{
public abstract class SpriteShader : StandardShader
{
protected string TextureHasAlphaOnlyUniformName { get; set; }
protected SpriteShader(GraphicsDevice graphicsDevice)
: base(graphicsDevice)
{
Initialize();
}
protected SpriteShader(GraphicsDevice graphicsDevice, string vertexShaderSource, string fragmentShaderSource)
: base(graphicsDevice, vertexShaderSource, fragmentShaderSource)
{
Initialize();
}
protected SpriteShader(GraphicsDevice graphicsDevice, TextReader vertexShaderSourceReader, TextReader fragmentShaderSourceReader)
: base(graphicsDevice, vertexShaderSourceReader, fragmentShaderSourceReader)
{
Initialize();
}
private void Initialize()
{
TextureHasAlphaOnlyUniformName = "u_textureHasAlphaOnly";
}
public void SetTextureHasAlphaOnly(bool hasAlphaOnly)
{
if (!IsReadyForUse)
throw new InvalidOperationException();
SetUniform(TextureHasAlphaOnlyUniformName, Convert.ToInt32(hasAlphaOnly));
}
}
}

View file

@ -0,0 +1,59 @@
using System;
using System.IO;
namespace Blarg.GameFramework.Graphics
{
public abstract class StandardShader : Shader
{
protected string ModelViewMatrixUniformName { get; set; }
protected string ProjectionMatrixUniformName { get; set; }
protected StandardShader(GraphicsDevice graphicsDevice)
: base(graphicsDevice)
{
Initialize();
}
protected StandardShader(GraphicsDevice graphicsDevice, string vertexShaderSource, string fragmentShaderSource)
: base(graphicsDevice, vertexShaderSource, fragmentShaderSource)
{
Initialize();
}
protected StandardShader(GraphicsDevice graphicsDevice, TextReader vertexShaderSourceReader, TextReader fragmentShaderSourceReader)
: base(graphicsDevice, vertexShaderSourceReader, fragmentShaderSourceReader)
{
Initialize();
}
private void Initialize()
{
ModelViewMatrixUniformName = "u_modelViewMatrix";
ProjectionMatrixUniformName = "u_projectionMatrix";
}
public void SetModelViewMatrix(Matrix4x4 m)
{
SetModelViewMatrix(ref m);
}
public void SetModelViewMatrix(ref Matrix4x4 m)
{
if (!IsReadyForUse)
throw new InvalidOperationException();
SetUniform(ModelViewMatrixUniformName, ref m);
}
public void SetProjectionMatrix(Matrix4x4 m)
{
SetProjectionMatrix(ref m);
}
public void SetProjectionMatrix(ref Matrix4x4 m)
{
if (!IsReadyForUse)
throw new InvalidOperationException();
SetUniform(ProjectionMatrixUniformName, ref m);
}
}
}

View file

@ -0,0 +1,286 @@
using System;
using PortableGL;
namespace Blarg.GameFramework.Graphics
{
public enum TextureFormat
{
RGB,
RGBA,
Alpha,
Depth
}
public class Texture : GraphicsContextResource
{
private TextureParameters _textureParams;
public int ID { get; private set; }
public int Width { get; private set; }
public int Height { get; private set; }
public TextureFormat Format { get; private set; }
public bool IsInvalidated
{
get { return ID == -1; }
}
public Texture(GraphicsDevice graphicsDevice, Image image, TextureParameters textureParameters = null)
: base(graphicsDevice)
{
// if this was specified, we should make our own separate copy of it as these
// parameters should stay the same when/if the texture is recreated
if (textureParameters != null)
_textureParams = (TextureParameters)textureParameters.Clone();
CreateTexture(image);
}
public Texture(GraphicsDevice graphicsDevice, int width, int height, TextureFormat format, TextureParameters textureParameters = null)
: base(graphicsDevice)
{
// if this was specified, we should make our own separate copy of it as these
// parameters should stay the same when/if the texture is recreated
if (textureParameters != null)
_textureParams = (TextureParameters)textureParameters.Clone();
CreateTexture(width, height, format);
}
#region OpenGL Texture Creation
private void CreateTexture(Image image)
{
if (image == null)
throw new ArgumentNullException("image");
if (!GraphicsDevice.IsNonPowerOfTwoTextureSupported)
{
if (!MathHelpers.IsPowerOf2(image.Width) || !MathHelpers.IsPowerOf2(image.Height))
throw new InvalidOperationException();
}
if (image.BitsPerPixel == 8 && image.PixelFormat != ImageFormat.A)
throw new InvalidOperationException();
if (image.BitsPerPixel == 16)
throw new InvalidOperationException();
TextureFormat textureFormat;
int internalFormat;
int pixelFormat;
int pixelType = GL20.GL_UNSIGNED_BYTE;
if (image.PixelFormat == ImageFormat.A)
{
textureFormat = TextureFormat.Alpha;
internalFormat = GL20.GL_ALPHA;
pixelFormat = GL20.GL_ALPHA;
}
else
{
if (image.BitsPerPixel == 24)
{
textureFormat = TextureFormat.RGB;
internalFormat = GL20.GL_RGB;
pixelFormat = GL20.GL_RGB;
}
else if (image.BitsPerPixel == 32)
{
textureFormat = TextureFormat.RGBA;
internalFormat = GL20.GL_RGBA;
pixelFormat = GL20.GL_RGBA;
}
else
throw new InvalidOperationException();
}
Width = image.Width;
Height = image.Height;
Format = textureFormat;
ID = Platform.GL.glGenTextures();
GraphicsDevice.BindTexture(this, 0);
if (_textureParams == null)
_textureParams = GraphicsDevice.GetCopyOfTextureParameters();
_textureParams.Apply();
Platform.GL.glTexImage2D(GL20.GL_TEXTURE_2D, 0, internalFormat, Width, Height, 0, pixelFormat, pixelType, image.Pixels);
Platform.Logger.Info("Graphics", "Created texture from image. ID = {0}, bpp = {1}, size = {2}x{3}.", ID, image.BitsPerPixel, Width, Height);
}
private void CreateTexture(int width, int height, TextureFormat format, bool useExistingTextureParams = false)
{
if (!GraphicsDevice.IsNonPowerOfTwoTextureSupported)
{
if (!MathHelpers.IsPowerOf2(width) || !MathHelpers.IsPowerOf2(height))
throw new InvalidOperationException("Texture dimensions must be a power of 2.");
}
int bpp = 0;
int internalFormat;
int pixelFormat;
int pixelType;
GetTextureSpecsFromFormat(format, out bpp, out internalFormat, out pixelFormat, out pixelType);
if (bpp == 0)
throw new InvalidOperationException();
Width = width;
Height = height;
Format = format;
ID = Platform.GL.glGenTextures();
GraphicsDevice.BindTexture(this, 0);
if (!useExistingTextureParams || _textureParams == null)
_textureParams = GraphicsDevice.GetCopyOfTextureParameters();
_textureParams.Apply();
Platform.GL.glTexImage2D(GL20.GL_TEXTURE_2D, 0, internalFormat, Width, Height, 0, pixelFormat, pixelType, IntPtr.Zero);
if (Format == TextureFormat.Depth)
Platform.Logger.Info("Graphics", "Created uninitialized texture. ID = {0}, depth component only, size = {1}x{2}", ID, Width, Height);
else
Platform.Logger.Info("Graphics", "Created uninitialized texture. ID = {0}, bpp = {1}, size = {2}x{3}", ID, bpp, Width, Height);
}
#endregion
public void Update(Image image, int destX = 0, int destY = 0)
{
if (IsInvalidated)
throw new InvalidOperationException();
if (Format == TextureFormat.Depth)
throw new InvalidOperationException();
if (image == null)
throw new ArgumentNullException("image");
if (destX < 0 || destX >= Width)
throw new InvalidOperationException();
if (destY < 0 || destY >= Height)
throw new InvalidOperationException();
if (image.Width > Width)
throw new InvalidOperationException();
if (image.Height > Height)
throw new InvalidOperationException();
if ((destX + image.Width) > Width)
throw new InvalidOperationException();
if ((destY + image.Height) > Height)
throw new InvalidOperationException();
if (image.BitsPerPixel == 8 && image.PixelFormat != ImageFormat.A)
throw new InvalidOperationException();
if (image.BitsPerPixel == 16)
throw new InvalidOperationException();
int pixelFormat;
int pixelType = GL20.GL_UNSIGNED_BYTE;
if (image.PixelFormat == ImageFormat.A)
pixelFormat = GL20.GL_ALPHA;
else
{
if (image.BitsPerPixel == 24)
pixelFormat = GL20.GL_RGB;
else if (image.BitsPerPixel == 32)
pixelFormat = GL20.GL_RGBA;
else
throw new InvalidOperationException();
}
GraphicsDevice.BindTexture(this, 0);
Platform.GL.glTexSubImage2D(GL20.GL_TEXTURE_2D, 0, destX, destY, image.Width, image.Height, pixelFormat, pixelType, image.Pixels);
}
private void GetTextureSpecsFromFormat(TextureFormat textureFormat, out int bpp, out int internalFormat, out int pixelFormat, out int type)
{
switch (textureFormat)
{
case TextureFormat.Alpha:
bpp = 8;
internalFormat = GL20.GL_ALPHA;
pixelFormat = GL20.GL_ALPHA;
type = GL20.GL_UNSIGNED_BYTE;
break;
case TextureFormat.RGB:
bpp = 24;
internalFormat = GL20.GL_RGB;
pixelFormat = GL20.GL_RGB;
type = GL20.GL_UNSIGNED_BYTE;
break;
case TextureFormat.RGBA:
bpp = 32;
internalFormat = GL20.GL_RGBA;
pixelFormat = GL20.GL_RGBA;
type = GL20.GL_UNSIGNED_BYTE;
break;
case TextureFormat.Depth:
bpp = 0; // doesn't really matter for this one... ?
internalFormat = GL20.GL_DEPTH_COMPONENT;
pixelFormat = GL20.GL_DEPTH_COMPONENT;
// TODO: check that these are correct ...
if (Platform.Application.Type == PlatformType.Mobile)
type = GL20.GL_UNSIGNED_SHORT;
else
type = GL20.GL_FLOAT;
break;
default:
bpp = 0;
// junk -- just to appease the compiler
internalFormat = GL20.GL_RGBA;
pixelFormat = GL20.GL_RGBA;
type = GL20.GL_UNSIGNED_BYTE;
break;
}
}
#region GraphicsContextResource
public override void OnNewContext()
{
// TODO: recreate empty texture with same width/height/format
// (it is up to the application code to refill proper image data,
// or the ContentManager if loaded that way)
CreateTexture(Width, Height, Format, true);
}
public override void OnLostContext()
{
ID = -1;
}
protected override bool ReleaseResource()
{
// this needs to happen before the OpenGL context is destroyed
// which is not guaranteed if we rely 100% on the GC to clean
// everything up. best solution is to ensure all Texture
// objects are not being referenced before the window is
// closed and do a GC.Collect()
if (!IsInvalidated)
{
if (GraphicsDevice != null)
GraphicsDevice.UnbindTexture(this);
Platform.GL.glDeleteTextures(ID);
Platform.Logger.Info("Graphics", "Deleted Texture ID = {0}.", ID);
ID = -1;
}
return true;
}
#endregion
}
}

View file

@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
namespace Blarg.GameFramework.Graphics
{
public abstract class TextureAtlas
{
public const float TexCoordEdgeBleedOffset = 0.02f;
private Texture _texture;
public int Width { get; protected set; }
public int Height { get; protected set; }
protected float TexCoordEdgeOffset { get; private set; }
protected List<TextureRegion> Tiles { get; private set; }
public Texture Texture
{
get
{
return _texture;
}
set
{
if (value == null)
throw new ArgumentNullException();
if (value.Width != Width || value.Height != Height)
throw new InvalidOperationException();
_texture = value;
}
}
public TextureAtlas(int textureWidth, int textureHeight, float texCoordEdgeOffset = TexCoordEdgeBleedOffset)
{
if (textureWidth < 0)
throw new ArgumentOutOfRangeException("textureWidth");
if (textureHeight < 0)
throw new ArgumentOutOfRangeException("textureHeight");
Width = textureWidth;
Height = textureHeight;
TexCoordEdgeOffset = texCoordEdgeOffset;
Tiles = new List<TextureRegion>();
}
public TextureAtlas(Texture texture, float texCoordEdgeOffset = TexCoordEdgeBleedOffset)
{
if (texture == null)
throw new ArgumentNullException("texture");
_texture = texture;
Width = _texture.Width;
Height = _texture.Height;
TexCoordEdgeOffset = texCoordEdgeOffset;
Tiles = new List<TextureRegion>();
}
public int NumTiles
{
get { return Tiles.Count; }
}
public TextureRegion GetTile(int index)
{
return Tiles[index];
}
public void GetTileDimensions(int index, out Rect dimensions)
{
dimensions = Tiles[index].Dimensions;
}
public void GetTileTexCoords(int index, out RectF texCoords)
{
texCoords = Tiles[index].TexCoords;
}
}
}

View file

@ -0,0 +1,115 @@
using System;
using PortableGL;
namespace Blarg.GameFramework.Graphics
{
public enum MinificationFilter
{
Nearest,
Linear,
NearestMipmapNearest,
LinearMipmapNearest,
NearestMipmapLinear,
LinearMipmapLinear
}
public enum MagnificationFilter
{
Nearest,
Linear
}
public enum WrapMode
{
ClampToEdge,
Repeat
}
public class TextureParameters
{
public static readonly TextureParameters Default;
public static readonly TextureParameters Pixelated;
public MinificationFilter MinFilter { get; set; }
public MagnificationFilter MagFilter { get; set; }
public WrapMode WrapS { get; set; }
public WrapMode WrapT { get; set; }
static TextureParameters()
{
Default = new TextureParameters();
Pixelated = new TextureParameters(MinificationFilter.Nearest, MagnificationFilter.Nearest);
}
public TextureParameters()
{
Init();
}
public TextureParameters(MinificationFilter minFilter, MagnificationFilter magFilter)
{
Init();
MinFilter = minFilter;
MagFilter = magFilter;
}
public void Apply()
{
int minFilter = GL20.GL_NEAREST;
int magFilter = GL20.GL_LINEAR;
int wrapS = GL20.GL_REPEAT;
int wrapT = GL20.GL_REPEAT;
switch (MinFilter)
{
case MinificationFilter.Nearest: minFilter = GL20.GL_NEAREST; break;
case MinificationFilter.Linear: minFilter = GL20.GL_LINEAR; break;
case MinificationFilter.NearestMipmapNearest: minFilter = GL20.GL_NEAREST_MIPMAP_NEAREST; break;
case MinificationFilter.LinearMipmapNearest: minFilter = GL20.GL_LINEAR_MIPMAP_NEAREST; break;
case MinificationFilter.NearestMipmapLinear: minFilter = GL20.GL_NEAREST_MIPMAP_LINEAR; break;
case MinificationFilter.LinearMipmapLinear: minFilter = GL20.GL_LINEAR_MIPMAP_LINEAR; break;
}
switch (MagFilter)
{
case MagnificationFilter.Nearest: magFilter = GL20.GL_NEAREST; break;
case MagnificationFilter.Linear: magFilter = GL20.GL_LINEAR; break;
}
switch (WrapS)
{
case WrapMode.ClampToEdge: wrapS = GL20.GL_CLAMP_TO_EDGE; break;
case WrapMode.Repeat: wrapS = GL20.GL_REPEAT; break;
}
switch (WrapT)
{
case WrapMode.ClampToEdge: wrapT = GL20.GL_CLAMP_TO_EDGE; break;
case WrapMode.Repeat: wrapT = GL20.GL_REPEAT; break;
}
Platform.GL.glTexParameteri(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MIN_FILTER, (int)minFilter);
Platform.GL.glTexParameteri(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MAG_FILTER, (int)magFilter);
Platform.GL.glTexParameteri(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_S, (int)wrapS);
Platform.GL.glTexParameteri(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_T, (int)wrapT);
}
private void Init()
{
MinFilter = MinificationFilter.Nearest;
MagFilter = MagnificationFilter.Linear;
WrapS = WrapMode.Repeat;
WrapT = WrapMode.Repeat;
}
public TextureParameters Clone()
{
var clone = new TextureParameters();
clone.MagFilter = MagFilter;
clone.MinFilter = MinFilter;
clone.WrapS = WrapS;
clone.WrapT = WrapT;
return clone;
}
}
}

View file

@ -0,0 +1,10 @@
using System;
namespace Blarg.GameFramework.Graphics
{
public struct TextureRegion
{
public Rect Dimensions;
public RectF TexCoords;
}
}

View file

@ -0,0 +1,81 @@
using System;
namespace Blarg.GameFramework.Graphics
{
public enum VertexStandardAttributes
{
// high byte = standard type bitmask
// low byte = number of floats needed
Position2D = 0x0102,
Position3D = 0x0203,
Normal = 0x0403,
Color = 0x0804,
TexCoord = 0x1002
}
public enum VertexAttributes
{
// standard types
// high byte = standard type bitmask
// low byte = number of floats needed
Position2D = VertexStandardAttributes.Position2D,
Position3D = VertexStandardAttributes.Position3D,
Normal = VertexStandardAttributes.Normal,
Color = VertexStandardAttributes.Color,
TexCoord = VertexStandardAttributes.TexCoord,
// generic types, value is equal to number of floats needed
Float1 = 1,
Float2 = 2,
Float3 = 3,
Float4 = 4,
Vector2 = 2,
Vector3 = 3,
Vector4 = 4,
Matrix3x3 = 9,
Matrix4x4 = 16
}
public static class VertexAttributeDeclarations
{
public static readonly VertexAttributes[] ColorPosition3D = new VertexAttributes[]
{
VertexAttributes.Color,
VertexAttributes.Position3D
};
public static readonly VertexAttributes[] TexturePosition3D = new VertexAttributes[]
{
VertexAttributes.TexCoord,
VertexAttributes.Position3D
};
public static readonly VertexAttributes[] ColorNormalPosition3D = new VertexAttributes[]
{
VertexAttributes.Color,
VertexAttributes.Normal,
VertexAttributes.Position3D
};
public static readonly VertexAttributes[] TextureNormalPosition3D = new VertexAttributes[]
{
VertexAttributes.TexCoord,
VertexAttributes.Normal,
VertexAttributes.Position3D
};
public static readonly VertexAttributes[] TextureColorPosition2D = new VertexAttributes[]
{
VertexAttributes.TexCoord,
VertexAttributes.Color,
VertexAttributes.Position2D
};
public static readonly VertexAttributes[] TextureColorPosition3D = new VertexAttributes[]
{
VertexAttributes.TexCoord,
VertexAttributes.Color,
VertexAttributes.Position3D
};
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,40 @@
using System;
using System.IO;
namespace Blarg.GameFramework.Graphics
{
public abstract class VertexLerpShader : StandardShader
{
protected string LerpUniformName { get; set; }
protected VertexLerpShader(GraphicsDevice graphicsDevice)
: base(graphicsDevice)
{
Initialize();
}
protected VertexLerpShader(GraphicsDevice graphicsDevice, string vertexShaderSource, string fragmentShaderSource)
: base(graphicsDevice, vertexShaderSource, fragmentShaderSource)
{
Initialize();
}
protected VertexLerpShader(GraphicsDevice graphicsDevice, TextReader vertexShaderSourceReader, TextReader fragmentShaderSourceReader)
: base(graphicsDevice, vertexShaderSourceReader, fragmentShaderSourceReader)
{
Initialize();
}
private void Initialize()
{
LerpUniformName = "u_lerp";
}
public void SetLerp(float t)
{
if (!IsReadyForUse)
throw new InvalidOperationException();
SetUniform(LerpUniformName, t);
}
}
}

View file

@ -0,0 +1,49 @@
using System;
using System.IO;
namespace Blarg.GameFramework.Graphics
{
public abstract class VertexSkinningShader : StandardShader
{
protected string JointPositionsUniformName { get; set; }
protected string JointRotationsUniformName { get; set; }
protected VertexSkinningShader(GraphicsDevice graphicsDevice)
: base(graphicsDevice)
{
Initialize();
}
protected VertexSkinningShader(GraphicsDevice graphicsDevice, string vertexShaderSource, string fragmentShaderSource)
: base(graphicsDevice, vertexShaderSource, fragmentShaderSource)
{
Initialize();
}
protected VertexSkinningShader(GraphicsDevice graphicsDevice, TextReader vertexShaderSourceReader, TextReader fragmentShaderSourceReader)
: base(graphicsDevice, vertexShaderSourceReader, fragmentShaderSourceReader)
{
Initialize();
}
private void Initialize()
{
JointPositionsUniformName = "u_jointPositions";
JointRotationsUniformName = "u_jointRotations";
}
public void SetJointPositions(Vector3[] positions)
{
if (!IsReadyForUse)
throw new InvalidOperationException();
SetUniform(JointPositionsUniformName, positions);
}
public void SetJointRotations(Quaternion[] rotations)
{
if (!IsReadyForUse)
throw new InvalidOperationException();
SetUniform(JointRotationsUniformName, rotations);
}
}
}

View file

@ -0,0 +1,226 @@
using System;
using PortableGL;
namespace Blarg.GameFramework.Graphics
{
public class ViewContext
{
private GraphicsDevice _graphicsDevice;
private Rect _viewport;
private bool _viewportIsFixedSize;
private ScreenOrientation _screenOrientation;
private Camera _camera;
private bool _isUsingDefaultCamera;
private Matrix4x4 _modelViewMatrix;
private Matrix4x4 _projectionMatrix;
public int ViewportTop { get { return _viewport.Top; } }
public int ViewportLeft { get { return _viewport.Left; } }
public int ViewportBottom { get { return _viewport.Bottom; } }
public int ViewportRight { get { return _viewport.Right; } }
public int ViewportWidth { get { return _viewport.Width; } }
public int ViewportHeight { get { return _viewport.Height; } }
public bool IsViewportFixedSize { get { return _viewportIsFixedSize; } }
public bool IgnoringScreenRotation { get { return _viewportIsFixedSize; } }
public Camera Camera
{
get
{
return _camera;
}
set
{
bool cameraWasChanged = false;
// using the default camera but a new camera is being provided?
if (_isUsingDefaultCamera && value != null)
{
_isUsingDefaultCamera = false;
_camera = value;
cameraWasChanged = true;
}
// not using the default camera already, but setting a new camera
else if (!_isUsingDefaultCamera && value != null)
{
_camera = value;
cameraWasChanged = true;
}
// not using the default camera, and clearing ("nulling") the camera
else if (!_isUsingDefaultCamera && value == null)
{
_camera = new Camera(this);
_isUsingDefaultCamera = true;
cameraWasChanged = true;
}
// update our local projection matrix if a new camera was applied
// (otherwise, we wouldn't get it until the next OnResize...)
if (cameraWasChanged)
ProjectionMatrix = _camera.Projection;
}
}
public Matrix4x4 ProjectionMatrix
{
get
{
return _projectionMatrix;
}
set
{
if (!IgnoringScreenRotation && _screenOrientation != ScreenOrientation.Rotation0)
{
// apply a rotation immediately _after_ the projection matrix transform
Matrix4x4 rotation;
Matrix4x4.CreateRotationZ(MathHelpers.DegreesToRadians(-((float)_screenOrientation)), out rotation);
Matrix4x4.Multiply(ref rotation, ref value, out _projectionMatrix);
}
else
_projectionMatrix = value;
}
}
public Matrix4x4 ModelViewMatrix
{
get
{
return _modelViewMatrix;
}
set
{
_modelViewMatrix = value;
}
}
public Matrix4x4 OrthographicProjectionMatrix
{
get
{
Matrix4x4 ortho;
Matrix4x4.CreateOrthographic((float)_viewport.Left, (float)_viewport.Right, (float)_viewport.Top, (float)_viewport.Bottom, 0.0f, 1.0f, out ortho);
if (!IgnoringScreenRotation && _screenOrientation != ScreenOrientation.Rotation0)
{
// apply a rotation immediately _after_ the projection matrix transform
Matrix4x4 rotation;
Matrix4x4.CreateRotationZ(MathHelpers.DegreesToRadians(-((float)_screenOrientation)), out rotation);
Matrix4x4.Multiply(ref rotation, ref ortho, out ortho);
}
return ortho;
}
}
public ViewContext(GraphicsDevice graphicsDevice)
{
if (graphicsDevice == null)
throw new ArgumentNullException("graphicsDevice");
_graphicsDevice = graphicsDevice;
/*
var r = new Rect(
_graphicsDevice.Window.ClientRectangle.Left,
_graphicsDevice.Window.ClientRectangle.Top,
_graphicsDevice.Window.ClientRectangle.Right,
_graphicsDevice.Window.ClientRectangle.Bottom
);
*/
var r = new Rect(0, 0, Platform.Application.Window.ClientWidth, Platform.Application.Window.ClientHeight);
Init(r, false);
}
public ViewContext(GraphicsDevice graphicsDevice, Rect fixedViewportSize)
{
if (graphicsDevice == null)
throw new ArgumentNullException("graphicsDevice");
_graphicsDevice = graphicsDevice;
Init(fixedViewportSize, true);
}
private void Init(Rect viewport, bool isFixedSizeViewport)
{
_viewport = viewport;
_viewportIsFixedSize = isFixedSizeViewport;
_screenOrientation = ScreenOrientation.Rotation0;
_camera = new Camera(this);
_isUsingDefaultCamera = true;
}
public void OnNewContext()
{
_modelViewMatrix = Matrix4x4.Identity;
_projectionMatrix = Matrix4x4.Identity;
}
public void OnLostContext()
{
}
public void OnResize(ref Rect size, ScreenOrientation screenOrientation = ScreenOrientation.Rotation0)
{
SetupViewport(ref size, screenOrientation);
}
public void OnRender(float delta)
{
if (_camera != null)
_camera.OnRender(delta);
}
public void OnApply(ref Rect size, ScreenOrientation screenOrientation = ScreenOrientation.Rotation0)
{
SetupViewport(ref size, screenOrientation);
// ensures it's set up for rendering immediately when this call returns
// NOTE: we assume OnApply() is going to be called in some other class's
// OnRender() event only (like, e.g. if a new framebuffer is bound)
if (_camera != null)
_camera.OnRender(0.0f);
}
private void SetupViewport(ref Rect size, ScreenOrientation screenOrientation)
{
Rect viewport;
if (_viewportIsFixedSize)
{
viewport = _viewport;
_screenOrientation = ScreenOrientation.Rotation0;
}
else
{
// based on the orientation, we may need to swap the width/height
// of the passed viewport dimensions
// (we don't do viewport rotation if the viewport is fixed)
if (!IgnoringScreenRotation && (screenOrientation == ScreenOrientation.Rotation90 || screenOrientation == ScreenOrientation.Rotation270))
{
// swap width and height
viewport.Left = size.Top;
viewport.Top = size.Left;
viewport.Right = size.Bottom;
viewport.Bottom = size.Right;
}
else
viewport = size;
// we **don't** want this to be rotated
_viewport = size;
_screenOrientation = screenOrientation;
}
// we **do** obviously want this to be rotated (if there is a rotation)
Platform.GL.glViewport(viewport.Left, viewport.Top, viewport.Width, viewport.Height);
// we also **don't** want the camera to work with a rotated viewport
if (_camera != null)
_camera.OnResize(ref _viewport);
}
}
}

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,31 @@
using System;
using System.IO;
using System.Reflection;
namespace Blarg.GameFramework.Resources
{
public static class ResourceUtils
{
public static Stream GetResource(string filename)
{
if (String.IsNullOrEmpty(filename))
throw new ArgumentNullException("filename");
return Assembly.GetExecutingAssembly().GetManifestResourceStream(filename);
}
public static string GetTextResource(string filename)
{
using (var stream = GetResource(filename))
{
if (stream == null)
return null;
using (var reader = new StreamReader(stream))
{
return reader.ReadToEnd();
}
}
}
}
}

View file

@ -0,0 +1,13 @@
#ifdef GL_ES
#define LOWP lowp
precision mediump float;
#else
#define LOWP
#endif
varying LOWP vec4 v_color;
void main()
{
gl_FragColor = v_color;
}

View file

@ -0,0 +1,12 @@
attribute vec4 a_position;
attribute vec4 a_color;
uniform mat4 u_modelViewMatrix;
uniform mat4 u_projectionMatrix;
varying vec4 v_color;
void main()
{
v_color = a_color;
gl_PointSize = 4.0;
gl_Position = u_projectionMatrix * u_modelViewMatrix * a_position;
}

View file

@ -0,0 +1,12 @@
#ifdef GL_ES
#define LOWP lowp
precision mediump float;
#else
#define LOWP
#endif
varying LOWP vec4 v_color;
void main()
{
gl_FragColor = v_color;
}

View file

@ -0,0 +1,11 @@
attribute vec4 a_position;
attribute vec4 a_color;
uniform mat4 u_modelViewMatrix;
uniform mat4 u_projectionMatrix;
varying vec4 v_color;
void main()
{
v_color = a_color;
gl_Position = u_projectionMatrix * u_modelViewMatrix * a_position;
}

View file

@ -0,0 +1,14 @@
#ifdef GL_ES
#define LOWP lowp
precision mediump float;
#else
#define LOWP
#endif
varying LOWP vec4 v_color;
varying vec2 v_texCoords;
uniform sampler2D u_texture;
void main()
{
gl_FragColor = v_color * texture2D(u_texture, v_texCoords);
}

View file

@ -0,0 +1,14 @@
attribute vec4 a_position;
attribute vec4 a_color;
attribute vec2 a_texcoord0;
uniform mat4 u_modelViewMatrix;
uniform mat4 u_projectionMatrix;
varying vec4 v_color;
varying vec2 v_texCoords;
void main()
{
v_color = a_color;
v_texCoords = a_texcoord0;
gl_Position = u_projectionMatrix * u_modelViewMatrix * a_position;
}

View file

@ -0,0 +1,10 @@
#ifdef GL_ES
precision mediump float;
#endif
varying vec2 v_texCoords;
uniform sampler2D u_texture;
void main()
{
gl_FragColor = texture2D(u_texture, v_texCoords);
}

View file

@ -0,0 +1,12 @@
attribute vec4 a_position;
attribute vec2 a_texcoord0;
uniform mat4 u_modelViewMatrix;
uniform mat4 u_projectionMatrix;
varying vec4 v_color;
varying vec2 v_texCoords;
void main()
{
v_texCoords = a_texcoord0;
gl_Position = u_projectionMatrix * u_modelViewMatrix * a_position;
}

View file

@ -0,0 +1,20 @@
#ifdef GL_ES
#define LOWP lowp
precision mediump float;
#else
#define LOWP
#endif
varying LOWP vec4 v_color;
varying vec2 v_texCoords;
uniform sampler2D u_texture;
uniform bool u_textureHasAlphaOnly;
void main()
{
vec4 finalColor;
if (u_textureHasAlphaOnly)
finalColor = vec4(v_color.xyz, (v_color.a * texture2D(u_texture, v_texCoords).a));
else
finalColor = v_color * texture2D(u_texture, v_texCoords);
gl_FragColor = finalColor;
}

View file

@ -0,0 +1,14 @@
attribute vec4 a_position;
attribute vec4 a_color;
attribute vec2 a_texcoord0;
uniform mat4 u_modelViewMatrix;
uniform mat4 u_projectionMatrix;
varying vec4 v_color;
varying vec2 v_texCoords;
void main()
{
v_color = a_color;
v_texCoords = a_texcoord0;
gl_Position = u_projectionMatrix * u_modelViewMatrix * a_position;
}

View file

@ -0,0 +1,26 @@
#ifdef GL_ES
#define LOWP lowp
precision mediump float;
#else
#define LOWP
#endif
varying LOWP vec4 v_color;
varying vec2 v_texCoords;
uniform sampler2D u_texture;
uniform bool u_textureHasAlphaOnly;
void main()
{
vec4 texColor = texture2D(u_texture, v_texCoords);
if (texColor.a > 0.0)
{
vec4 finalColor;
if (u_textureHasAlphaOnly)
finalColor = vec4(v_color.xyz, (v_color.a * texColor.a));
else
finalColor = v_color * texColor;
gl_FragColor = finalColor;
}
else
discard;
}

View file

@ -0,0 +1,14 @@
attribute vec4 a_position;
attribute vec4 a_color;
attribute vec2 a_texcoord0;
uniform mat4 u_modelViewMatrix;
uniform mat4 u_projectionMatrix;
varying vec4 v_color;
varying vec2 v_texCoords;
void main()
{
v_color = a_color;
v_texCoords = a_texcoord0;
gl_Position = u_projectionMatrix * u_modelViewMatrix * a_position;
}

View file

@ -0,0 +1,10 @@
#ifdef GL_ES
precision mediump float;
#endif
varying vec2 v_texCoords;
uniform sampler2D u_texture;
void main()
{
gl_FragColor = texture2D(u_texture, v_texCoords);
}

View file

@ -0,0 +1,14 @@
attribute vec4 a_position1;
attribute vec4 a_position2;
attribute vec2 a_texcoord0;
uniform mat4 u_modelViewMatrix;
uniform mat4 u_projectionMatrix;
uniform float u_lerp;
varying vec4 v_color;
varying vec2 v_texCoords;
void main()
{
v_texCoords = a_texcoord0;
gl_Position = u_projectionMatrix * u_modelViewMatrix * mix(a_position1, a_position2, u_lerp);
}

View file

@ -0,0 +1,10 @@
#ifdef GL_ES
precision mediump float;
#endif
varying vec2 v_texCoord;
uniform sampler2D u_texture;
void main()
{
gl_FragColor = texture2D(u_texture, v_texCoord);
}

View file

@ -0,0 +1,32 @@
#ifdef GL_ES
precision mediump float;
#endif
const int MAX_BONES = 50;
attribute vec4 a_position;
attribute vec2 a_texcoord0;
attribute float a_jointIndex;
uniform mat4 u_modelViewMatrix;
uniform mat4 u_projectionMatrix;
uniform vec3 u_jointPositions[MAX_BONES];
uniform vec4 u_jointRotations[MAX_BONES];
varying vec2 v_texCoord;
vec3 qtransform(vec4 q, vec3 v)
{
vec3 temp = cross(q.xyz, v) + q.w * v;
return cross(temp, -q.xyz) + dot(q.xyz,v) * q.xyz + q.w * temp;
}
void main()
{
int j = int(a_jointIndex);
vec4 skinnedPosition = vec4(qtransform(u_jointRotations[j], a_position.xyz) + u_jointPositions[j], 1.0);
v_texCoord = a_texcoord0;
gl_Position = u_projectionMatrix * u_modelViewMatrix * skinnedPosition;
}

View file

@ -0,0 +1,20 @@
using System;
namespace Blarg.GameFramework.Support
{
internal static class StringExtensions
{
public static string Copy(string source)
{
if (source == null)
throw new ArgumentNullException("source");
var result = new char[source.Length];
for (int i = 0; i < source.Length; ++i)
result[i] = source[i];
return new string(result);
}
}
}