Initial commit
This commit is contained in:
commit
0b2510826b
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
.obj/
|
||||
.vs/
|
||||
packages/
|
||||
/TODO
|
58
BNA/BNA.csproj
Normal file
58
BNA/BNA.csproj
Normal file
@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{53E4C9F9-CE12-4067-8336-663D3582DCBA}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<RootNamespace>BNA</RootNamespace>
|
||||
<AssemblyName>BNA</AssemblyName>
|
||||
<RunPostBuildEvent>OnOutputUpdated</RunPostBuildEvent>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<!-- disable a warning about references having 'x86' architecture -->
|
||||
<ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\Solution.project" />
|
||||
<ItemGroup>
|
||||
<Reference Include="$(ObjDir)Android.dll" />
|
||||
<Reference Include="$(FNA_DLL)" />
|
||||
<Compile Include="src\**\*.cs" />
|
||||
<CustomAdditionalCompileInputs Include="FNA.filter" />
|
||||
<Filter Include="FNA.filter" />
|
||||
</ItemGroup>
|
||||
<!-- check prerequisites -->
|
||||
<Target Name="CheckPreReqs" BeforeTargets="ImportAndroid">
|
||||
<Error Condition="(! Exists($(ANDROID_JAR)))"
|
||||
Text="Cannot find Android JAR at '$(ANDROID_JAR)' using property ANDROID_JAR."/>
|
||||
<Error Condition="(! Exists($(BLUEBONNET_EXE)))"
|
||||
Text="Cannot find Bluebonnet EXE at '$(BLUEBONNET_EXE)' using property BLUEBONNET_EXE."/>
|
||||
<Error Condition="(! Exists($(FNA_DLL)))"
|
||||
Text="Cannot find FNA DLL at '$(FNA_DLL)' using environment variable FNA_DLL."/>
|
||||
</Target>
|
||||
<!-- import android.jar into android.dll -->
|
||||
<Target Name="ImportAndroid" BeforeTargets="ResolveAssemblyReferences"
|
||||
Inputs="$(ANDROID_JAR)" Outputs="$(ObjDir)android.dll">
|
||||
<Delete Files="$(ObjDir)android.dll" />
|
||||
<Exec Command=""$(BLUEBONNET_EXE)" "$(ANDROID_JAR)" "$(ObjDir)android.dll"" />
|
||||
</Target>
|
||||
<!-- middle of the build; compile BNA.dll here -->
|
||||
<!-- run our converter on BNA.dll to produce BNA.jar -->
|
||||
<Target Name="ExportToJar" AfterTargets="AfterBuild"
|
||||
Condition=" '$(_AssemblyTimestampBeforeCompile)' != '$(_AssemblyTimestampAfterCompile)'"
|
||||
Inputs="$(OutputPath)$(AssemblyName).dll" Outputs="$(ObjDir)$(AssemblyName).jar">
|
||||
<Delete Files="$(ObjDir)$(AssemblyName).jar" />
|
||||
<Exec Command=""$(BLUEBONNET_EXE)" "$(OutputPath)$(AssemblyName).dll" "$(ObjDir)$(AssemblyName).jar"" />
|
||||
<!-- run our converter on types from the FNA DLL and insert into BA.jar -->
|
||||
<ReadLinesFromFile File="@(Filter)">
|
||||
<Output TaskParameter="Lines" ItemName="FilterItem" />
|
||||
</ReadLinesFromFile>
|
||||
<PropertyGroup>
|
||||
<FilterProp>%22:@(FilterItem, '%22 %22:')%22</FilterProp>
|
||||
</PropertyGroup>
|
||||
<Exec Command=""$(BLUEBONNET_EXE)" "$(FNA_DLL)" "$(ObjDir)$(AssemblyName).jar" $(FilterProp)" />
|
||||
</Target>
|
||||
<Target Name="CleanGamelibInSolutionOutputDirectory" AfterTargets="Clean">
|
||||
<Delete Files="$(ObjDir)android.dll" />
|
||||
<Delete Files="$(ObjDir)$(AssemblyName).dll" />
|
||||
<Delete Files="$(ObjDir)$(AssemblyName).pdb" />
|
||||
<Delete Files="$(ObjDir)$(AssemblyName).jar" />
|
||||
</Target>
|
||||
</Project>
|
104
BNA/FNA.filter
Normal file
104
BNA/FNA.filter
Normal file
@ -0,0 +1,104 @@
|
||||
|
||||
*.Bounding*
|
||||
*.Color
|
||||
*.ContainmentType
|
||||
*.DisplayOrientation
|
||||
|
||||
*.DrawableGameComponent
|
||||
*.Game
|
||||
*.GameComponent
|
||||
*.GameComponentCollection
|
||||
*.GameComponentCollectionEventArgs
|
||||
*.GameServiceContainer
|
||||
*.GameTime
|
||||
*.GameWindow
|
||||
|
||||
*.GraphicsDeviceInformation
|
||||
*.GraphicsDeviceManager
|
||||
|
||||
*.IDrawable
|
||||
*.IGameComponent
|
||||
*.IGraphicsDeviceManager
|
||||
*.IUpdateable
|
||||
|
||||
*.MathHelper
|
||||
*.Matrix
|
||||
|
||||
*.PlayerIndex
|
||||
*.Point
|
||||
*.PreparingDeviceSettingsEventArgs
|
||||
|
||||
*.Quaternion
|
||||
|
||||
*.Rectangle
|
||||
|
||||
*.Vector2
|
||||
*.Vector3
|
||||
*.Vector4
|
||||
|
||||
*.Input.ButtonState
|
||||
*.Input.MouseState
|
||||
*.Input.Touch.GestureDetector
|
||||
*.Input.Touch.GestureSample
|
||||
*.Input.Touch.GestureType
|
||||
*.Input.Touch.TouchLocation
|
||||
*.Input.Touch.TouchLocationState
|
||||
*.Input.Touch.TouchPanel
|
||||
*.Input.Touch.TouchPanelCapabilities
|
||||
|
||||
*.Graphics.BasicEffect
|
||||
*.Graphics.Blend
|
||||
*.Graphics.BlendFunction
|
||||
*.Graphics.BlendState
|
||||
*.Graphics.BufferUsage
|
||||
*.Graphics.ClearOptions
|
||||
*.Graphics.ColorWriteChannels
|
||||
*.Graphics.CompareFunction
|
||||
*.Graphics.CubeMapFace
|
||||
*.Graphics.CullMode
|
||||
*.Graphics.DepthFormat
|
||||
*.Graphics.DepthStencilState
|
||||
*.Graphics.DirectionalLight
|
||||
*.Graphics.DisplayMode
|
||||
*.Graphics.DisplayModeCollection
|
||||
*.Graphics.DxtUtil
|
||||
*.Graphics.DynamicVertexBuffer
|
||||
*.Graphics.EffectDirtyFlags
|
||||
*.Graphics.EffectHelpers
|
||||
*.Graphics.EffectParameterCollection
|
||||
*.Graphics.EffectPass
|
||||
*.Graphics.EffectPassCollection
|
||||
*.Graphics.EffectTechnique
|
||||
*.Graphics.EffectTechniqueCollection
|
||||
*.Graphics.FillMode
|
||||
*.Graphics.GraphicsAdapter
|
||||
*.Graphics.GraphicsDevice
|
||||
*.Graphics.GraphicsProfile
|
||||
*.Graphics.GraphicsResource
|
||||
*.Graphics.IGraphicsDeviceService
|
||||
*.Graphics.IEffect*
|
||||
*.Graphics.IndexBuffer
|
||||
*.Graphics.IndexElementSize
|
||||
*.Graphics.IRenderTarget
|
||||
*.Graphics.IVertexType
|
||||
*.Graphics.NoSuitableGraphicsDeviceException
|
||||
*.Graphics.PipelineCache
|
||||
*.Graphics.PresentationParameters
|
||||
*.Graphics.PresentInterval
|
||||
*.Graphics.PrimitiveType
|
||||
*.Graphics.RasterizerState
|
||||
*.Graphics.RenderTarget*
|
||||
*.Graphics.SamplerState
|
||||
*.Graphics.SamplerStateCollection
|
||||
*.Graphics.Sprite*
|
||||
*.Graphics.StateHash
|
||||
*.Graphics.StencilOperation
|
||||
*.Graphics.SurfaceFormat
|
||||
*.Graphics.Texture*
|
||||
*.Graphics.Vertex*
|
||||
*.Graphics.Viewport
|
||||
|
||||
*.Graphics.PackedVector.*
|
||||
|
||||
*.Content.*
|
||||
*.Storage.*
|
119
BNA/src/Activity.cs
Normal file
119
BNA/src/Activity.cs
Normal file
@ -0,0 +1,119 @@
|
||||
|
||||
namespace Microsoft.Xna.Framework
|
||||
{
|
||||
|
||||
public class Activity : android.app.Activity
|
||||
{
|
||||
|
||||
//
|
||||
// Android onCreate
|
||||
//
|
||||
|
||||
protected override void onCreate(android.os.Bundle savedInstanceState)
|
||||
{
|
||||
base.onCreate(savedInstanceState);
|
||||
|
||||
_LogTag = GetMetaAttr("log.tag") ?? _LogTag;
|
||||
|
||||
new java.lang.Thread(gameRunner = new GameRunner(this)).start();
|
||||
}
|
||||
|
||||
//
|
||||
// FinishAndRestart
|
||||
//
|
||||
|
||||
public void FinishAndRestart(bool restart)
|
||||
{
|
||||
if (! (isFinishing() || isChangingConfigurations()))
|
||||
{
|
||||
runOnUiThread(((java.lang.Runnable.Delegate) (() =>
|
||||
{
|
||||
finish();
|
||||
restartActivity = restart;
|
||||
})).AsInterface());
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Android events forwarded to GameRunner:
|
||||
// onPause, onResume, onDestroy, onTouchEvent
|
||||
//
|
||||
|
||||
protected override void onPause()
|
||||
{
|
||||
gameRunner?.ActivityPause();
|
||||
base.onPause();
|
||||
}
|
||||
|
||||
protected override void onResume()
|
||||
{
|
||||
gameRunner?.ActivityResume();
|
||||
base.onResume();
|
||||
}
|
||||
|
||||
protected override void onDestroy()
|
||||
{
|
||||
gameRunner?.ActivityDestroy();
|
||||
base.onDestroy();
|
||||
|
||||
if (restartActivity)
|
||||
{
|
||||
// note, do not use activity.recreate() here, as it occasionally
|
||||
// keeps the old surface locked for a few seconds
|
||||
startActivity(getIntent()
|
||||
.addFlags(android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION)
|
||||
.addFlags(android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME)
|
||||
.addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
);
|
||||
}
|
||||
|
||||
// we have to destroy the activity process to get rid of leaking
|
||||
// static references that can never be garbage collected
|
||||
|
||||
java.lang.System.exit(0);
|
||||
}
|
||||
|
||||
public override bool onTouchEvent(android.view.MotionEvent motionEvent)
|
||||
{
|
||||
gameRunner?.ActivityTouch(motionEvent);
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// GetMetaAttr
|
||||
//
|
||||
|
||||
public string GetMetaAttr(string name, bool warn = false)
|
||||
{
|
||||
var info = getPackageManager().getActivityInfo(getComponentName(),
|
||||
android.content.pm.PackageManager.GET_ACTIVITIES
|
||||
| android.content.pm.PackageManager.GET_META_DATA);
|
||||
name = "microsoft.xna.framework." + name;
|
||||
var str = info?.metaData?.getString(name);
|
||||
if (string.IsNullOrEmpty(str))
|
||||
{
|
||||
if (warn)
|
||||
Activity.Log($"missing metadata attribute '{name}'");
|
||||
str = null;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
//
|
||||
// Log
|
||||
//
|
||||
|
||||
public static void Log(string s) => android.util.Log.i(_LogTag, s);
|
||||
|
||||
private static string _LogTag = "BNA_Game";
|
||||
|
||||
//
|
||||
// data
|
||||
//
|
||||
|
||||
private GameRunner gameRunner;
|
||||
private bool restartActivity;
|
||||
|
||||
}
|
||||
|
||||
}
|
881
BNA/src/Effect.cs
Normal file
881
BNA/src/Effect.cs
Normal file
@ -0,0 +1,881 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Xna.Framework;
|
||||
using android.opengl;
|
||||
#pragma warning disable 0436
|
||||
|
||||
namespace Microsoft.Xna.Framework.Graphics
|
||||
{
|
||||
public class Effect : GraphicsResource
|
||||
{
|
||||
|
||||
//
|
||||
// Effect
|
||||
//
|
||||
|
||||
public Effect(GraphicsDevice graphicsDevice, byte[] effectCode)
|
||||
{
|
||||
GraphicsDevice = graphicsDevice;
|
||||
CreateProgram(System.Text.Encoding.ASCII.GetString(effectCode));
|
||||
CollectUniforms();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// CreateProgram
|
||||
//
|
||||
|
||||
void CreateProgram(string text)
|
||||
{
|
||||
var (vertOfs, vertHdr) = HeaderOffset(text, "--- vertex ---", 0);
|
||||
var (fragOfs, fragHdr) = HeaderOffset(text, "--- fragment ---", vertOfs);
|
||||
var (stopOfs, stopHdr) = HeaderOffset(text, "--- end ---", fragOfs);
|
||||
|
||||
string versionDef = "#version 300 es\n";
|
||||
string precisionDef = "#ifdef GL_FRAGMENT_PRECISION_HIGH \n"
|
||||
+ "precision highp float; \n"
|
||||
+ "#else \n"
|
||||
+ "precision mediump float; \n"
|
||||
+ "#endif \n";
|
||||
|
||||
string vertText = versionDef + text.Substring(vertOfs + vertHdr,
|
||||
fragOfs - (vertOfs + vertHdr));
|
||||
string fragText = versionDef + precisionDef
|
||||
+ text.Substring(fragOfs + fragHdr,
|
||||
stopOfs - (fragOfs + fragHdr));
|
||||
|
||||
var err = CreateProgram2(vertText, fragText);
|
||||
if (err != null)
|
||||
throw new System.InvalidProgramException("Effect: " + err);
|
||||
|
||||
SetTechnique(text.Substring(0, vertOfs));
|
||||
|
||||
(int, int) HeaderOffset(string text, string header, int index)
|
||||
{
|
||||
index = text.IndexOf(header, index);
|
||||
if (index == -1)
|
||||
throw new System.InvalidProgramException("Effect: " + header);
|
||||
return (index, header.Length);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// CreateProgram2
|
||||
//
|
||||
|
||||
public string CreateProgram2(string vertexText, string fragmentText)
|
||||
{
|
||||
string errText = null;
|
||||
Renderer.Get(GraphicsDevice.GLDevice).Send( () =>
|
||||
{
|
||||
(vertexId, errText) = CompileShader(
|
||||
GLES20.GL_VERTEX_SHADER, "vertex", vertexText);
|
||||
if (errText == null)
|
||||
{
|
||||
(fragmentId, errText) = CompileShader(
|
||||
GLES20.GL_FRAGMENT_SHADER, "fragment", fragmentText);
|
||||
if (errText == null)
|
||||
{
|
||||
(programId, errText) = LinkProgram(vertexId, fragmentId);
|
||||
if (errText == null)
|
||||
{
|
||||
return; // success
|
||||
}
|
||||
}
|
||||
GLES20.glDeleteShader(fragmentId);
|
||||
}
|
||||
GLES20.glDeleteShader(vertexId);
|
||||
});
|
||||
return errText;
|
||||
|
||||
// CompileShader
|
||||
|
||||
(int, string) CompileShader(int kind, string errKind, string text)
|
||||
{
|
||||
//GameRunner.Log($"SHADER PROGRAM: [[[" + text + "]]]");
|
||||
string errText = null;
|
||||
int shaderId = GLES20.glCreateShader(kind);
|
||||
int errCode = GLES20.glGetError();
|
||||
if (shaderId == 0 || errCode != 0)
|
||||
errText = "glCreateShader";
|
||||
else
|
||||
{
|
||||
GLES20.glShaderSource(shaderId, text);
|
||||
errCode = GLES20.glGetError();
|
||||
if (errCode != 0)
|
||||
errText = "glShaderSource";
|
||||
else
|
||||
{
|
||||
GLES20.glCompileShader(shaderId);
|
||||
errCode = GLES20.glGetError();
|
||||
if (errCode != 0)
|
||||
errText = "glCompileShader";
|
||||
else
|
||||
{
|
||||
var status = new int[1];
|
||||
GLES20.glGetShaderiv(
|
||||
shaderId, GLES20.GL_COMPILE_STATUS, status, 0);
|
||||
errCode = GLES20.glGetError();
|
||||
if (errCode == 0 && status[0] != 0)
|
||||
{
|
||||
return (shaderId, null); // success
|
||||
}
|
||||
errText = "compile error: "
|
||||
+ GLES20.glGetShaderInfoLog(shaderId);
|
||||
}
|
||||
GLES20.glDeleteShader(shaderId);
|
||||
}
|
||||
}
|
||||
if (errCode != 0)
|
||||
errText = "GL error " + errCode + ": " + errText;
|
||||
errText = "in " + errKind + " shader: " + errText;
|
||||
return (0, errText);
|
||||
}
|
||||
|
||||
// LinkProgram
|
||||
|
||||
(int, string) LinkProgram(int vertexId, int fragmentId)
|
||||
{
|
||||
string errText = null;
|
||||
int programId = GLES20.glCreateProgram();
|
||||
int errCode = GLES20.glGetError();
|
||||
if (programId == 0 || errCode != 0)
|
||||
errText = "glCreateProgram";
|
||||
else
|
||||
{
|
||||
GLES20.glAttachShader(programId, vertexId);
|
||||
errCode = GLES20.glGetError();
|
||||
if (errCode != 0)
|
||||
errText = "glAttachShader (vertex)";
|
||||
else
|
||||
{
|
||||
GLES20.glAttachShader(programId, fragmentId);
|
||||
errCode = GLES20.glGetError();
|
||||
if (errCode != 0)
|
||||
errText = "glAttachShader (fragment)";
|
||||
else
|
||||
{
|
||||
GLES20.glLinkProgram(programId);
|
||||
errCode = GLES20.glGetError();
|
||||
if (errCode != 0)
|
||||
errText = "glLinkProgram";
|
||||
else
|
||||
{
|
||||
var status = new int[1];
|
||||
GLES20.glGetProgramiv(
|
||||
programId, GLES20.GL_LINK_STATUS, status, 0);
|
||||
errCode = GLES20.glGetError();
|
||||
if (errCode == 0 && status[0] != 0)
|
||||
{
|
||||
return (programId, null); // success
|
||||
}
|
||||
errText = "link error: "
|
||||
+ GLES20.glGetProgramInfoLog(programId);
|
||||
}
|
||||
GLES20.glDetachShader(programId, fragmentId);
|
||||
}
|
||||
GLES20.glDetachShader(programId, vertexId);
|
||||
}
|
||||
GLES20.glDeleteProgram(programId);
|
||||
}
|
||||
if (errCode != 0)
|
||||
errText = "GL error " + errCode + ": " + errText;
|
||||
errText = "in shader program: " + errText;
|
||||
return (0, errText);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// SetTechnique
|
||||
//
|
||||
|
||||
void SetTechnique(string text)
|
||||
{
|
||||
string name;
|
||||
string search = "#technique ";
|
||||
int idx = text.IndexOf(search);
|
||||
if (idx != -1)
|
||||
name = text.Substring(idx + search.Length).Trim();
|
||||
else
|
||||
name = "Default";
|
||||
|
||||
var passes = new List<EffectPass>();
|
||||
passes.Add(new EffectPass(name, null, this, IntPtr.Zero, 0));
|
||||
var list = new List<EffectTechnique>();
|
||||
technique = new EffectTechnique(name, IntPtr.Zero,
|
||||
new EffectPassCollection(passes), null);
|
||||
list.Add(technique);
|
||||
Techniques = new EffectTechniqueCollection(list);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// CollectUniforms
|
||||
//
|
||||
|
||||
void CollectUniforms()
|
||||
{
|
||||
var list = new List<EffectParameter>();
|
||||
var uniforms = GetProgramUniforms();
|
||||
if (uniforms != null)
|
||||
{
|
||||
for (int i = 0; i < uniforms.Length; i++)
|
||||
{
|
||||
var (name, type, size) = uniforms[i];
|
||||
list.Add(new EffectParameter(name, type, size));
|
||||
}
|
||||
}
|
||||
Parameters = new EffectParameterCollection(list);
|
||||
}
|
||||
|
||||
//
|
||||
// GetProgramUniforms
|
||||
//
|
||||
|
||||
public (string name, int type, int size)[] GetProgramUniforms()
|
||||
{
|
||||
(string, int, int)[] result = null;
|
||||
Renderer.Get(GraphicsDevice.GLDevice).Send( () =>
|
||||
{
|
||||
var count = new int[1];
|
||||
GLES20.glGetProgramiv(programId, GLES20.GL_ACTIVE_UNIFORM_MAX_LENGTH, count, 0);
|
||||
byte[] nameBuf = new byte[count[0] + 1];
|
||||
GLES20.glGetProgramiv(programId, GLES20.GL_ACTIVE_UNIFORMS, count, 0);
|
||||
if (count[0] == 0)
|
||||
return;
|
||||
|
||||
result = new (string name, int type, int size)[count[0]];
|
||||
var type = new int[1];
|
||||
var size = new int[1];
|
||||
for (int i = 0; i < result.Length; i++)
|
||||
{
|
||||
GLES20.glGetActiveUniform(programId, i, nameBuf.Length,
|
||||
/* length, lengthOffset */ count, 0,
|
||||
/* size, sizeOffset */ size, 0,
|
||||
/* type, typeOffset */ type, 0,
|
||||
/* name, nameOffset */ (sbyte[]) (object) nameBuf, 0);
|
||||
var nameStr = System.Text.Encoding.ASCII.GetString(nameBuf, 0, count[0]);
|
||||
result[i] = (nameStr, type[0], size[0]);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// INTERNAL_applyEffect
|
||||
//
|
||||
|
||||
public void INTERNAL_applyEffect(uint pass)
|
||||
{
|
||||
var graphicsDevice = GraphicsDevice;
|
||||
Renderer.Get(graphicsDevice.GLDevice).Send( () =>
|
||||
{
|
||||
GLES20.glUseProgram(programId);
|
||||
int n = Parameters.Count;
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
if (! Parameters[i].Apply(i, graphicsDevice))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"uniform {Parameters[i].Name} (#{i}) in effect {technique.Name}");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Dispose
|
||||
//
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (! base.IsDisposed)
|
||||
{
|
||||
Renderer.Get(GraphicsDevice.GLDevice).Send( () =>
|
||||
{
|
||||
GLES20.glDeleteShader(fragmentId);
|
||||
GLES20.glDeleteShader(vertexId);
|
||||
GLES20.glDeleteProgram(programId);
|
||||
fragmentId = 0;
|
||||
vertexId = 0;
|
||||
programId = 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// data
|
||||
//
|
||||
|
||||
private int programId;
|
||||
private int vertexId, fragmentId;
|
||||
|
||||
public EffectParameterCollection Parameters { get; private set; }
|
||||
public EffectTechniqueCollection Techniques { get; private set; }
|
||||
|
||||
private EffectTechnique technique;
|
||||
public EffectTechnique CurrentTechnique
|
||||
{
|
||||
get => technique;
|
||||
set => throw new System.PlatformNotSupportedException();
|
||||
}
|
||||
|
||||
protected internal virtual void OnApply() { }
|
||||
}
|
||||
|
||||
|
||||
|
||||
public sealed class EffectParameter
|
||||
{
|
||||
public unsafe EffectParameter(string name, int type, int size)
|
||||
{
|
||||
Name = name;
|
||||
this.type = type;
|
||||
|
||||
int floatCount = 0;
|
||||
int intCount = 0;
|
||||
|
||||
if (type == android.opengl.GLES20.GL_FLOAT)
|
||||
{
|
||||
floatCount = 1;
|
||||
if (size == 1 && name == "MultiplierY")
|
||||
kind = 'Y';
|
||||
}
|
||||
|
||||
else if (type == android.opengl.GLES20.GL_FLOAT_VEC2)
|
||||
floatCount = 2;
|
||||
else if (type == android.opengl.GLES20.GL_FLOAT_VEC3)
|
||||
floatCount = 3;
|
||||
else if (type == android.opengl.GLES20.GL_FLOAT_VEC4)
|
||||
floatCount = 4;
|
||||
|
||||
else if (type == android.opengl.GLES20.GL_FLOAT_MAT4)
|
||||
floatCount = 4 * 4;
|
||||
else if (type == android.opengl.GLES20.GL_FLOAT_MAT3)
|
||||
floatCount = 3 * 3;
|
||||
else if (type == android.opengl.GLES20.GL_FLOAT_MAT2)
|
||||
floatCount = 2 * 2;
|
||||
else if ( type == android.opengl.GLES30.GL_FLOAT_MAT3x4
|
||||
|| type == android.opengl.GLES30.GL_FLOAT_MAT4x3)
|
||||
{
|
||||
floatCount = 4 * 3;
|
||||
}
|
||||
|
||||
else if ( type == android.opengl.GLES20.GL_INT
|
||||
|| type == android.opengl.GLES20.GL_BOOL)
|
||||
{
|
||||
intCount = 1;
|
||||
}
|
||||
|
||||
else if ( type == android.opengl.GLES20.GL_SAMPLER_2D
|
||||
|| type == android.opengl.GLES30.GL_SAMPLER_3D
|
||||
|| type == android.opengl.GLES20.GL_SAMPLER_CUBE)
|
||||
{
|
||||
kind = 'S';
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
throw new System.InvalidProgramException(
|
||||
$"Effect: unsupported type {type:X8} in uniform '{name}'");
|
||||
}
|
||||
|
||||
if (floatCount != 0)
|
||||
{
|
||||
var floatArray = new float[floatCount * size];
|
||||
fixed (float* floatPointer = &floatArray[0])
|
||||
values = (IntPtr) (void*) floatPointer;
|
||||
storage = floatArray;
|
||||
storageCopy = java.util.Arrays.copyOf(floatArray, floatArray.Length);
|
||||
}
|
||||
|
||||
else if (intCount != 0)
|
||||
{
|
||||
var intArray = new int[intCount * size];
|
||||
fixed (int* intPointer = &intArray[0])
|
||||
values = (IntPtr) (void*) intPointer;
|
||||
storage = intArray;
|
||||
storageCopy = java.util.Arrays.copyOf(intArray, intArray.Length);
|
||||
}
|
||||
|
||||
//GameRunner.Log($"EFFECT PARAMETER {Name} VALUES {values} STORAGE {this.storage}");
|
||||
}
|
||||
|
||||
//
|
||||
// bool value
|
||||
//
|
||||
|
||||
public bool GetValueBoolean() => GetValueBooleanArray(1)[0];
|
||||
|
||||
public bool[] GetValueBooleanArray(int count)
|
||||
{
|
||||
CheckCount(count);
|
||||
var intArray = IntArray(android.opengl.GLES20.GL_BOOL, count);
|
||||
var value = new bool[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
value[i] = intArray[i] != 0;
|
||||
return value;
|
||||
}
|
||||
|
||||
public void SetValue(bool[] value)
|
||||
{
|
||||
int count = value.Length;
|
||||
var intArray = IntArray(android.opengl.GLES20.GL_BOOL, count);
|
||||
for (int i = 0; i < count; i++)
|
||||
intArray[i] = value[i] ? 1 : 0;
|
||||
}
|
||||
|
||||
public void SetValue(bool value) => SetValue(new bool[] { value });
|
||||
|
||||
//
|
||||
// int value
|
||||
//
|
||||
|
||||
public int GetValueInt32() => GetValueInt32Array(1)[0];
|
||||
|
||||
public int[] GetValueInt32Array(int count)
|
||||
{
|
||||
CheckCount(count);
|
||||
return java.util.Arrays.copyOf(
|
||||
IntArray(android.opengl.GLES20.GL_INT, count), count);
|
||||
}
|
||||
|
||||
public void SetValue(int[] value)
|
||||
{
|
||||
int count = value.Length;
|
||||
var intArray = IntArray(android.opengl.GLES20.GL_INT, count);
|
||||
for (int i = 0; i < count; i++)
|
||||
intArray[i] = value[i];
|
||||
}
|
||||
|
||||
public void SetValue(int value) => SetValue(new int[] { value });
|
||||
|
||||
//
|
||||
// float value
|
||||
//
|
||||
|
||||
public float GetValueSingle() => GetValueSingleArray(1)[0];
|
||||
|
||||
public float[] GetValueSingleArray(int count)
|
||||
{
|
||||
CheckCount(count);
|
||||
return java.util.Arrays.copyOf(
|
||||
FloatArray(android.opengl.GLES20.GL_FLOAT, count), count);
|
||||
}
|
||||
|
||||
public void SetValue(float[] value)
|
||||
{
|
||||
int count = value.Length;
|
||||
var floatArray = FloatArray(android.opengl.GLES20.GL_FLOAT, count);
|
||||
for (int i = 0; i < count; i++)
|
||||
floatArray[i] = value[i];
|
||||
}
|
||||
|
||||
public void SetValue(float value) => SetValue(new float[] { value });
|
||||
|
||||
//
|
||||
// Matrix
|
||||
//
|
||||
|
||||
public Matrix GetValueMatrix() => GetValueMatrixArray(1)[0];
|
||||
|
||||
public Matrix[] GetValueMatrixArray(int count)
|
||||
{
|
||||
CheckCount(count);
|
||||
|
||||
int i = 0, j;
|
||||
float[] floatArray;
|
||||
// allocate an array of values without allocating each element
|
||||
var value = (Matrix[]) java.lang.reflect.Array.newInstance(
|
||||
(java.lang.Class) typeof(Matrix), count);
|
||||
Matrix matrix;
|
||||
|
||||
if (type == android.opengl.GLES20.GL_FLOAT_MAT4)
|
||||
{
|
||||
floatArray = FloatArray(type, 4 * 4 * count);
|
||||
for (j = 0; j < count; j++)
|
||||
{
|
||||
matrix.M11 = floatArray[i++]; // 0
|
||||
matrix.M21 = floatArray[i++];
|
||||
matrix.M31 = floatArray[i++];
|
||||
matrix.M41 = floatArray[i++];
|
||||
matrix.M12 = floatArray[i++]; // 4
|
||||
matrix.M22 = floatArray[i++];
|
||||
matrix.M32 = floatArray[i++];
|
||||
matrix.M42 = floatArray[i++];
|
||||
matrix.M13 = floatArray[i++]; // 8
|
||||
matrix.M23 = floatArray[i++];
|
||||
matrix.M33 = floatArray[i++];
|
||||
matrix.M43 = floatArray[i++];
|
||||
matrix.M14 = floatArray[i++]; // 12
|
||||
matrix.M24 = floatArray[i++];
|
||||
matrix.M34 = floatArray[i++];
|
||||
matrix.M44 = floatArray[i++];
|
||||
java.lang.reflect.Array.set(value, j, matrix); // matrix is cloned
|
||||
}
|
||||
}
|
||||
|
||||
else if (type == android.opengl.GLES20.GL_FLOAT_MAT3)
|
||||
{
|
||||
matrix.M41 = matrix.M42 = matrix.M43 =
|
||||
matrix.M14 = matrix.M24 = matrix.M34 = matrix.M44 = 0;
|
||||
floatArray = FloatArray(type, 3 * 3 * count);
|
||||
for (j = 0; j < count; j++)
|
||||
{
|
||||
matrix.M11 = floatArray[i++]; // 0
|
||||
matrix.M21 = floatArray[i++];
|
||||
matrix.M31 = floatArray[i++];
|
||||
matrix.M12 = floatArray[i++]; // 3
|
||||
matrix.M22 = floatArray[i++];
|
||||
matrix.M32 = floatArray[i++];
|
||||
matrix.M13 = floatArray[i++]; // 6
|
||||
matrix.M23 = floatArray[i++];
|
||||
matrix.M33 = floatArray[i++];
|
||||
java.lang.reflect.Array.set(value, j, matrix); // matrix is cloned
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
throw new InvalidCastException(Name + " MAT TYPE " + type);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public void SetValue(Matrix[] value)
|
||||
{
|
||||
int count = value.Length;
|
||||
|
||||
int i = 0, j;
|
||||
float[] floatArray;
|
||||
Matrix matrix;
|
||||
|
||||
if (type == android.opengl.GLES20.GL_FLOAT_MAT4)
|
||||
{
|
||||
floatArray = FloatArray(type, 4 * 4 * count);
|
||||
for (j = 0; j < count; j++)
|
||||
{
|
||||
matrix = (Matrix) java.lang.reflect.Array.get(value, j);
|
||||
floatArray[i++] = matrix.M11; // 0
|
||||
floatArray[i++] = matrix.M21;
|
||||
floatArray[i++] = matrix.M31;
|
||||
floatArray[i++] = matrix.M41;
|
||||
floatArray[i++] = matrix.M12; // 4
|
||||
floatArray[i++] = matrix.M22;
|
||||
floatArray[i++] = matrix.M32;
|
||||
floatArray[i++] = matrix.M42;
|
||||
floatArray[i++] = matrix.M13; // 8
|
||||
floatArray[i++] = matrix.M23;
|
||||
floatArray[i++] = matrix.M33;
|
||||
floatArray[i++] = matrix.M43;
|
||||
floatArray[i++] = matrix.M14; // 12
|
||||
floatArray[i++] = matrix.M24;
|
||||
floatArray[i++] = matrix.M34;
|
||||
floatArray[i++] = matrix.M44;
|
||||
}
|
||||
}
|
||||
|
||||
else if (type == android.opengl.GLES20.GL_FLOAT_MAT3)
|
||||
{
|
||||
floatArray = FloatArray(type, 3 * 3 * count);
|
||||
for (j = 0; j < count; j++)
|
||||
{
|
||||
matrix = (Matrix) java.lang.reflect.Array.get(value, j);
|
||||
floatArray[i++] = matrix.M11; // 0
|
||||
floatArray[i++] = matrix.M21;
|
||||
floatArray[i++] = matrix.M31;
|
||||
floatArray[i++] = matrix.M12; // 3
|
||||
floatArray[i++] = matrix.M22;
|
||||
floatArray[i++] = matrix.M32;
|
||||
floatArray[i++] = matrix.M13; // 6
|
||||
floatArray[i++] = matrix.M23;
|
||||
floatArray[i++] = matrix.M33;
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
throw new InvalidCastException(Name + " MAT TYPE " + type);
|
||||
}
|
||||
|
||||
public void SetValue(Matrix value)
|
||||
{
|
||||
// allocate an array of values without allocating each element
|
||||
var array = (Matrix[]) java.lang.reflect.Array.newInstance(
|
||||
(java.lang.Class) typeof(Matrix), 1);
|
||||
java.lang.reflect.Array.set(array, 0, value);
|
||||
SetValue(array);
|
||||
}
|
||||
|
||||
//
|
||||
// Vector3
|
||||
//
|
||||
|
||||
public Vector3 GetValueVector3() => GetValueVector3Array(1)[0];
|
||||
|
||||
public Vector3[] GetValueVector3Array(int count)
|
||||
{
|
||||
CheckCount(count);
|
||||
var floatArray = FloatArray(android.opengl.GLES20.GL_FLOAT_VEC3, 3 * count);
|
||||
|
||||
// allocate an array of values without allocating each element
|
||||
var value = (Vector3[]) java.lang.reflect.Array.newInstance(
|
||||
(java.lang.Class) typeof(Vector3), count);
|
||||
Vector3 vector;
|
||||
|
||||
int i = 0;
|
||||
for (int j = 0; j < count; j++)
|
||||
{
|
||||
vector.X = floatArray[i++];
|
||||
vector.Y = floatArray[i++];
|
||||
vector.Z = floatArray[i++];
|
||||
java.lang.reflect.Array.set(value, j, vector); // vector is cloned
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public void SetValue(Vector3[] value)
|
||||
{
|
||||
int count = value.Length;
|
||||
var floatArray = FloatArray(android.opengl.GLES20.GL_FLOAT_VEC3, 3 * count);
|
||||
Vector3 vector;
|
||||
int i = 0;
|
||||
for (int j = 0; j < count; j++)
|
||||
{
|
||||
vector = (Vector3) java.lang.reflect.Array.get(value, j);
|
||||
floatArray[i++] = vector.X;
|
||||
floatArray[i++] = vector.Y;
|
||||
floatArray[i++] = vector.Z;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetValue(Vector3 value)
|
||||
{
|
||||
// allocate an array of values without allocating each element
|
||||
var array = (Vector3[]) java.lang.reflect.Array.newInstance(
|
||||
(java.lang.Class) typeof(Vector3), 1);
|
||||
java.lang.reflect.Array.set(array, 0, value);
|
||||
SetValue(array);
|
||||
}
|
||||
|
||||
//
|
||||
// Vector4
|
||||
//
|
||||
|
||||
public Vector4 GetValueVector4() => GetValueVector4Array(1)[0];
|
||||
|
||||
public Vector4[] GetValueVector4Array(int count)
|
||||
{
|
||||
CheckCount(count);
|
||||
var floatArray = FloatArray(android.opengl.GLES20.GL_FLOAT_VEC4, 4 * count);
|
||||
|
||||
// allocate an array of values without allocating each element
|
||||
var value = (Vector4[]) java.lang.reflect.Array.newInstance(
|
||||
(java.lang.Class) typeof(Vector4), count);
|
||||
Vector4 vector;
|
||||
|
||||
int i = 0;
|
||||
for (int j = 0; j < count; j++)
|
||||
{
|
||||
vector.X = floatArray[i++];
|
||||
vector.Y = floatArray[i++];
|
||||
vector.Z = floatArray[i++];
|
||||
vector.W = floatArray[i++];
|
||||
java.lang.reflect.Array.set(value, j, vector); // vector is cloned
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public void SetValue(Vector4[] value)
|
||||
{
|
||||
int count = value.Length;
|
||||
var floatArray = FloatArray(android.opengl.GLES20.GL_FLOAT_VEC4, 4 * count);
|
||||
Vector4 vector;
|
||||
int i = 0;
|
||||
for (int j = 0; j < count; j++)
|
||||
{
|
||||
vector = (Vector4) java.lang.reflect.Array.get(value, j);
|
||||
floatArray[i++] = vector.X;
|
||||
floatArray[i++] = vector.Y;
|
||||
floatArray[i++] = vector.Z;
|
||||
floatArray[i++] = vector.W;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetValue(Vector4 value)
|
||||
{
|
||||
// allocate an array of values without allocating each element
|
||||
var array = (Vector4[]) java.lang.reflect.Array.newInstance(
|
||||
(java.lang.Class) typeof(Vector4), 1);
|
||||
java.lang.reflect.Array.set(array, 0, value);
|
||||
SetValue(array);
|
||||
}
|
||||
|
||||
//
|
||||
// texture
|
||||
//
|
||||
|
||||
public Texture2D GetValueTexture2D() => (Texture2D) storage;
|
||||
|
||||
public Texture3D GetValueTexture3D() => (Texture3D) storage;
|
||||
|
||||
public TextureCube GetValueTextureCube() => (TextureCube) storage;
|
||||
|
||||
public void SetValue(Texture value)
|
||||
{
|
||||
if ( type == android.opengl.GLES20.GL_SAMPLER_2D
|
||||
|| type == android.opengl.GLES30.GL_SAMPLER_3D
|
||||
|| type == android.opengl.GLES20.GL_SAMPLER_CUBE)
|
||||
{
|
||||
storage = value;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// string
|
||||
//
|
||||
|
||||
public void SetValue(string value) => throw new NotImplementedException(Name);
|
||||
|
||||
//
|
||||
// Array access
|
||||
//
|
||||
|
||||
int[] IntArray(int checkType, int checkCount)
|
||||
{
|
||||
if (type != checkType)
|
||||
throw new InvalidCastException();
|
||||
var intArray = (int[]) storage;
|
||||
if (intArray.Length < checkCount)
|
||||
throw new InvalidCastException();
|
||||
return intArray;
|
||||
}
|
||||
|
||||
float[] FloatArray(int checkType, int checkCount)
|
||||
{
|
||||
if (type != checkType)
|
||||
throw new InvalidCastException(Name);
|
||||
var floatArray = (float[]) storage;
|
||||
if (floatArray.Length < checkCount)
|
||||
throw new InvalidCastException(Name);
|
||||
return floatArray;
|
||||
}
|
||||
|
||||
void CheckCount(int count)
|
||||
{
|
||||
if (count <= 0)
|
||||
throw new ArgumentOutOfRangeException(Name);
|
||||
}
|
||||
|
||||
//
|
||||
// Apply
|
||||
//
|
||||
|
||||
public bool Apply(int uni, GraphicsDevice graphicsDevice)
|
||||
{
|
||||
if (storage is float[] floatArray)
|
||||
{
|
||||
if (kind == 'Y')
|
||||
return ApplyMultiplierY(floatArray, uni, graphicsDevice);
|
||||
else
|
||||
return Apply(uni, floatArray);
|
||||
}
|
||||
|
||||
if (storage is int[] intArray)
|
||||
return Apply(uni, intArray);
|
||||
|
||||
if (kind == 'S')
|
||||
{
|
||||
if (storage != null)
|
||||
{
|
||||
// note that the uniform sampler (i.e. the texture
|
||||
// unit selector) always remains set to default value
|
||||
graphicsDevice.Textures[0] = (Texture) storage;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool Apply(int uni, float[] floatArray)
|
||||
{
|
||||
var floatArrayCopy = (float[]) storageCopy;
|
||||
if (! java.util.Arrays.@equals(floatArray, floatArrayCopy))
|
||||
{
|
||||
int num = floatArray.Length;
|
||||
for (int i = 0; i < num; i++)
|
||||
floatArrayCopy[i] = floatArray[i];
|
||||
|
||||
if (type == GLES20.GL_FLOAT)
|
||||
GLES20.glUniform1fv(uni, num, floatArray, 0);
|
||||
else if (type == GLES20.GL_FLOAT_VEC2)
|
||||
GLES20.glUniform2fv(uni, num / 2, floatArray, 0);
|
||||
else if (type == GLES20.GL_FLOAT_VEC3)
|
||||
GLES20.glUniform3fv(uni, num / 3, floatArray, 0);
|
||||
else if (type == GLES20.GL_FLOAT_VEC4)
|
||||
GLES20.glUniform4fv(uni, num / 4, floatArray, 0);
|
||||
else if (type == GLES20.GL_FLOAT_MAT4)
|
||||
GLES20.glUniformMatrix4fv(uni, num / 16, true, floatArray, 0);
|
||||
else if (type == GLES20.GL_FLOAT_MAT3)
|
||||
GLES20.glUniformMatrix3fv(uni, num / 9, true, floatArray, 0);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool Apply(int uni, int[] intArray)
|
||||
{
|
||||
var intArrayCopy = (int[]) storageCopy;
|
||||
if (! java.util.Arrays.@equals(intArray, intArrayCopy))
|
||||
{
|
||||
int num = intArray.Length;
|
||||
for (int i = 0; i < num; i++)
|
||||
intArrayCopy[i] = intArray[i];
|
||||
|
||||
if ( type == android.opengl.GLES20.GL_INT
|
||||
|| type == android.opengl.GLES20.GL_BOOL
|
||||
|| type == android.opengl.GLES20.GL_SAMPLER_2D)
|
||||
{
|
||||
GLES20.glUniform1iv(uni, num, intArray, 0);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool ApplyMultiplierY(float[] floatArray, int uni, GraphicsDevice graphicsDevice)
|
||||
{
|
||||
// when rendering to texture, we have to flip vertically
|
||||
var floatValue = FNA3D.IsRenderToTexture(graphicsDevice) ? -1f : 1f;
|
||||
if (floatValue != floatArray[0])
|
||||
{
|
||||
GLES20.glUniform1f(uni, floatValue);
|
||||
floatArray[0] = floatValue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// data
|
||||
//
|
||||
|
||||
object storage;
|
||||
object storageCopy;
|
||||
int type;
|
||||
char kind;
|
||||
|
||||
public IntPtr values;
|
||||
public string Name { get; private set; }
|
||||
}
|
||||
|
||||
}
|
27
BNA/src/Empty.cs
Normal file
27
BNA/src/Empty.cs
Normal file
@ -0,0 +1,27 @@
|
||||
|
||||
namespace Microsoft.Xna.Framework
|
||||
{
|
||||
|
||||
public class LaunchParameters
|
||||
{
|
||||
}
|
||||
|
||||
// this forwards audio events
|
||||
|
||||
public static class FrameworkDispatcher
|
||||
{
|
||||
|
||||
public static void Update()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class FNAInternalExtensions
|
||||
{
|
||||
public static bool TryGetBuffer(System.IO.MemoryStream stream, ref byte[] buffer)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
577
BNA/src/FNA3D.cs
Normal file
577
BNA/src/FNA3D.cs
Normal file
@ -0,0 +1,577 @@
|
||||
|
||||
using System;
|
||||
using android.opengl;
|
||||
#pragma warning disable 0436
|
||||
|
||||
namespace Microsoft.Xna.Framework.Graphics
|
||||
{
|
||||
|
||||
public static partial class FNA3D
|
||||
{
|
||||
|
||||
//
|
||||
// FNA3D_SetViewport
|
||||
//
|
||||
|
||||
public static void FNA3D_SetViewport(IntPtr device, ref FNA3D_Viewport viewport)
|
||||
{
|
||||
var renderer = Renderer.Get(device);
|
||||
var state = (State) renderer.UserData;
|
||||
var v = viewport;
|
||||
|
||||
if ( state.AdjustViewport && (! state.RenderToTexture)
|
||||
&& v.x == 0 && v.y == 0 && v.w > 0 && v.h > 0
|
||||
&& v.w == state.BackBufferWidth && v.h == state.BackBufferHeight)
|
||||
{
|
||||
var (s_w, s_h) = (renderer.SurfaceWidth, renderer.SurfaceHeight);
|
||||
if (v.w >= v.h)
|
||||
{
|
||||
// adjust from virtual landscape
|
||||
v.h = (int) ((v.h * s_w) / (float) v.w);
|
||||
v.w = s_w;
|
||||
v.y = (s_h - v.h) / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
// adjust from virtual portrait
|
||||
v.w = (int) ((v.w * s_w) / (float) v.h);
|
||||
v.h = s_h;
|
||||
v.x = (s_w - v.w) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
Renderer.Get(device).Send( () =>
|
||||
{
|
||||
GLES20.glViewport(v.x, v.y, v.w, v.h);
|
||||
GLES20.glDepthRangef(v.minDepth, v.maxDepth);
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// FNA3D_SetScissorRect
|
||||
//
|
||||
|
||||
public static void FNA3D_SetScissorRect(IntPtr device, ref Rectangle scissor)
|
||||
{
|
||||
var s = scissor;
|
||||
Renderer.Get(device).Send( () =>
|
||||
{
|
||||
GLES20.glScissor(s.X, s.Y, s.Width, s.Height);
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// FNA3D_Clear
|
||||
//
|
||||
|
||||
public static void FNA3D_Clear(IntPtr device, ClearOptions options, ref Vector4 color,
|
||||
float depth, int stencil)
|
||||
{
|
||||
var clearColor = color;
|
||||
var renderer = Renderer.Get(device);
|
||||
renderer.Send( () =>
|
||||
{
|
||||
var state = (State) renderer.UserData;
|
||||
var WriteMask = state.WriteMask;
|
||||
|
||||
if (state.ScissorTest)
|
||||
{
|
||||
// disable scissor before clear
|
||||
GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
|
||||
}
|
||||
|
||||
bool restoreColorMask = false;
|
||||
bool restoreDepthMask = false;
|
||||
bool restoreStencilMask = false;
|
||||
|
||||
int mask = 0;
|
||||
if ((options & ClearOptions.Target) != 0)
|
||||
{
|
||||
mask |= GLES20.GL_COLOR_BUFFER_BIT;
|
||||
|
||||
if (clearColor != state.ClearColor)
|
||||
{
|
||||
state.ClearColor = clearColor;
|
||||
GLES20.glClearColor(
|
||||
clearColor.X, clearColor.Y, clearColor.Z, clearColor.W);
|
||||
}
|
||||
|
||||
if ((WriteMask & (RED_MASK | GREEN_MASK | BLUE_MASK | ALPHA_MASK))
|
||||
!= (RED_MASK | GREEN_MASK | BLUE_MASK | ALPHA_MASK))
|
||||
{
|
||||
// reset color masks before clear
|
||||
GLES20.glColorMask(true, true, true, true);
|
||||
restoreColorMask = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((options & ClearOptions.DepthBuffer) != 0)
|
||||
{
|
||||
mask |= GLES20.GL_DEPTH_BUFFER_BIT;
|
||||
|
||||
if (depth != state.ClearDepth)
|
||||
{
|
||||
state.ClearDepth = depth;
|
||||
GLES20.glClearDepthf(depth);
|
||||
}
|
||||
|
||||
if ((state.WriteMask & DEPTH_MASK) == 0)
|
||||
{
|
||||
// reset depth mask before clear
|
||||
GLES20.glDepthMask(true);
|
||||
restoreDepthMask = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((options & ClearOptions.Stencil) != 0)
|
||||
{
|
||||
mask |= GLES20.GL_STENCIL_BUFFER_BIT;
|
||||
|
||||
if (stencil != state.ClearStencil)
|
||||
{
|
||||
state.ClearStencil = stencil;
|
||||
GLES20.glClearStencil(stencil);
|
||||
}
|
||||
|
||||
if ((WriteMask & STENCIL_MASK) == 0)
|
||||
{
|
||||
// reset stencil mask before clear
|
||||
GLES20.glStencilMask(-1);
|
||||
restoreStencilMask = true;
|
||||
}
|
||||
}
|
||||
|
||||
GLES20.glClear(mask);
|
||||
|
||||
if (restoreStencilMask)
|
||||
GLES20.glStencilMask(WriteMask & STENCIL_MASK);
|
||||
|
||||
if (restoreDepthMask)
|
||||
GLES20.glDepthMask(false);
|
||||
|
||||
if (restoreColorMask)
|
||||
{
|
||||
GLES20.glColorMask((WriteMask & RED_MASK) != 0 ? true : false,
|
||||
(WriteMask & GREEN_MASK) != 0 ? true : false,
|
||||
(WriteMask & BLUE_MASK) != 0 ? true : false,
|
||||
(WriteMask & ALPHA_MASK) != 0 ? true : false);
|
||||
}
|
||||
|
||||
if (state.ScissorTest)
|
||||
{
|
||||
// restore scissor after clear
|
||||
GLES20.glEnable(GLES20.GL_SCISSOR_TEST);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// FNA3D_SwapBuffers
|
||||
//
|
||||
|
||||
public static void FNA3D_SwapBuffers(IntPtr device,
|
||||
IntPtr sourceRectangle,
|
||||
IntPtr destinationRectangle,
|
||||
IntPtr overrideWindowHandle)
|
||||
{
|
||||
if ( (long) sourceRectangle != 0
|
||||
|| (long) destinationRectangle != 0
|
||||
|| (long) overrideWindowHandle != 0)
|
||||
{
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
var renderer = Renderer.Get(device);
|
||||
renderer.Present();
|
||||
}
|
||||
|
||||
//
|
||||
// FNA3D_SetBlendState
|
||||
//
|
||||
|
||||
public static void FNA3D_SetBlendState(IntPtr device, ref FNA3D_BlendState blendState)
|
||||
{
|
||||
var input = blendState;
|
||||
var renderer = Renderer.Get(device);
|
||||
Renderer.Get(device).Send( () =>
|
||||
{
|
||||
var state = (State) renderer.UserData;
|
||||
|
||||
if ( input.colorSourceBlend != Blend.One
|
||||
|| input.colorDestinationBlend != Blend.Zero
|
||||
|| input.alphaSourceBlend != Blend.One
|
||||
|| input.alphaDestinationBlend != Blend.Zero)
|
||||
{
|
||||
//
|
||||
// blend state
|
||||
//
|
||||
|
||||
if (! state.BlendEnable)
|
||||
{
|
||||
state.BlendEnable = true;
|
||||
GLES20.glEnable(GLES20.GL_BLEND);
|
||||
}
|
||||
|
||||
//
|
||||
// XNA blend factor / GL blend color
|
||||
//
|
||||
|
||||
if (input.blendFactor != state.BlendColor)
|
||||
{
|
||||
state.BlendColor = input.blendFactor;
|
||||
|
||||
GLES20.glBlendColor(state.BlendColor.R / 255f, state.BlendColor.G / 255f,
|
||||
state.BlendColor.B / 255f, state.BlendColor.A / 255f);
|
||||
}
|
||||
|
||||
//
|
||||
// XNA blend mode / GL blend function
|
||||
//
|
||||
|
||||
if ( input.colorSourceBlend != state.BlendSrcColor
|
||||
|| input.colorDestinationBlend != state.BlendDstColor
|
||||
|| input.alphaSourceBlend != state.BlendSrcAlpha
|
||||
|| input.alphaDestinationBlend != state.BlendDstAlpha)
|
||||
{
|
||||
state.BlendSrcColor = input.colorSourceBlend;
|
||||
state.BlendDstColor = input.colorDestinationBlend;
|
||||
state.BlendSrcAlpha = input.alphaSourceBlend;
|
||||
state.BlendDstAlpha = input.alphaDestinationBlend;
|
||||
|
||||
GLES20.glBlendFuncSeparate(
|
||||
BlendModeToBlendFunc[(int) state.BlendSrcColor],
|
||||
BlendModeToBlendFunc[(int) state.BlendDstColor],
|
||||
BlendModeToBlendFunc[(int) state.BlendSrcAlpha],
|
||||
BlendModeToBlendFunc[(int) state.BlendDstAlpha]);
|
||||
}
|
||||
|
||||
//
|
||||
// XNA blend function / GL blend equation
|
||||
//
|
||||
|
||||
if ( input.colorBlendFunction != state.BlendFuncColor
|
||||
|| input.alphaBlendFunction != state.BlendFuncAlpha)
|
||||
{
|
||||
state.BlendFuncColor = input.colorBlendFunction;
|
||||
state.BlendFuncAlpha = input.alphaBlendFunction;
|
||||
|
||||
GLES20.glBlendEquationSeparate(
|
||||
BlendFunctionToBlendEquation[(int) state.BlendFuncColor],
|
||||
BlendFunctionToBlendEquation[(int) state.BlendFuncAlpha]);
|
||||
}
|
||||
|
||||
//
|
||||
// color write mask
|
||||
//
|
||||
|
||||
bool inputRed = ((input.colorWriteEnable & ColorWriteChannels.Red) != 0);
|
||||
bool inputGreen = ((input.colorWriteEnable & ColorWriteChannels.Green) != 0);
|
||||
bool inputBlue = ((input.colorWriteEnable & ColorWriteChannels.Blue) != 0);
|
||||
bool inputAlpha = ((input.colorWriteEnable & ColorWriteChannels.Alpha) != 0);
|
||||
var WriteMask = state.WriteMask;
|
||||
|
||||
if ( inputRed != ((WriteMask & RED_MASK) != 0)
|
||||
|| inputGreen != ((WriteMask & GREEN_MASK) != 0)
|
||||
|| inputBlue != ((WriteMask & BLUE_MASK) != 0)
|
||||
|| inputAlpha != ((WriteMask & ALPHA_MASK) != 0))
|
||||
{
|
||||
state.WriteMask = (inputRed ? RED_MASK : 0)
|
||||
| (inputGreen ? GREEN_MASK : 0)
|
||||
| (inputBlue ? BLUE_MASK : 0)
|
||||
| (inputAlpha ? ALPHA_MASK : 0);
|
||||
|
||||
GLES20.glColorMask(inputRed, inputGreen, inputBlue, inputAlpha);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
state.BlendEnable = false;
|
||||
GLES20.glDisable(GLES20.GL_BLEND);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static int[] BlendModeToBlendFunc = new int[]
|
||||
{
|
||||
GLES20.GL_ONE, // Blend.One
|
||||
GLES20.GL_ZERO, // Blend.Zero
|
||||
GLES20.GL_SRC_COLOR, // Blend.SourceColor
|
||||
GLES20.GL_ONE_MINUS_SRC_COLOR, // Blend.InverseSourceColor
|
||||
GLES20.GL_SRC_ALPHA, // Blend.SourceAlpha
|
||||
GLES20.GL_ONE_MINUS_SRC_ALPHA, // Blend.InverseSourceAlpha
|
||||
GLES20.GL_DST_COLOR, // Blend.DestinationColor
|
||||
GLES20.GL_ONE_MINUS_DST_COLOR, // Blend.InverseDestinationColor
|
||||
GLES20.GL_DST_ALPHA, // Blend.DestinationAlpha
|
||||
GLES20.GL_ONE_MINUS_DST_ALPHA, // Blend.InverseDestinationAlpha
|
||||
GLES20.GL_CONSTANT_COLOR, // Blend.BlendFactor
|
||||
GLES20.GL_ONE_MINUS_CONSTANT_COLOR, // Blend.InverseBlendFactor
|
||||
GLES20.GL_SRC_ALPHA_SATURATE // Blend.SourceAlphaSaturation
|
||||
};
|
||||
|
||||
static int[] BlendFunctionToBlendEquation = new int[]
|
||||
{
|
||||
GLES20.GL_FUNC_ADD, // BlendFunction.Add
|
||||
GLES20.GL_FUNC_SUBTRACT, // BlendFunction.Subtract
|
||||
GLES20.GL_FUNC_REVERSE_SUBTRACT, // BlendFunction.ReverseSubtract
|
||||
GLES30.GL_MAX, // BlendFunction.Max
|
||||
GLES30.GL_MIN // BlendFunction.Min
|
||||
};
|
||||
|
||||
//
|
||||
// FNA3D_SetDepthStencilState
|
||||
//
|
||||
|
||||
public static void FNA3D_SetDepthStencilState(IntPtr device,
|
||||
ref FNA3D_DepthStencilState depthStencilState)
|
||||
{
|
||||
// AndroidActivity.LogI(">>>>> SET DEPTH AND STENCIL STATE");
|
||||
}
|
||||
|
||||
//
|
||||
// FNA3D_ApplyRasterizerState
|
||||
//
|
||||
|
||||
public static void FNA3D_ApplyRasterizerState(IntPtr device,
|
||||
ref FNA3D_RasterizerState rasterizerState)
|
||||
{
|
||||
var input = rasterizerState;
|
||||
var renderer = Renderer.Get(device);
|
||||
Renderer.Get(device).Send( () =>
|
||||
{
|
||||
var state = (State) renderer.UserData;
|
||||
|
||||
var inputScissorTest = (input.scissorTestEnable != 0);
|
||||
if (inputScissorTest != state.ScissorTest)
|
||||
{
|
||||
state.ScissorTest = inputScissorTest;
|
||||
if (inputScissorTest)
|
||||
GLES20.glEnable(GLES20.GL_SCISSOR_TEST);
|
||||
else
|
||||
GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
|
||||
}
|
||||
|
||||
int inputCullMode;
|
||||
if (state.RenderToTexture)
|
||||
{
|
||||
// select culling mode when rendering to texture, where we
|
||||
// flip vertically; see also EffectParameter::ApplyMultiplierY
|
||||
inputCullMode =
|
||||
(input.cullMode == CullMode.CullCounterClockwiseFace) ? GLES20.GL_BACK
|
||||
: (input.cullMode == CullMode.CullClockwiseFace) ? GLES20.GL_FRONT : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// select culling mode when rendering directly to the screen
|
||||
inputCullMode =
|
||||
(input.cullMode == CullMode.CullCounterClockwiseFace) ? GLES20.GL_FRONT
|
||||
: (input.cullMode == CullMode.CullClockwiseFace) ? GLES20.GL_BACK : 0;
|
||||
}
|
||||
if (inputCullMode != state.CullMode)
|
||||
{
|
||||
state.CullMode = inputCullMode;
|
||||
if (inputCullMode == 0)
|
||||
GLES20.glDisable(GLES20.GL_CULL_FACE);
|
||||
else
|
||||
{
|
||||
GLES20.glEnable(GLES20.GL_CULL_FACE);
|
||||
GLES20.glCullFace(inputCullMode);
|
||||
}
|
||||
}
|
||||
|
||||
// fillMode (glPolygonMode) is not supported on GL ES,
|
||||
// so we also ignore depthBias (glPolygonOffset)
|
||||
});
|
||||
}
|
||||
|
||||
public static int FNA3D_GetMaxMultiSampleCount(IntPtr device, SurfaceFormat format,
|
||||
int preferredMultiSampleCount)
|
||||
{
|
||||
return 0;
|
||||
|
||||
#if false
|
||||
for (int i = 0; i < 21; i++)
|
||||
GLES30.glGetInternalformativ(GLES20.GL_RENDERBUFFER,
|
||||
SurfaceFormatToTextureInternalFormat[i],
|
||||
GLES20.GL_SAMPLES, 1, count, 0);
|
||||
if (GLES20.glGetError() == 0 && count[0] < preferredMultiSampleCount)
|
||||
preferredMultiSampleCount = count[0];
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
// FNA3D_DrawIndexedPrimitives
|
||||
//
|
||||
|
||||
public static void FNA3D_DrawIndexedPrimitives(IntPtr device, PrimitiveType primitiveType,
|
||||
int baseVertex, int minVertexIndex,
|
||||
int numVertices, int startIndex,
|
||||
int primitiveCount, IntPtr indices,
|
||||
IndexElementSize indexElementSize)
|
||||
{
|
||||
int elementSize, elementType;
|
||||
if (indexElementSize == IndexElementSize.SixteenBits)
|
||||
{
|
||||
elementSize = 2;
|
||||
elementType = GLES20.GL_UNSIGNED_SHORT;
|
||||
}
|
||||
else if (indexElementSize == IndexElementSize.ThirtyTwoBits)
|
||||
{
|
||||
elementSize = 4;
|
||||
elementType = GLES20.GL_UNSIGNED_INT;
|
||||
}
|
||||
else
|
||||
throw new ArgumentException("invalid IndexElementSize");
|
||||
|
||||
int drawMode = PrimitiveTypeToDrawMode[(int) primitiveType];
|
||||
int maxVertexIndex = minVertexIndex + numVertices - 1;
|
||||
int indexOffset = startIndex * elementSize;
|
||||
primitiveCount = PrimitiveCount(primitiveType, primitiveCount);
|
||||
|
||||
Renderer.Get(device).Send( () =>
|
||||
{
|
||||
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, (int) indices);
|
||||
GLES30.glDrawRangeElements(drawMode, minVertexIndex, maxVertexIndex,
|
||||
primitiveCount, elementType, indexOffset);
|
||||
});
|
||||
}
|
||||
|
||||
static int[] PrimitiveTypeToDrawMode = new int[]
|
||||
{
|
||||
GLES20.GL_TRIANGLES, // PrimitiveType.TriangleList
|
||||
GLES20.GL_TRIANGLE_STRIP, // PrimitiveType.TriangleStrip
|
||||
GLES20.GL_LINES, // PrimitiveType.LineList
|
||||
GLES20.GL_LINE_STRIP, // PrimitiveType.LineStrip
|
||||
};
|
||||
|
||||
static int PrimitiveCount(PrimitiveType primitiveType, int primitiveCount)
|
||||
{
|
||||
return primitiveType switch
|
||||
{
|
||||
PrimitiveType.TriangleList => primitiveCount * 3,
|
||||
PrimitiveType.TriangleStrip => primitiveCount + 2,
|
||||
PrimitiveType.LineList => primitiveCount * 2,
|
||||
PrimitiveType.LineStrip => primitiveCount + 1,
|
||||
_ => throw new ArgumentException("invalid PrimitiveType"),
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// FNA3D_DrawPrimitives
|
||||
//
|
||||
|
||||
public static void FNA3D_DrawPrimitives(IntPtr device, PrimitiveType primitiveType,
|
||||
int vertexStart, int primitiveCount)
|
||||
{
|
||||
int drawMode = PrimitiveTypeToDrawMode[(int) primitiveType];
|
||||
primitiveCount = PrimitiveCount(primitiveType, primitiveCount);
|
||||
|
||||
Renderer.Get(device).Send( () =>
|
||||
{
|
||||
GLES20.glDrawArrays(drawMode, vertexStart, primitiveCount);
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// FNA3D_BlendState
|
||||
//
|
||||
|
||||
public struct FNA3D_BlendState
|
||||
{
|
||||
public Blend colorSourceBlend;
|
||||
public Blend colorDestinationBlend;
|
||||
public BlendFunction colorBlendFunction;
|
||||
public Blend alphaSourceBlend;
|
||||
public Blend alphaDestinationBlend;
|
||||
public BlendFunction alphaBlendFunction;
|
||||
public ColorWriteChannels colorWriteEnable;
|
||||
public ColorWriteChannels colorWriteEnable1;
|
||||
public ColorWriteChannels colorWriteEnable2;
|
||||
public ColorWriteChannels colorWriteEnable3;
|
||||
public Color blendFactor;
|
||||
public int multiSampleMask;
|
||||
}
|
||||
|
||||
//
|
||||
// FNA3D_DepthStencilState
|
||||
//
|
||||
|
||||
public struct FNA3D_DepthStencilState
|
||||
{
|
||||
public byte depthBufferEnable;
|
||||
public byte depthBufferWriteEnable;
|
||||
public CompareFunction depthBufferFunction;
|
||||
public byte stencilEnable;
|
||||
public int stencilMask;
|
||||
public int stencilWriteMask;
|
||||
public byte twoSidedStencilMode;
|
||||
public StencilOperation stencilFail;
|
||||
public StencilOperation stencilDepthBufferFail;
|
||||
public StencilOperation stencilPass;
|
||||
public CompareFunction stencilFunction;
|
||||
public StencilOperation ccwStencilFail;
|
||||
public StencilOperation ccwStencilDepthBufferFail;
|
||||
public StencilOperation ccwStencilPass;
|
||||
public CompareFunction ccwStencilFunction;
|
||||
public int referenceStencil;
|
||||
}
|
||||
|
||||
//
|
||||
// FNA3D_RasterizerState
|
||||
//
|
||||
|
||||
public struct FNA3D_RasterizerState
|
||||
{
|
||||
public FillMode fillMode;
|
||||
public CullMode cullMode;
|
||||
public float depthBias;
|
||||
public float slopeScaleDepthBias;
|
||||
public byte scissorTestEnable;
|
||||
public byte multiSampleAntiAlias;
|
||||
}
|
||||
|
||||
//
|
||||
// FNA3D_Viewport
|
||||
//
|
||||
|
||||
public struct FNA3D_Viewport
|
||||
{
|
||||
public int x;
|
||||
public int y;
|
||||
public int w;
|
||||
public int h;
|
||||
public float minDepth;
|
||||
public float maxDepth;
|
||||
}
|
||||
|
||||
//
|
||||
// State
|
||||
//
|
||||
|
||||
private partial class State
|
||||
{
|
||||
public Vector4 ClearColor;
|
||||
public float ClearDepth;
|
||||
public int ClearStencil;
|
||||
public int WriteMask = -1;
|
||||
public int CullMode;
|
||||
public bool ScissorTest;
|
||||
|
||||
public bool BlendEnable;
|
||||
public Color BlendColor;
|
||||
|
||||
public Blend BlendSrcColor;
|
||||
public Blend BlendDstColor = Blend.Zero;
|
||||
public Blend BlendSrcAlpha;
|
||||
public Blend BlendDstAlpha = Blend.Zero;
|
||||
public BlendFunction BlendFuncColor;
|
||||
public BlendFunction BlendFuncAlpha;
|
||||
}
|
||||
|
||||
private const int DEPTH_MASK = 0x40000000;
|
||||
private const int ALPHA_MASK = 0x20000000;
|
||||
private const int BLUE_MASK = 0x04000000;
|
||||
private const int GREEN_MASK = 0x02000000;
|
||||
private const int RED_MASK = 0x01000000;
|
||||
private const int STENCIL_MASK = 0x00FFFFFF;
|
||||
}
|
||||
|
||||
}
|
498
BNA/src/FNA3D_Buf.cs
Normal file
498
BNA/src/FNA3D_Buf.cs
Normal file
@ -0,0 +1,498 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using android.opengl;
|
||||
#pragma warning disable 0436
|
||||
|
||||
namespace Microsoft.Xna.Framework.Graphics
|
||||
{
|
||||
|
||||
public static partial class FNA3D
|
||||
{
|
||||
|
||||
//
|
||||
// FNA3D_SupportsNoOverwrite
|
||||
//
|
||||
|
||||
public static byte FNA3D_SupportsNoOverwrite(IntPtr device)
|
||||
{
|
||||
// prevent flag SetDataOptions.NoOverwrite in Set*BufferData calls
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Create Buffers
|
||||
//
|
||||
|
||||
private static int CreateBuffer(Renderer renderer, int target, byte dynamic, int size)
|
||||
{
|
||||
int bufferId = 0;
|
||||
renderer.Send( () =>
|
||||
{
|
||||
int[] id = new int[1];
|
||||
GLES20.glGenBuffers(1, id, 0);
|
||||
if (id[0] != 0)
|
||||
{
|
||||
bufferId = id[0];
|
||||
int usage = (dynamic != 0 ? GLES20.GL_STREAM_DRAW
|
||||
: GLES20.GL_STATIC_DRAW);
|
||||
GLES20.glBindBuffer(target, bufferId);
|
||||
GLES20.glBufferData(target, size, null, usage);
|
||||
|
||||
var state = (State) renderer.UserData;
|
||||
state.BufferSizeUsage[bufferId] = new int[] { size, usage };
|
||||
}
|
||||
});
|
||||
return bufferId;
|
||||
}
|
||||
|
||||
public static IntPtr FNA3D_GenVertexBuffer(IntPtr device, byte dynamic,
|
||||
BufferUsage usage, int sizeInBytes)
|
||||
{
|
||||
return (IntPtr) CreateBuffer(Renderer.Get(device),
|
||||
GLES20.GL_ARRAY_BUFFER,
|
||||
dynamic, sizeInBytes);
|
||||
}
|
||||
|
||||
public static IntPtr FNA3D_GenIndexBuffer(IntPtr device, byte dynamic,
|
||||
BufferUsage usage, int sizeInBytes)
|
||||
{
|
||||
return (IntPtr) CreateBuffer(Renderer.Get(device),
|
||||
GLES20.GL_ELEMENT_ARRAY_BUFFER,
|
||||
dynamic, sizeInBytes);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Delete Buffers
|
||||
//
|
||||
|
||||
public static void FNA3D_AddDisposeVertexBuffer(IntPtr device, IntPtr buffer)
|
||||
{
|
||||
var renderer = Renderer.Get(device);
|
||||
renderer.Send( () =>
|
||||
{
|
||||
GLES20.glDeleteBuffers(1, new int[] { (int) buffer }, 0);
|
||||
|
||||
var state = (State) renderer.UserData;
|
||||
state.BufferSizeUsage.Remove((int) buffer);
|
||||
});
|
||||
}
|
||||
|
||||
public static void FNA3D_AddDisposeIndexBuffer(IntPtr device, IntPtr buffer)
|
||||
{
|
||||
FNA3D_AddDisposeVertexBuffer(device, buffer);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Set Buffer Data
|
||||
//
|
||||
|
||||
private static void SetBufferData(Renderer renderer, int target, int bufferId,
|
||||
int bufferOffset, bool discard,
|
||||
IntPtr dataPointer, int dataLength)
|
||||
{
|
||||
var state = (State) renderer.UserData;
|
||||
var dataBuffer = BufferSerializer.Convert(
|
||||
dataPointer, dataLength, state, bufferId);
|
||||
|
||||
renderer.Send( () =>
|
||||
{
|
||||
GLES20.glBindBuffer(target, bufferId);
|
||||
|
||||
if (discard)
|
||||
{
|
||||
var sizeUsage = state.BufferSizeUsage[bufferId];
|
||||
GLES20.glBufferData(target, sizeUsage[0], null, sizeUsage[1]);
|
||||
}
|
||||
|
||||
GLES20.glBufferSubData(target, bufferOffset, dataLength, dataBuffer);
|
||||
});
|
||||
}
|
||||
|
||||
public static void FNA3D_SetVertexBufferData(IntPtr device, IntPtr buffer,
|
||||
int offsetInBytes, IntPtr data,
|
||||
int elementCount, int elementSizeInBytes,
|
||||
int vertexStride, SetDataOptions options)
|
||||
{
|
||||
if (elementSizeInBytes != vertexStride)
|
||||
throw new System.ArgumentException("elementSizeInBytes != vertexStride");
|
||||
|
||||
SetBufferData(Renderer.Get(device), GLES20.GL_ARRAY_BUFFER,
|
||||
(int) buffer, offsetInBytes,
|
||||
(options == SetDataOptions.Discard),
|
||||
data, elementCount * elementSizeInBytes);
|
||||
}
|
||||
|
||||
public static void FNA3D_SetIndexBufferData(IntPtr device, IntPtr buffer,
|
||||
int offsetInBytes, IntPtr data,
|
||||
int dataLength, SetDataOptions options)
|
||||
{
|
||||
SetBufferData(Renderer.Get(device), GLES20.GL_ELEMENT_ARRAY_BUFFER,
|
||||
(int) buffer, offsetInBytes,
|
||||
(options == SetDataOptions.Discard),
|
||||
data, dataLength);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Set Buffer Attributes
|
||||
//
|
||||
|
||||
public static unsafe void FNA3D_ApplyVertexBufferBindings(IntPtr device,
|
||||
FNA3D_VertexBufferBinding* bindings,
|
||||
int numBindings, byte bindingsUpdated,
|
||||
int baseVertex)
|
||||
{
|
||||
var bindingsCopy = new FNA3D_VertexBufferBinding[numBindings];
|
||||
for (int i = 0; i < numBindings; i++)
|
||||
bindingsCopy[i] = bindings[i];
|
||||
|
||||
Renderer.Get(device).Send( () =>
|
||||
{
|
||||
int nextAttribIndex = 0;
|
||||
foreach (var binding in bindingsCopy)
|
||||
{
|
||||
if (binding.instanceFrequency != 0)
|
||||
throw new ArgumentException("InstanceFrequnecy != 0");
|
||||
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, (int) binding.vertexBuffer);
|
||||
|
||||
var vertexDecl = binding.vertexDeclaration;
|
||||
var elements = (VertexElement[]) System.Runtime.InteropServices.GCHandle
|
||||
.FromIntPtr(vertexDecl.elements).Target;
|
||||
for (int j = 0; j < vertexDecl.elementCount; j++)
|
||||
{
|
||||
if (elements[j].UsageIndex != 0)
|
||||
throw new ArgumentException("UsageIndex != 0");
|
||||
var fmt = elements[j].VertexElementFormat;
|
||||
|
||||
int size = VertexElementToBindingSize[(int) fmt];
|
||||
int type = VertexElementToBindingType[(int) fmt];
|
||||
bool norm = ( elements[j].VertexElementUsage ==
|
||||
VertexElementUsage.Color
|
||||
|| fmt == VertexElementFormat.NormalizedShort2
|
||||
|| fmt == VertexElementFormat.NormalizedShort4);
|
||||
|
||||
int stride = vertexDecl.vertexStride;
|
||||
int offset = (binding.vertexOffset + baseVertex) * stride
|
||||
+ elements[j].Offset;
|
||||
|
||||
GLES20.glVertexAttribPointer(nextAttribIndex, size, type, norm,
|
||||
stride, offset);
|
||||
|
||||
GLES20.glEnableVertexAttribArray(nextAttribIndex++);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// FNA3D_VertexBufferBinding
|
||||
//
|
||||
|
||||
public struct FNA3D_VertexBufferBinding
|
||||
{
|
||||
public IntPtr vertexBuffer;
|
||||
public FNA3D_VertexDeclaration vertexDeclaration;
|
||||
public int vertexOffset;
|
||||
public int instanceFrequency;
|
||||
}
|
||||
|
||||
//
|
||||
// FNA3D_VertexDeclaration
|
||||
//
|
||||
|
||||
public struct FNA3D_VertexDeclaration
|
||||
{
|
||||
public int vertexStride;
|
||||
public int elementCount;
|
||||
public IntPtr elements;
|
||||
}
|
||||
|
||||
//
|
||||
// VertexElementToBindingSize
|
||||
//
|
||||
|
||||
static int[] VertexElementToBindingSize = new int[]
|
||||
{
|
||||
1, // VertexElementFormat.Single
|
||||
2, // VertexElementFormat.Vector2
|
||||
3, // VertexElementFormat.Vector3
|
||||
4, // VertexElementFormat.Vector4
|
||||
4, // VertexElementFormat.Color
|
||||
4, // VertexElementFormat.Byte4
|
||||
2, // VertexElementFormat.Short2
|
||||
4, // VertexElementFormat.Short4
|
||||
2, // VertexElementFormat.NormalizedShort2
|
||||
4, // VertexElementFormat.NormalizedShort4
|
||||
2, // VertexElementFormat.HalfVector2
|
||||
4 // VertexElementFormat.HalfVector4
|
||||
};
|
||||
|
||||
//
|
||||
// VertexElementToBindingType
|
||||
//
|
||||
|
||||
static int[] VertexElementToBindingType = new int[]
|
||||
{
|
||||
GLES20.GL_FLOAT, // VertexElementFormat.Single
|
||||
GLES20.GL_FLOAT, // VertexElementFormat.Vector2
|
||||
GLES20.GL_FLOAT, // VertexElementFormat.Vector3
|
||||
GLES20.GL_FLOAT, // VertexElementFormat.Vector4
|
||||
GLES20.GL_UNSIGNED_BYTE, // VertexElementFormat.Color
|
||||
GLES20.GL_UNSIGNED_BYTE, // VertexElementFormat.Byte4
|
||||
GLES20.GL_SHORT, // VertexElementFormat.Short2
|
||||
GLES20.GL_SHORT, // VertexElementFormat.Short4
|
||||
GLES20.GL_SHORT, // VertexElementFormat.NormalizedShort2
|
||||
GLES20.GL_SHORT, // VertexElementFormat.NormalizedShort4
|
||||
GLES30.GL_HALF_FLOAT, // VertexElementFormat.HalfVector2
|
||||
GLES30.GL_HALF_FLOAT // VertexElementFormat.HalfVector4
|
||||
};
|
||||
|
||||
|
||||
|
||||
//
|
||||
// BufferSerializer
|
||||
//
|
||||
|
||||
private static class BufferSerializer
|
||||
{
|
||||
|
||||
public static java.nio.Buffer Convert(IntPtr data, int length,
|
||||
State state, int bufferId)
|
||||
{
|
||||
java.nio.Buffer oldBuffer, newBuffer;
|
||||
lock (state.BufferCache)
|
||||
{
|
||||
state.BufferCache.TryGetValue(bufferId, out oldBuffer);
|
||||
}
|
||||
|
||||
// FNA IndexBuffer uses GCHandle::Alloc and GCHandle::AddrOfPinnedObject.
|
||||
// we use GCHandle::FromIntPtr to convert that address to an object reference.
|
||||
// see also: system.runtime.interopservices.GCHandle struct in baselib.
|
||||
int offset = (int) data;
|
||||
newBuffer = Convert(GCHandle.FromIntPtr(data - offset).Target,
|
||||
offset, length, oldBuffer);
|
||||
|
||||
if (newBuffer != oldBuffer)
|
||||
{
|
||||
lock (state.BufferCache)
|
||||
{
|
||||
state.BufferCache[bufferId] = newBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
return newBuffer;
|
||||
}
|
||||
|
||||
|
||||
public static java.nio.Buffer Convert(object data, int offset, int length,
|
||||
java.nio.Buffer buffer)
|
||||
{
|
||||
if (data is short[])
|
||||
{
|
||||
return FromShort((short[]) data, offset, length);
|
||||
}
|
||||
|
||||
var byteBuffer = (buffer != null && buffer.limit() >= length)
|
||||
? (java.nio.ByteBuffer) buffer
|
||||
: java.nio.ByteBuffer.allocateDirect(length)
|
||||
.order(java.nio.ByteOrder.nativeOrder());
|
||||
|
||||
if (data is SpriteBatch.VertexPositionColorTexture4[])
|
||||
{
|
||||
FromVertexPositionColorTexture4(
|
||||
(SpriteBatch.VertexPositionColorTexture4[]) data,
|
||||
offset, length, byteBuffer);
|
||||
}
|
||||
|
||||
else if (data is VertexPositionColor[])
|
||||
{
|
||||
FromVertexPositionColor(
|
||||
(VertexPositionColor[]) data, offset, length, byteBuffer);
|
||||
}
|
||||
|
||||
else if (data is VertexPositionColorTexture[])
|
||||
{
|
||||
FromVertexPositionColorTexture(
|
||||
(VertexPositionColorTexture[]) data, offset, length, byteBuffer);
|
||||
}
|
||||
|
||||
else if (data is VertexPositionNormalTexture[])
|
||||
{
|
||||
FromVertexPositionNormalTexture(
|
||||
(VertexPositionNormalTexture[]) data, offset, length, byteBuffer);
|
||||
}
|
||||
|
||||
else if (data is VertexPositionTexture[])
|
||||
{
|
||||
FromVertexPositionTexture(
|
||||
(VertexPositionTexture[]) data, offset, length, byteBuffer);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
/*IVertexType iVertexType,
|
||||
=> FromVertexDeclaration(iVertexType.VertexDeclaration,
|
||||
data, offset, length),*/
|
||||
|
||||
throw new ArgumentException($"unsupported buffer type '{data.GetType()}'");
|
||||
};
|
||||
|
||||
return byteBuffer.position(0);
|
||||
}
|
||||
|
||||
private static void ValidateOffsetAndLength(int offset, int length, int divisor)
|
||||
{
|
||||
if ((offset % divisor) != 0 || (length % divisor) != 0)
|
||||
throw new ArgumentException(
|
||||
$"length and offset of buffer should be divisible by {divisor}");
|
||||
}
|
||||
|
||||
private static java.nio.Buffer FromShort(short[] array, int offset, int length)
|
||||
{
|
||||
ValidateOffsetAndLength(offset, length, 2);
|
||||
return java.nio.ShortBuffer.wrap(array,
|
||||
offset / sizeof(short),
|
||||
length / sizeof(short));
|
||||
}
|
||||
|
||||
private static void FromVertexPositionColorTexture4(
|
||||
SpriteBatch.VertexPositionColorTexture4[] array,
|
||||
int offset, int length, java.nio.ByteBuffer buffer)
|
||||
{
|
||||
ValidateOffsetAndLength(offset, length, 96);
|
||||
|
||||
int index = offset / 96;
|
||||
int count = length / 96;
|
||||
for (; count-- > 0; index++)
|
||||
{
|
||||
PutVector3(buffer, ref array[index].Position0);
|
||||
PutColor(buffer, ref array[index].Color0);
|
||||
PutVector2(buffer, ref array[index].TextureCoordinate0);
|
||||
|
||||
PutVector3(buffer, ref array[index].Position1);
|
||||
PutColor(buffer, ref array[index].Color1);
|
||||
PutVector2(buffer, ref array[index].TextureCoordinate1);
|
||||
|
||||
PutVector3(buffer, ref array[index].Position2);
|
||||
PutColor(buffer, ref array[index].Color2);
|
||||
PutVector2(buffer, ref array[index].TextureCoordinate2);
|
||||
|
||||
PutVector3(buffer, ref array[index].Position3);
|
||||
PutColor(buffer, ref array[index].Color3);
|
||||
PutVector2(buffer, ref array[index].TextureCoordinate3);
|
||||
}
|
||||
}
|
||||
|
||||
private static void FromVertexPositionColor(
|
||||
VertexPositionColor[] array,
|
||||
int offset, int length, java.nio.ByteBuffer buffer)
|
||||
{
|
||||
ValidateOffsetAndLength(offset, length, 16);
|
||||
|
||||
int index = offset / 16;
|
||||
int count = length / 16;
|
||||
for (; count-- > 0; index++)
|
||||
{
|
||||
PutVector3(buffer, ref array[index].Position);
|
||||
PutColor(buffer, ref array[index].Color);
|
||||
}
|
||||
}
|
||||
|
||||
private static void FromVertexPositionColorTexture(
|
||||
VertexPositionColorTexture[] array,
|
||||
int offset, int length, java.nio.ByteBuffer buffer)
|
||||
{
|
||||
ValidateOffsetAndLength(offset, length, 24);
|
||||
|
||||
int index = offset / 24;
|
||||
int count = length / 24;
|
||||
for (; count-- > 0; index++)
|
||||
{
|
||||
PutVector3(buffer, ref array[index].Position);
|
||||
PutColor(buffer, ref array[index].Color);
|
||||
PutVector2(buffer, ref array[index].TextureCoordinate);
|
||||
}
|
||||
}
|
||||
|
||||
private static void FromVertexPositionNormalTexture(
|
||||
VertexPositionNormalTexture[] array,
|
||||
int offset, int length, java.nio.ByteBuffer buffer)
|
||||
{
|
||||
ValidateOffsetAndLength(offset, length, 32);
|
||||
|
||||
int index = offset / 32;
|
||||
int count = length / 32;
|
||||
for (; count-- > 0; index++)
|
||||
{
|
||||
PutVector3(buffer, ref array[index].Position);
|
||||
PutVector3(buffer, ref array[index].Normal);
|
||||
PutVector2(buffer, ref array[index].TextureCoordinate);
|
||||
}
|
||||
}
|
||||
|
||||
private static void FromVertexPositionTexture(
|
||||
VertexPositionTexture[] array,
|
||||
int offset, int length, java.nio.ByteBuffer buffer)
|
||||
{
|
||||
ValidateOffsetAndLength(offset, length, 20);
|
||||
|
||||
int index = offset / 20;
|
||||
int count = length / 20;
|
||||
for (; count-- > 0; index++)
|
||||
{
|
||||
PutVector3(buffer, ref array[index].Position);
|
||||
PutVector2(buffer, ref array[index].TextureCoordinate);
|
||||
}
|
||||
}
|
||||
|
||||
/*private static java.nio.Buffer FromVertexDeclaration(
|
||||
VertexDeclaration vertexDeclaration,
|
||||
object data, int offset, int length) { } */
|
||||
|
||||
private static void PutVector3(java.nio.ByteBuffer buffer, ref Vector3 vector3)
|
||||
{
|
||||
buffer.putFloat(vector3.X);
|
||||
buffer.putFloat(vector3.Y);
|
||||
buffer.putFloat(vector3.Z);
|
||||
}
|
||||
|
||||
private static void PutVector2(java.nio.ByteBuffer buffer, ref Vector2 vector2)
|
||||
{
|
||||
buffer.putFloat(vector2.X);
|
||||
buffer.putFloat(vector2.Y);
|
||||
}
|
||||
|
||||
private static void PutColor(java.nio.ByteBuffer buffer, ref Color color)
|
||||
{
|
||||
buffer.put((sbyte) color.R);
|
||||
buffer.put((sbyte) color.G);
|
||||
buffer.put((sbyte) color.B);
|
||||
buffer.put((sbyte) color.A);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// State
|
||||
//
|
||||
|
||||
private partial class State
|
||||
{
|
||||
public Dictionary<int, int[]> BufferSizeUsage = new Dictionary<int, int[]>();
|
||||
public Dictionary<int, java.nio.Buffer> BufferCache = new Dictionary<int, java.nio.Buffer>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
140
BNA/src/FNA3D_Dev.cs
Normal file
140
BNA/src/FNA3D_Dev.cs
Normal file
@ -0,0 +1,140 @@
|
||||
|
||||
using System;
|
||||
using android.opengl;
|
||||
#pragma warning disable 0436
|
||||
|
||||
namespace Microsoft.Xna.Framework.Graphics
|
||||
{
|
||||
|
||||
public static partial class FNA3D
|
||||
{
|
||||
|
||||
//
|
||||
// FNA3D_CreateDevice
|
||||
//
|
||||
|
||||
public static IntPtr FNA3D_CreateDevice(
|
||||
ref FNA3D_PresentationParameters presentationParameters,
|
||||
byte debugMode)
|
||||
{
|
||||
int depthSize, stencilSize = 0;
|
||||
switch (presentationParameters.depthStencilFormat)
|
||||
{
|
||||
case DepthFormat.None: depthSize = 0; break;
|
||||
case DepthFormat.Depth16: depthSize = 16; break;
|
||||
case DepthFormat.Depth24: depthSize = 24; break;
|
||||
case DepthFormat.Depth24Stencil8: depthSize = 24; stencilSize = 8; break;
|
||||
default: throw new ArgumentException("depthStencilFormat");
|
||||
}
|
||||
|
||||
var device = Renderer.Create(GameRunner.Singleton.Activity,
|
||||
GameRunner.Singleton.OnSurfaceChanged,
|
||||
8, 8, 8, 0, depthSize, stencilSize);
|
||||
FNA3D_ResetBackbuffer(device, ref presentationParameters);
|
||||
return device;
|
||||
|
||||
/*var renderer = Renderer.Get(device);
|
||||
renderer.UserData = new State()
|
||||
{
|
||||
BackBufferWidth = presentationParameters.backBufferWidth,
|
||||
BackBufferHeight = presentationParameters.backBufferHeight,
|
||||
AdjustViewport = // see also FNA3D_SetViewport
|
||||
(presentationParameters.displayOrientation == DisplayOrientation.Default)
|
||||
};*/
|
||||
}
|
||||
|
||||
//
|
||||
// FNA3D_ResetBackbuffer
|
||||
//
|
||||
|
||||
public static void FNA3D_ResetBackbuffer(IntPtr device,
|
||||
ref FNA3D_PresentationParameters presentationParameters)
|
||||
{
|
||||
var renderer = Renderer.Get(device);
|
||||
|
||||
var state = (State) renderer.UserData;
|
||||
if (state == null)
|
||||
{
|
||||
state = new State();
|
||||
renderer.UserData = state;
|
||||
}
|
||||
|
||||
state.BackBufferWidth = presentationParameters.backBufferWidth;
|
||||
state.BackBufferHeight = presentationParameters.backBufferHeight;
|
||||
state.AdjustViewport = // see also FNA3D_SetViewport
|
||||
(presentationParameters.displayOrientation == DisplayOrientation.Default);
|
||||
|
||||
presentationParameters.backBufferFormat = SurfaceFormat.Color;
|
||||
presentationParameters.isFullScreen = 1;
|
||||
}
|
||||
|
||||
//
|
||||
// FNA3D_DestroyDevice
|
||||
//
|
||||
|
||||
public static void FNA3D_DestroyDevice(IntPtr device)
|
||||
{
|
||||
Renderer.Get(device).Release();
|
||||
}
|
||||
|
||||
//
|
||||
// FNA3D_GetMaxTextureSlots
|
||||
//
|
||||
|
||||
public static void FNA3D_GetMaxTextureSlots(IntPtr device,
|
||||
out int textures, out int vertexTextures)
|
||||
{
|
||||
// XNA GraphicsDevice Limits from FNA3D/src/FNA3D_Driver.h
|
||||
const int MAX_TEXTURE_SAMPLERS = 16;
|
||||
const int MAX_VERTEXTEXTURE_SAMPLERS = 4;
|
||||
|
||||
var renderer = Renderer.Get(device);
|
||||
int numSamplers = renderer.TextureUnits;
|
||||
// number of texture slots
|
||||
textures = Math.Min(numSamplers, MAX_TEXTURE_SAMPLERS);
|
||||
// number of vertex texture slots
|
||||
vertexTextures = Math.Min(Math.Max(numSamplers - MAX_TEXTURE_SAMPLERS, 0),
|
||||
MAX_VERTEXTEXTURE_SAMPLERS);
|
||||
}
|
||||
|
||||
//
|
||||
// FNA3D_GetBackbufferDepthFormat
|
||||
//
|
||||
|
||||
public static DepthFormat FNA3D_GetBackbufferDepthFormat(IntPtr device)
|
||||
{
|
||||
return Renderer.Get(device).SurfaceDepthFormat;
|
||||
}
|
||||
|
||||
//
|
||||
// FNA3D_PresentationParameters
|
||||
//
|
||||
|
||||
public struct FNA3D_PresentationParameters
|
||||
{
|
||||
public int backBufferWidth;
|
||||
public int backBufferHeight;
|
||||
public SurfaceFormat backBufferFormat;
|
||||
public int multiSampleCount;
|
||||
public IntPtr deviceWindowHandle;
|
||||
public byte isFullScreen;
|
||||
public DepthFormat depthStencilFormat;
|
||||
public PresentInterval presentationInterval;
|
||||
public DisplayOrientation displayOrientation;
|
||||
public RenderTargetUsage renderTargetUsage;
|
||||
}
|
||||
|
||||
//
|
||||
// State
|
||||
//
|
||||
|
||||
private partial class State
|
||||
{
|
||||
public int BackBufferWidth;
|
||||
public int BackBufferHeight;
|
||||
public bool AdjustViewport;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
247
BNA/src/FNA3D_Rt.cs
Normal file
247
BNA/src/FNA3D_Rt.cs
Normal file
@ -0,0 +1,247 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using android.opengl;
|
||||
#pragma warning disable 0436
|
||||
|
||||
namespace Microsoft.Xna.Framework.Graphics
|
||||
{
|
||||
|
||||
public static partial class FNA3D
|
||||
{
|
||||
|
||||
//
|
||||
// FNA3D_SetRenderTargets
|
||||
//
|
||||
|
||||
public static unsafe void FNA3D_SetRenderTargets(IntPtr device,
|
||||
FNA3D_RenderTargetBinding* renderTargets,
|
||||
int numRenderTargets,
|
||||
IntPtr depthStencilBuffer,
|
||||
DepthFormat depthFormat,
|
||||
byte preserveContents)
|
||||
{
|
||||
var renderTargetsCopy = new FNA3D_RenderTargetBinding[numRenderTargets];
|
||||
for (int i = 0; i < numRenderTargets; i++)
|
||||
renderTargetsCopy[i] = renderTargets[i];
|
||||
|
||||
var renderer = Renderer.Get(device);
|
||||
renderer.Send( () =>
|
||||
{
|
||||
var state = (State) renderer.UserData;
|
||||
if (state.TargetFramebuffer == 0)
|
||||
{
|
||||
var id = new int[1];
|
||||
GLES20.glGenFramebuffers(1, id, 0);
|
||||
if ((state.TargetFramebuffer = id[0]) == 0)
|
||||
return;
|
||||
}
|
||||
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, state.TargetFramebuffer);
|
||||
|
||||
int attachmentIndex = GLES20.GL_COLOR_ATTACHMENT0;
|
||||
foreach (var renderTarget in renderTargetsCopy)
|
||||
{
|
||||
if (renderTarget.colorBuffer != IntPtr.Zero)
|
||||
{
|
||||
// a color buffer is only created if a non-zero result
|
||||
// from FNA3D_GetMaxMultiSampleCount, which we never do
|
||||
throw new PlatformNotSupportedException();
|
||||
/*GLES20.glFramebufferRenderbuffer(
|
||||
GLES20.GL_FRAMEBUFFER, attachmentIndex,
|
||||
GLES20.GL_RENDERBUFFER, (int) renderTarget.colorBuffer);*/
|
||||
}
|
||||
else
|
||||
{
|
||||
int attachmentType = GLES20.GL_TEXTURE_2D;
|
||||
if (renderTarget.type != /* FNA3D_RENDERTARGET_TYPE_2D */ 0)
|
||||
{
|
||||
attachmentType = GLES20.GL_TEXTURE_CUBE_MAP_POSITIVE_X
|
||||
+ renderTarget.data2;
|
||||
}
|
||||
GLES20.glFramebufferTexture2D(
|
||||
GLES20.GL_FRAMEBUFFER, attachmentIndex,
|
||||
attachmentType, (int) renderTarget.texture, 0);
|
||||
}
|
||||
attachmentIndex++;
|
||||
}
|
||||
|
||||
int lastAttachmentPlusOne = state.ActiveAttachments;
|
||||
state.ActiveAttachments = attachmentIndex;
|
||||
while (attachmentIndex < lastAttachmentPlusOne)
|
||||
{
|
||||
GLES20.glFramebufferRenderbuffer(
|
||||
GLES20.GL_FRAMEBUFFER, attachmentIndex++,
|
||||
GLES20.GL_RENDERBUFFER, 0);
|
||||
}
|
||||
|
||||
GLES20.glFramebufferRenderbuffer(
|
||||
GLES20.GL_FRAMEBUFFER, GLES30.GL_DEPTH_STENCIL_ATTACHMENT,
|
||||
GLES20.GL_RENDERBUFFER, (int) depthStencilBuffer);
|
||||
|
||||
state.RenderToTexture = true;
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// FNA3D_SetRenderTargets
|
||||
//
|
||||
|
||||
public static void FNA3D_SetRenderTargets(IntPtr device,
|
||||
IntPtr renderTargets,
|
||||
int numRenderTargets,
|
||||
IntPtr depthStencilBuffer,
|
||||
DepthFormat depthFormat,
|
||||
byte preserveContents)
|
||||
{
|
||||
if (renderTargets != IntPtr.Zero || numRenderTargets != 0)
|
||||
throw new PlatformNotSupportedException();
|
||||
|
||||
var renderer = Renderer.Get(device);
|
||||
renderer.Send( () =>
|
||||
{
|
||||
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
|
||||
|
||||
var state = (State) renderer.UserData;
|
||||
state.RenderToTexture = false;
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// FNA3D_ResolveTarget
|
||||
//
|
||||
|
||||
public static void FNA3D_ResolveTarget(IntPtr device,
|
||||
ref FNA3D_RenderTargetBinding renderTarget)
|
||||
{
|
||||
if (renderTarget.multiSampleCount > 0)
|
||||
{
|
||||
// no support for multisampling; see FNA3D_SetRenderTargets
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
|
||||
if (renderTarget.levelCount > 1)
|
||||
{
|
||||
int attachmentType = GLES20.GL_TEXTURE_2D;
|
||||
if (renderTarget.type != /* FNA3D_RENDERTARGET_TYPE_2D */ 0)
|
||||
{
|
||||
attachmentType = GLES20.GL_TEXTURE_CUBE_MAP_POSITIVE_X
|
||||
+ renderTarget.data2;
|
||||
}
|
||||
int texture = (int) renderTarget.texture;
|
||||
|
||||
var renderer = Renderer.Get(device);
|
||||
renderer.Send( () =>
|
||||
{
|
||||
int textureUnit = GLES20.GL_TEXTURE0 + renderer.TextureUnits - 1;
|
||||
GLES20.glActiveTexture(textureUnit);
|
||||
|
||||
GLES20.glBindTexture(attachmentType, texture);
|
||||
GLES20.glGenerateMipmap(attachmentType);
|
||||
GLES20.glBindTexture(attachmentType, 0);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// FNA3D_GenDepthStencilRenderbuffer
|
||||
//
|
||||
|
||||
public static IntPtr FNA3D_GenDepthStencilRenderbuffer(IntPtr device,
|
||||
int width, int height,
|
||||
DepthFormat format,
|
||||
int multiSampleCount)
|
||||
{
|
||||
if (multiSampleCount > 0)
|
||||
{
|
||||
// no support for multisampling; see FNA3D_SetRenderTargets
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
|
||||
int bufferId = 0;
|
||||
|
||||
var renderer = Renderer.Get(device);
|
||||
renderer.Send( () =>
|
||||
{
|
||||
var state = (State) renderer.UserData;
|
||||
|
||||
var id = new int[1];
|
||||
GLES20.glGenRenderbuffers(1, id, 0);
|
||||
if (id[0] != 0)
|
||||
{
|
||||
GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, id[0]);
|
||||
GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER,
|
||||
DepthFormatToDepthStorage[(int) format],
|
||||
width, height);
|
||||
|
||||
bufferId = id[0];
|
||||
}
|
||||
});
|
||||
|
||||
return (IntPtr) bufferId;
|
||||
}
|
||||
|
||||
//
|
||||
// Delete Render Target
|
||||
//
|
||||
|
||||
public static void FNA3D_AddDisposeRenderbuffer(IntPtr device, IntPtr renderbuffer)
|
||||
{
|
||||
var renderer = Renderer.Get(device);
|
||||
renderer.Send( () =>
|
||||
{
|
||||
GLES20.glDeleteRenderbuffers(1, new int[] { (int) renderbuffer }, 0);
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// IsRenderToTexture
|
||||
//
|
||||
|
||||
public static bool IsRenderToTexture(GraphicsDevice graphicsDevice)
|
||||
{
|
||||
// should be called in the renderer thread context
|
||||
return ((State) Renderer.Get(graphicsDevice.GLDevice).UserData).RenderToTexture;
|
||||
}
|
||||
|
||||
//
|
||||
// DepthFormatToDepthStorage
|
||||
//
|
||||
|
||||
static int[] DepthFormatToDepthStorage = new int[]
|
||||
{
|
||||
GLES20.GL_ZERO, // invalid
|
||||
GLES20.GL_DEPTH_COMPONENT16, // DepthFormat.Depth16
|
||||
GLES30.GL_DEPTH_COMPONENT24, // DepthFormat.Depth24
|
||||
GLES30.GL_DEPTH24_STENCIL8, // DepthFormat.Depth24Stencil8
|
||||
};
|
||||
|
||||
//
|
||||
// FNA3D_RenderTargetBinding
|
||||
//
|
||||
|
||||
public struct FNA3D_RenderTargetBinding
|
||||
{
|
||||
public byte type; // 0 for RenderTarget2D, 1 for RenderTargetCube
|
||||
public int data1; // 2D width or cube size
|
||||
public int data2; // 2D height or cube face
|
||||
public int levelCount;
|
||||
public int multiSampleCount;
|
||||
public IntPtr texture;
|
||||
public IntPtr colorBuffer;
|
||||
}
|
||||
|
||||
//
|
||||
// State
|
||||
//
|
||||
|
||||
private partial class State
|
||||
{
|
||||
public bool RenderToTexture;
|
||||
public int TargetFramebuffer;
|
||||
public int ActiveAttachments;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
577
BNA/src/FNA3D_Tex.cs
Normal file
577
BNA/src/FNA3D_Tex.cs
Normal file
@ -0,0 +1,577 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using android.opengl;
|
||||
#pragma warning disable 0436
|
||||
|
||||
namespace Microsoft.Xna.Framework.Graphics
|
||||
{
|
||||
|
||||
public static partial class FNA3D
|
||||
{
|
||||
|
||||
//
|
||||
// FNA3D_SupportsDXT1
|
||||
//
|
||||
|
||||
public static byte FNA3D_SupportsDXT1(IntPtr device)
|
||||
{
|
||||
var fmts = Renderer.Get(device).TextureFormats;
|
||||
bool f = -1 != Array.IndexOf(fmts, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT);
|
||||
return (byte) (f ? 1 : 0);
|
||||
}
|
||||
|
||||
//
|
||||
// FNA3D_SupportsS3TC
|
||||
//
|
||||
|
||||
public static byte FNA3D_SupportsS3TC(IntPtr device)
|
||||
{
|
||||
var fmts = Renderer.Get(device).TextureFormats;
|
||||
bool f = -1 != Array.IndexOf(fmts, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT)
|
||||
&& -1 != Array.IndexOf(fmts, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT);
|
||||
return (byte) (f ? 1 : 0);
|
||||
}
|
||||
|
||||
//
|
||||
// Create Textures
|
||||
//
|
||||
|
||||
private static int CreateTexture(Renderer renderer, int textureKind,
|
||||
SurfaceFormat format, int levelCount)
|
||||
{
|
||||
int[] id = new int[1];
|
||||
GLES20.glGenTextures(1, id, 0);
|
||||
if (id[0] != 0)
|
||||
{
|
||||
GLES20.glBindTexture(textureKind, id[0]);
|
||||
|
||||
var state = (State) renderer.UserData;
|
||||
state.TextureConfigs[id[0]] =
|
||||
new int[] { textureKind, (int) format, levelCount };
|
||||
|
||||
}
|
||||
return id[0];
|
||||
}
|
||||
|
||||
public static IntPtr FNA3D_CreateTexture2D(IntPtr device, SurfaceFormat format,
|
||||
int width, int height, int levelCount,
|
||||
byte isRenderTarget)
|
||||
{
|
||||
var renderer = Renderer.Get(device);
|
||||
if ( width <= 0 || width > renderer.TextureSize
|
||||
|| height <= 0 || height > renderer.TextureSize)
|
||||
{
|
||||
throw new ArgumentException($"bad texture size {width} x {height}");
|
||||
}
|
||||
|
||||
int textureFormat = SurfaceFormatToTextureFormat[(int) format];
|
||||
int internalFormat = SurfaceFormatToTextureInternalFormat[(int) format];
|
||||
int dataType, dataSize;
|
||||
|
||||
if (textureFormat == GLES20.GL_COMPRESSED_TEXTURE_FORMATS)
|
||||
{
|
||||
dataType = textureFormat;
|
||||
dataSize = SurfaceFormatToTextureDataSize[(int) format];
|
||||
}
|
||||
else
|
||||
{
|
||||
dataType = SurfaceFormatToTextureDataType[(int) format];
|
||||
dataSize = 0;
|
||||
}
|
||||
|
||||
if ( textureFormat == GLES20.GL_ZERO
|
||||
|| internalFormat == GLES20.GL_ZERO
|
||||
|| dataType == GLES20.GL_ZERO)
|
||||
{
|
||||
throw new PlatformNotSupportedException(
|
||||
$"unsupported texture format {format}");
|
||||
}
|
||||
|
||||
int textureId = 0;
|
||||
renderer.Send( () =>
|
||||
{
|
||||
int id = CreateTexture(renderer, GLES20.GL_TEXTURE_2D, format, levelCount);
|
||||
if (id != 0)
|
||||
{
|
||||
textureId = id;
|
||||
|
||||
for (int level = 0; level < levelCount; level++)
|
||||
{
|
||||
if (textureFormat == GLES20.GL_COMPRESSED_TEXTURE_FORMATS)
|
||||
{
|
||||
int levelSize = dataSize *
|
||||
((width + 3) / 4) * ((height + 3) / 4);
|
||||
|
||||
GLES20.glCompressedTexImage2D(GLES20.GL_TEXTURE_2D,
|
||||
level, internalFormat,
|
||||
width, height, /* border */ 0,
|
||||
levelSize, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D,
|
||||
level, internalFormat,
|
||||
width, height, /* border */ 0,
|
||||
textureFormat, dataType, null);
|
||||
}
|
||||
|
||||
width >>= 1;
|
||||
height >>= 1;
|
||||
if (width <= 0)
|
||||
width = 1;
|
||||
if (height <= 0)
|
||||
height = 1;
|
||||
}
|
||||
}
|
||||
});
|
||||
return (IntPtr) textureId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Delete Textures
|
||||
//
|
||||
|
||||
public static void FNA3D_AddDisposeTexture(IntPtr device, IntPtr texture)
|
||||
{
|
||||
var renderer = Renderer.Get(device);
|
||||
renderer.Send( () =>
|
||||
{
|
||||
GLES20.glDeleteTextures(1, new int[] { (int) texture }, 0);
|
||||
|
||||
var state = (State) renderer.UserData;
|
||||
state.TextureConfigs.Remove((int) texture);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Set Texture Data
|
||||
//
|
||||
|
||||
private static void SetTextureData(Renderer renderer, int textureId,
|
||||
int x, int y, int w, int h, int level,
|
||||
object dataObject, int dataOffset, int dataLength)
|
||||
{
|
||||
java.nio.Buffer buffer = dataObject switch
|
||||
{
|
||||
sbyte[] byteArray =>
|
||||
java.nio.ByteBuffer.wrap(byteArray, dataOffset, dataLength),
|
||||
|
||||
int[] intArray =>
|
||||
java.nio.IntBuffer.wrap(intArray, dataOffset / 4, dataLength / 4),
|
||||
|
||||
_ => throw new ArgumentException(dataObject?.GetType().ToString()),
|
||||
};
|
||||
|
||||
renderer.Send( () =>
|
||||
{
|
||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
|
||||
|
||||
var state = (State) renderer.UserData;
|
||||
var config = state.TextureConfigs[textureId];
|
||||
int format = config[1];
|
||||
|
||||
int textureFormat = SurfaceFormatToTextureFormat[format];
|
||||
if (textureFormat == GLES20.GL_COMPRESSED_TEXTURE_FORMATS)
|
||||
{
|
||||
int internalFormat = SurfaceFormatToTextureInternalFormat[format];
|
||||
|
||||
GLES20.glCompressedTexSubImage2D(GLES20.GL_TEXTURE_2D,
|
||||
level, x, y, w, h, internalFormat, dataLength, buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
int dataType = SurfaceFormatToTextureDataType[format];
|
||||
int dataSize = SurfaceFormatToTextureDataSize[format];
|
||||
|
||||
if (dataSize != 4)
|
||||
GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, dataSize);
|
||||
|
||||
GLES20.glTexSubImage2D(GLES20.GL_TEXTURE_2D, level, x, y, w, h,
|
||||
textureFormat, dataType, buffer);
|
||||
|
||||
if (dataSize != 4)
|
||||
GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 4);
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void FNA3D_SetTextureData2D(IntPtr device, IntPtr texture,
|
||||
int x, int y, int w, int h, int level,
|
||||
IntPtr data, int dataLength)
|
||||
{
|
||||
// FNA Texture2D uses GCHandle::Alloc and GCHandle::AddrOfPinnedObject.
|
||||
// we use GCHandle::FromIntPtr to convert that address to an object reference.
|
||||
// see also: system.runtime.interopservices.GCHandle struct in baselib.
|
||||
int dataOffset = (int) data;
|
||||
var dataObject = System.Runtime.InteropServices.GCHandle.FromIntPtr(data).Target;
|
||||
SetTextureData(Renderer.Get(device), (int) texture,
|
||||
x, y, w, h, level, dataObject, dataOffset, dataLength);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// ReadImageStream
|
||||
//
|
||||
|
||||
public static IntPtr ReadImageStream(System.IO.Stream stream,
|
||||
out int width, out int height, out int len,
|
||||
int forceWidth, int forceHeight, bool zoom)
|
||||
{
|
||||
var bitmap = LoadBitmap(stream);
|
||||
|
||||
int[] pixels;
|
||||
if (forceWidth == -1 || forceHeight == -1)
|
||||
{
|
||||
pixels = GetPixels(bitmap, out width, out height, out len);
|
||||
}
|
||||
else
|
||||
{
|
||||
pixels = CropAndScale(bitmap, forceWidth, forceHeight, zoom,
|
||||
out width, out height, out len);
|
||||
}
|
||||
|
||||
// keep a strong reference until FNA3D_Image_Free is called
|
||||
ImagePixels.set(pixels);
|
||||
|
||||
return System.Runtime.InteropServices.GCHandle.Alloc(
|
||||
pixels, System.Runtime.InteropServices.GCHandleType.Pinned)
|
||||
.AddrOfPinnedObject();
|
||||
|
||||
|
||||
android.graphics.Bitmap LoadBitmap(System.IO.Stream stream)
|
||||
{
|
||||
if (stream is Microsoft.Xna.Framework.TitleContainer.TitleStream titleStream)
|
||||
{
|
||||
var bitmap = android.graphics.BitmapFactory
|
||||
.decodeStream(titleStream.JavaStream);
|
||||
if ( bitmap == null
|
||||
|| bitmap.getConfig() != android.graphics.Bitmap.Config.ARGB_8888)
|
||||
{
|
||||
string reason = (bitmap == null) ? "unspecified error"
|
||||
: $"unsupported config '{bitmap.getConfig()}'";
|
||||
throw new System.BadImageFormatException(
|
||||
$"Load failed for bitmap image '{titleStream.Name}': {reason}");
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
throw new ArgumentException(stream?.GetType()?.ToString());
|
||||
}
|
||||
|
||||
|
||||
int[] CropAndScale(android.graphics.Bitmap bitmap,
|
||||
int newWidth, int newHeight, bool zoom,
|
||||
out int width, out int height, out int len)
|
||||
{
|
||||
int oldWidth = bitmap.getWidth();
|
||||
int oldHeight = bitmap.getHeight();
|
||||
bool scaleWidth = zoom ? (oldWidth < oldHeight) : (oldWidth > oldHeight);
|
||||
float scaleFactor = scaleWidth ? ((float) newWidth / (float) oldWidth)
|
||||
: ((float) newHeight / (float) oldHeight);
|
||||
if (zoom)
|
||||
{
|
||||
int x, y, w, h;
|
||||
if (scaleWidth)
|
||||
{
|
||||
x = 0;
|
||||
y = (int) (oldHeight / 2 - (newHeight / scaleFactor) / 2);
|
||||
w = oldWidth;
|
||||
h = (int) (newHeight / scaleFactor);
|
||||
}
|
||||
else
|
||||
{
|
||||
x = (int) (oldWidth / 2 - (newWidth / scaleFactor) / 2);
|
||||
y = 0;
|
||||
w = (int) (newWidth / scaleFactor);
|
||||
h = oldHeight;
|
||||
}
|
||||
bitmap = android.graphics.Bitmap.createBitmap(bitmap, x, y, w, h);
|
||||
}
|
||||
else
|
||||
{
|
||||
newWidth = (int) (oldWidth * scaleFactor);
|
||||
newHeight = (int) (oldHeight * scaleFactor);
|
||||
}
|
||||
|
||||
return GetPixels(android.graphics.Bitmap.createScaledBitmap(
|
||||
bitmap, newWidth, newHeight, false),
|
||||
out width, out height, out len);
|
||||
}
|
||||
|
||||
|
||||
int[] GetPixels(android.graphics.Bitmap bitmap,
|
||||
out int width, out int height, out int len)
|
||||
{
|
||||
int w = bitmap.getWidth();
|
||||
int h = bitmap.getHeight();
|
||||
var pixels = new int[w * h];
|
||||
bitmap.copyPixelsToBuffer(java.nio.IntBuffer.wrap(pixels));
|
||||
width = w;
|
||||
height = h;
|
||||
len = w * h * 4;
|
||||
return pixels;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// FNA3D_Image_Free
|
||||
//
|
||||
|
||||
public static void FNA3D_Image_Free(IntPtr mem)
|
||||
{
|
||||
// FNA calls this method after uploading the data returned by
|
||||
// ReadImageStream, so we can safely discard the reference
|
||||
ImagePixels.set(null);
|
||||
}
|
||||
|
||||
//
|
||||
// FNA3D_VerifySampler
|
||||
//
|
||||
|
||||
public static void FNA3D_VerifySampler(IntPtr device, int index, IntPtr texture,
|
||||
ref FNA3D_SamplerState sampler)
|
||||
{
|
||||
var samplerCopy = sampler;
|
||||
var renderer = Renderer.Get(device);
|
||||
|
||||
renderer.Send( () =>
|
||||
{
|
||||
var state = (State) renderer.UserData;
|
||||
var config = state.TextureConfigs[(int) texture];
|
||||
|
||||
GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + index);
|
||||
GLES20.glBindTexture(config[0], (int) texture);
|
||||
|
||||
GLES20.glTexParameteri(config[0], GLES30.GL_TEXTURE_MAX_LEVEL,
|
||||
config[2] - 1);
|
||||
GLES20.glTexParameteri(config[0], GLES30.GL_TEXTURE_BASE_LEVEL,
|
||||
samplerCopy.maxMipLevel);
|
||||
|
||||
GLES20.glTexParameteri(config[0], GLES20.GL_TEXTURE_WRAP_S,
|
||||
TextureWrapMode[(int) samplerCopy.addressU]);
|
||||
GLES20.glTexParameteri(config[0], GLES20.GL_TEXTURE_WRAP_T,
|
||||
TextureWrapMode[(int) samplerCopy.addressV]);
|
||||
if (config[0] == GLES30.GL_TEXTURE_3D)
|
||||
{
|
||||
GLES20.glTexParameteri(config[0], GLES30.GL_TEXTURE_WRAP_R,
|
||||
TextureWrapMode[(int) samplerCopy.addressW]);
|
||||
}
|
||||
|
||||
int magIndex = (int) samplerCopy.filter * 3;
|
||||
int minIndex = magIndex + (config[2] <= 1 ? 1 : 2);
|
||||
|
||||
GLES20.glTexParameteri(config[0], GLES20.GL_TEXTURE_MAG_FILTER,
|
||||
TextureFilterMode[magIndex]);
|
||||
GLES20.glTexParameteri(config[0], GLES20.GL_TEXTURE_MIN_FILTER,
|
||||
TextureFilterMode[minIndex]);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// SurfaceFormatToTextureFormat
|
||||
//
|
||||
|
||||
static int[] SurfaceFormatToTextureFormat = new int[]
|
||||
{
|
||||
GLES20.GL_RGBA, // SurfaceFormat.Color
|
||||
GLES20.GL_RGB, // SurfaceFormat.Bgr565
|
||||
GLES20.GL_ZERO, // was GL_BGRA // SurfaceFormat.Bgra5551
|
||||
GLES20.GL_ZERO, // was GL_BGRA // SurfaceFormat.Bgra4444
|
||||
GLES20.GL_COMPRESSED_TEXTURE_FORMATS, // SurfaceFormat.Dxt1
|
||||
GLES20.GL_COMPRESSED_TEXTURE_FORMATS, // SurfaceFormat.Dxt3
|
||||
GLES20.GL_COMPRESSED_TEXTURE_FORMATS, // SurfaceFormat.Dxt5
|
||||
GLES30.GL_RG, // SurfaceFormat.NormalizedByte2
|
||||
GLES20.GL_RGBA, // SurfaceFormat.NormalizedByte4
|
||||
GLES20.GL_RGBA, // SurfaceFormat.Rgba1010102
|
||||
GLES30.GL_RG, // SurfaceFormat.Rg32
|
||||
GLES20.GL_RGBA, // SurfaceFormat.Rgba64
|
||||
GLES20.GL_ALPHA, // SurfaceFormat.Alpha8
|
||||
GLES30.GL_RED, // SurfaceFormat.Single
|
||||
GLES30.GL_RG, // SurfaceFormat.Vector2
|
||||
GLES20.GL_RGBA, // SurfaceFormat.Vector4
|
||||
GLES30.GL_RED, // SurfaceFormat.HalfSingle
|
||||
GLES30.GL_RG, // SurfaceFormat.HalfVector2
|
||||
GLES20.GL_RGBA, // SurfaceFormat.HalfVector4
|
||||
GLES20.GL_RGBA, // SurfaceFormat.HdrBlendable
|
||||
GLES20.GL_ZERO, // was GL_BGRA // SurfaceFormat.ColorBgraEXT
|
||||
};
|
||||
|
||||
//
|
||||
// SurfaceFormatToTextureInternalFormat
|
||||
//
|
||||
|
||||
static int[] SurfaceFormatToTextureInternalFormat = new int[]
|
||||
{
|
||||
GLES30.GL_RGBA8, // SurfaceFormat.Color
|
||||
GLES30.GL_RGB8, // SurfaceFormat.Bgr565
|
||||
GLES20.GL_RGB5_A1, // SurfaceFormat.Bgra5551
|
||||
GLES20.GL_RGBA4, // SurfaceFormat.Bgra4444
|
||||
GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, // SurfaceFormat.Dxt1
|
||||
GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, // SurfaceFormat.Dxt3
|
||||
GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, // SurfaceFormat.Dxt5
|
||||
GLES30.GL_RG8, // SurfaceFormat.NormalizedByte2
|
||||
GLES30.GL_RGBA8, // SurfaceFormat.NormalizedByte4
|
||||
GLES30.GL_RGB10_A2, // was ..._A2_EXT // SurfaceFormat.Rgba1010102
|
||||
GLES20.GL_ZERO, // was GL_RG16 // SurfaceFormat.Rg32
|
||||
GLES20.GL_ZERO, // was GL_RGBA16, // SurfaceFormat.Rgba64
|
||||
GLES20.GL_ALPHA, // SurfaceFormat.Alpha8
|
||||
GLES30.GL_R32F, // SurfaceFormat.Single
|
||||
GLES30.GL_RG32F, // SurfaceFormat.Vector2
|
||||
GLES30.GL_RGBA32F, // SurfaceFormat.Vector4
|
||||
GLES30.GL_R16F, // SurfaceFormat.HalfSingle
|
||||
GLES30.GL_RG16F, // SurfaceFormat.HalfVector2
|
||||
GLES30.GL_RGBA16F, // SurfaceFormat.HalfVector4
|
||||
GLES30.GL_RGBA16F, // SurfaceFormat.HdrBlendable
|
||||
GLES30.GL_RGBA8, // SurfaceFormat.ColorBgraEXT
|
||||
};
|
||||
|
||||
//
|
||||
// SurfaceFormatToTextureDataType
|
||||
//
|
||||
|
||||
static int[] SurfaceFormatToTextureDataType = new int[]
|
||||
{
|
||||
GLES20.GL_UNSIGNED_BYTE, // SurfaceFormat.Color
|
||||
GLES20.GL_UNSIGNED_SHORT_5_6_5, // SurfaceFormat.Bgr565
|
||||
GLES20.GL_ZERO, // was ..._5_5_5_1_REV // SurfaceFormat.Bgra5551
|
||||
GLES20.GL_ZERO, // was ..._4_4_4_4_REV // SurfaceFormat.Bgra4444
|
||||
GLES20.GL_ZERO, // not applicable // SurfaceFormat.Dxt1
|
||||
GLES20.GL_ZERO, // not applicable // SurfaceFormat.Dxt3
|
||||
GLES20.GL_ZERO, // not applicable // SurfaceFormat.Dxt5
|
||||
GLES20.GL_BYTE, // SurfaceFormat.NormalizedByte2
|
||||
GLES20.GL_BYTE, // SurfaceFormat.NormalizedByte4
|
||||
GLES30.GL_UNSIGNED_INT_2_10_10_10_REV, // SurfaceFormat.Rgba1010102
|
||||
GLES20.GL_UNSIGNED_SHORT, // SurfaceFormat.Rg32
|
||||
GLES20.GL_UNSIGNED_SHORT, // SurfaceFormat.Rgba64
|
||||
GLES20.GL_UNSIGNED_BYTE, // SurfaceFormat.Alpha8
|
||||
GLES20.GL_FLOAT, // SurfaceFormat.Single
|
||||
GLES20.GL_FLOAT, // SurfaceFormat.Vector2
|
||||
GLES20.GL_FLOAT, // SurfaceFormat.Vector4
|
||||
GLES30.GL_HALF_FLOAT, // SurfaceFormat.HalfSingle
|
||||
GLES30.GL_HALF_FLOAT, // SurfaceFormat.HalfVector2
|
||||
GLES30.GL_HALF_FLOAT, // SurfaceFormat.HalfVector4
|
||||
GLES30.GL_HALF_FLOAT, // SurfaceFormat.HdrBlendable
|
||||
GLES20.GL_UNSIGNED_BYTE // SurfaceFormat.ColorBgraEXT
|
||||
};
|
||||
|
||||
// from ubiquitous extension EXT_texture_compression_s3tc
|
||||
const int GL_COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1;
|
||||
const int GL_COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2;
|
||||
const int GL_COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3;
|
||||
|
||||
//
|
||||
// SurfaceFormatToTextureDataSize
|
||||
//
|
||||
|
||||
static int[] SurfaceFormatToTextureDataSize = new int[]
|
||||
{
|
||||
4, // SurfaceFormat.Color
|
||||
2, // SurfaceFormat.Bgr565
|
||||
2, // SurfaceFormat.Bgra5551
|
||||
2, // SurfaceFormat.Bgra4444
|
||||
8, // SurfaceFormat.Dxt1
|
||||
16, // SurfaceFormat.Dxt3
|
||||
16, // SurfaceFormat.Dxt5
|
||||
2, // SurfaceFormat.NormalizedByte2
|
||||
4, // SurfaceFormat.NormalizedByte4
|
||||
4, // SurfaceFormat.Rgba1010102
|
||||
4, // SurfaceFormat.Rg32
|
||||
8, // SurfaceFormat.Rgba64
|
||||
1, // SurfaceFormat.Alpha8
|
||||
4, // SurfaceFormat.Single
|
||||
8, // SurfaceFormat.Vector2
|
||||
16, // SurfaceFormat.Vector4
|
||||
2, // SurfaceFormat.HalfSingle
|
||||
4, // SurfaceFormat.HalfVector2
|
||||
8, // SurfaceFormat.HalfVector4
|
||||
8, // SurfaceFormat.HdrBlendable
|
||||
4, // SurfaceFormat.ColorBgraEXT
|
||||
};
|
||||
|
||||
//
|
||||
// TextureWrapMode
|
||||
//
|
||||
|
||||
static int[] TextureWrapMode = new int[]
|
||||
{
|
||||
GLES20.GL_REPEAT, // TextureAddressMode.Wrap
|
||||
GLES20.GL_CLAMP_TO_EDGE, // TextureAddressMode.Clamp
|
||||
GLES20.GL_MIRRORED_REPEAT // TextureAddressMode.Mirror
|
||||
};
|
||||
|
||||
//
|
||||
// TextureFilterMode
|
||||
//
|
||||
|
||||
static int[] TextureFilterMode = new int[]
|
||||
{
|
||||
// TextureFilter.Linear: mag filter, min filter, mipmap filter
|
||||
GLES20.GL_LINEAR, GLES20.GL_LINEAR, GLES20.GL_LINEAR_MIPMAP_LINEAR,
|
||||
// TextureFilter.Point
|
||||
GLES20.GL_NEAREST, GLES20.GL_NEAREST, GLES20.GL_NEAREST_MIPMAP_NEAREST,
|
||||
// TextureFilter.Anisotropic
|
||||
GLES20.GL_LINEAR, GLES20.GL_LINEAR, GLES20.GL_LINEAR_MIPMAP_LINEAR,
|
||||
// TextureFilter.LinearMipPoint
|
||||
GLES20.GL_LINEAR, GLES20.GL_LINEAR, GLES20.GL_LINEAR_MIPMAP_NEAREST,
|
||||
// TextureFilter.PointMipLinear
|
||||
GLES20.GL_NEAREST, GLES20.GL_NEAREST, GLES20.GL_NEAREST_MIPMAP_LINEAR,
|
||||
// TextureFilter.MinLinearMagPointMipLinear
|
||||
GLES20.GL_NEAREST, GLES20.GL_LINEAR, GLES20.GL_LINEAR_MIPMAP_LINEAR,
|
||||
// TextureFilter.MinLinearMagPointMipPoint
|
||||
GLES20.GL_NEAREST, GLES20.GL_LINEAR, GLES20.GL_LINEAR_MIPMAP_NEAREST,
|
||||
// TextureFilter.MinPointMagLinearMipLinear
|
||||
GLES20.GL_LINEAR, GLES20.GL_NEAREST, GLES20.GL_NEAREST_MIPMAP_LINEAR,
|
||||
// TextureFilter.MinPointMagLinearMipPoint
|
||||
GLES20.GL_LINEAR, GLES20.GL_NEAREST, GLES20.GL_NEAREST_MIPMAP_NEAREST,
|
||||
};
|
||||
|
||||
//
|
||||
// FNA3D_SamplerState
|
||||
//
|
||||
|
||||
public struct FNA3D_SamplerState
|
||||
{
|
||||
public TextureFilter filter;
|
||||
public TextureAddressMode addressU;
|
||||
public TextureAddressMode addressV;
|
||||
public TextureAddressMode addressW;
|
||||
public float mipMapLevelOfDetailBias;
|
||||
public int maxAnisotropy;
|
||||
public int maxMipLevel;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// data
|
||||
//
|
||||
|
||||
private static readonly java.lang.ThreadLocal ImagePixels = new java.lang.ThreadLocal();
|
||||
|
||||
//
|
||||
// state
|
||||
//
|
||||
|
||||
private partial class State
|
||||
{
|
||||
// texture config array:
|
||||
// #0 - target type
|
||||
// #1 - SurfaceFormat
|
||||
// #2 - levels count
|
||||
public Dictionary<int, int[]> TextureConfigs = new Dictionary<int, int[]>();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
172
BNA/src/FNAPlatform.cs
Normal file
172
BNA/src/FNAPlatform.cs
Normal file
@ -0,0 +1,172 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using Microsoft.Xna.Framework.Input.Touch;
|
||||
#pragma warning disable 0436
|
||||
|
||||
namespace Microsoft.Xna.Framework
|
||||
{
|
||||
|
||||
public static class FNAPlatform
|
||||
{
|
||||
//
|
||||
// CreateWindow
|
||||
//
|
||||
|
||||
private static GameWindow CreateWindowImpl() => GameRunner.Singleton;
|
||||
|
||||
public delegate GameWindow CreateWindowFunc();
|
||||
public static readonly CreateWindowFunc CreateWindow = CreateWindowImpl;
|
||||
|
||||
//
|
||||
// DisposeWindow
|
||||
//
|
||||
|
||||
private static void DisposeWindowImpl(GameWindow window) { }
|
||||
|
||||
public delegate void DisposeWindowFunc(GameWindow window);
|
||||
public static readonly DisposeWindowFunc DisposeWindow = DisposeWindowImpl;
|
||||
|
||||
//
|
||||
// SupportsOrientationChanges
|
||||
//
|
||||
|
||||
private static bool SupportsOrientationChangesImpl() => true;
|
||||
|
||||
public delegate bool SupportsOrientationChangesFunc();
|
||||
public static readonly SupportsOrientationChangesFunc SupportsOrientationChanges = SupportsOrientationChangesImpl;
|
||||
|
||||
//
|
||||
// GetGraphicsAdapters
|
||||
//
|
||||
|
||||
private static GraphicsAdapter[] GetGraphicsAdaptersImpl()
|
||||
{
|
||||
var bounds = GameRunner.Singleton.ClientBounds;
|
||||
var modesList = new List<DisplayMode>();
|
||||
var theMode = new DisplayMode(bounds.Width, bounds.Height, SurfaceFormat.Color);
|
||||
modesList.Add(theMode);
|
||||
var modesCollection = new DisplayModeCollection(modesList);
|
||||
var name = "Android Surface";
|
||||
var theAdapter = new GraphicsAdapter(modesCollection, name, name);
|
||||
return new GraphicsAdapter[] { (GraphicsAdapter) (object) theAdapter };
|
||||
}
|
||||
|
||||
public delegate GraphicsAdapter[] GetGraphicsAdaptersFunc();
|
||||
public static readonly GetGraphicsAdaptersFunc GetGraphicsAdapters = GetGraphicsAdaptersImpl;
|
||||
|
||||
//
|
||||
// RegisterGame
|
||||
//
|
||||
|
||||
public static GraphicsAdapter RegisterGameImpl(Game game) => GraphicsAdapter.Adapters[0];
|
||||
|
||||
public delegate GraphicsAdapter RegisterGameFunc(Game game);
|
||||
public static readonly RegisterGameFunc RegisterGame = RegisterGameImpl;
|
||||
|
||||
//
|
||||
// GetTouchCapabilities
|
||||
//
|
||||
|
||||
public static TouchPanelCapabilities GetTouchCapabilitiesImpl()
|
||||
=> (TouchPanelCapabilities) (object) new TouchPanelCapabilities(true, 4);
|
||||
|
||||
public delegate TouchPanelCapabilities GetTouchCapabilitiesFunc();
|
||||
public static readonly GetTouchCapabilitiesFunc GetTouchCapabilities = GetTouchCapabilitiesImpl;
|
||||
|
||||
//
|
||||
// NeedsPlatformMainLoop
|
||||
//
|
||||
|
||||
public static bool NeedsPlatformMainLoopImpl() => true;
|
||||
|
||||
public delegate bool NeedsPlatformMainLoopFunc();
|
||||
public static readonly NeedsPlatformMainLoopFunc NeedsPlatformMainLoop = NeedsPlatformMainLoopImpl;
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
public static void RunPlatformMainLoopImpl(Game game)
|
||||
=> GameRunner.Singleton.MainLoop(game);
|
||||
|
||||
public delegate void RunPlatformMainLoopFunc(Game game);
|
||||
public static readonly RunPlatformMainLoopFunc RunPlatformMainLoop = RunPlatformMainLoopImpl;
|
||||
|
||||
//
|
||||
// UnregisterGame
|
||||
//
|
||||
|
||||
public static void UnregisterGameImpl(Game game) { }
|
||||
|
||||
public delegate void UnregisterGameFunc(Game game);
|
||||
public static readonly UnregisterGameFunc UnregisterGame = UnregisterGameImpl;
|
||||
|
||||
//
|
||||
// OnIsMouseVisibleChanged
|
||||
//
|
||||
|
||||
public static void OnIsMouseVisibleChangedImpl(bool visible) { }
|
||||
|
||||
public delegate void OnIsMouseVisibleChangedFunc(bool visible);
|
||||
public static readonly OnIsMouseVisibleChangedFunc OnIsMouseVisibleChanged = OnIsMouseVisibleChangedImpl;
|
||||
|
||||
//
|
||||
// GetNumTouchFingers
|
||||
//
|
||||
|
||||
public static int GetNumTouchFingersImpl() => Mouse.NumTouchFingers.get();
|
||||
|
||||
public delegate int GetNumTouchFingersFunc();
|
||||
public static readonly GetNumTouchFingersFunc GetNumTouchFingers = GetNumTouchFingersImpl;
|
||||
|
||||
//
|
||||
// GetStorageRoot
|
||||
//
|
||||
|
||||
public static string GetStorageRootImpl()
|
||||
{
|
||||
var s = GameRunner.Singleton.Activity.getFilesDir();
|
||||
return s.getAbsolutePath();
|
||||
}
|
||||
|
||||
public delegate string GetStorageRootFunc();
|
||||
public static readonly GetStorageRootFunc GetStorageRoot = GetStorageRootImpl;
|
||||
|
||||
//
|
||||
// GetDriveInfo
|
||||
//
|
||||
|
||||
public static System.IO.DriveInfo GetDriveInfoImpl(string storageRoot) => null;
|
||||
|
||||
public delegate System.IO.DriveInfo GetDriveInfoFunc(string storageRoot);
|
||||
public static readonly GetDriveInfoFunc GetDriveInfo = GetDriveInfoImpl;
|
||||
|
||||
//
|
||||
// GetMouseState
|
||||
//
|
||||
|
||||
/*public static void GetMouseStateImpl(IntPtr window, out int x, out int y, out ButtonState left, out ButtonState middle, out ButtonState right, out ButtonState x1, out ButtonState x2)
|
||||
{
|
||||
x = 0;
|
||||
y = 0;
|
||||
left = ButtonState.Released;
|
||||
middle = ButtonState.Released;
|
||||
right = ButtonState.Released;
|
||||
x1 = ButtonState.Released;
|
||||
x2 = ButtonState.Released;
|
||||
}
|
||||
|
||||
public delegate void GetMouseStateFunc(IntPtr window, out int x, out int y, out ButtonState left, out ButtonState middle, out ButtonState right, out ButtonState x1, out ButtonState x2);
|
||||
public static readonly GetMouseStateFunc GetMouseState = GetMouseStateImpl;*/
|
||||
|
||||
//
|
||||
// TextInputCharacters
|
||||
//
|
||||
|
||||
public static readonly char[] TextInputCharacters = new char[0];
|
||||
}
|
||||
|
||||
}
|
413
BNA/src/GameRunner.cs
Normal file
413
BNA/src/GameRunner.cs
Normal file
@ -0,0 +1,413 @@
|
||||
|
||||
using System;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using GL10 = javax.microedition.khronos.opengles.GL10;
|
||||
using EGLConfig = javax.microedition.khronos.egl.EGLConfig;
|
||||
#pragma warning disable 0436
|
||||
|
||||
namespace Microsoft.Xna.Framework
|
||||
{
|
||||
|
||||
public class GameRunner : GameWindow, IServiceProvider, java.lang.Runnable
|
||||
|
||||
{
|
||||
|
||||
private Activity activity;
|
||||
private System.Collections.Hashtable dict;
|
||||
private int clientWidth, clientHeight;
|
||||
private Rectangle clientBounds;
|
||||
private bool recreateActivity;
|
||||
|
||||
private java.util.concurrent.atomic.AtomicInteger inModal;
|
||||
private java.util.concurrent.atomic.AtomicInteger shouldPause;
|
||||
private java.util.concurrent.atomic.AtomicInteger shouldResume;
|
||||
private java.util.concurrent.atomic.AtomicInteger shouldExit;
|
||||
private java.util.concurrent.atomic.AtomicInteger shouldEvents;
|
||||
private android.os.ConditionVariable waitForPause;
|
||||
private android.os.ConditionVariable waitForResume;
|
||||
|
||||
private static readonly java.lang.ThreadLocal selfTls =
|
||||
new java.lang.ThreadLocal();
|
||||
|
||||
private const int CONFIG_EVENT = 1;
|
||||
private const int TOUCH_EVENT = 2;
|
||||
|
||||
//
|
||||
// constructor
|
||||
//
|
||||
|
||||
public GameRunner(Activity activity)
|
||||
{
|
||||
this.activity = activity;
|
||||
|
||||
UpdateConfiguration(false);
|
||||
|
||||
inModal = new java.util.concurrent.atomic.AtomicInteger();
|
||||
shouldPause = new java.util.concurrent.atomic.AtomicInteger();
|
||||
shouldResume = new java.util.concurrent.atomic.AtomicInteger();
|
||||
shouldExit = new java.util.concurrent.atomic.AtomicInteger();
|
||||
shouldEvents = new java.util.concurrent.atomic.AtomicInteger();
|
||||
waitForPause = new android.os.ConditionVariable();
|
||||
waitForResume = new android.os.ConditionVariable();
|
||||
}
|
||||
|
||||
//
|
||||
// Singleton and Activity properties
|
||||
//
|
||||
|
||||
public static GameRunner Singleton
|
||||
=> (GameRunner) selfTls.get()
|
||||
?? throw new System.InvalidOperationException("not main thread");
|
||||
|
||||
public android.app.Activity Activity => activity;
|
||||
|
||||
//
|
||||
// InModal
|
||||
//
|
||||
|
||||
public bool InModal
|
||||
{
|
||||
get => inModal.get() != 0 ? true : false;
|
||||
set => inModal.set(value ? 1 : 0);
|
||||
}
|
||||
|
||||
//
|
||||
// Thread run() method
|
||||
//
|
||||
|
||||
[java.attr.RetainName]
|
||||
public void run()
|
||||
{
|
||||
selfTls.set(this);
|
||||
|
||||
RunMainMethod();
|
||||
|
||||
shouldExit.set(1);
|
||||
waitForPause.open();
|
||||
|
||||
activity.FinishAndRestart(recreateActivity);
|
||||
}
|
||||
|
||||
//
|
||||
// RunMainMethod
|
||||
//
|
||||
|
||||
private void RunMainMethod()
|
||||
{
|
||||
try
|
||||
{
|
||||
CallMainMethod(GetMainClass());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
GameRunner.Log("========================================");
|
||||
GameRunner.Log(e.ToString());
|
||||
GameRunner.Log("========================================");
|
||||
System.Windows.Forms.MessageBox.Show(e.ToString());
|
||||
}
|
||||
|
||||
|
||||
Type GetMainClass()
|
||||
{
|
||||
Type clsType = null;
|
||||
|
||||
var clsName = activity.GetMetaAttr("main.class", true);
|
||||
if (clsName != null)
|
||||
{
|
||||
if (clsName[0] == '.')
|
||||
clsName = activity.getPackageName() + clsName;
|
||||
|
||||
clsType = System.Type.GetType(clsName, false, true);
|
||||
}
|
||||
|
||||
if (clsType == null)
|
||||
{
|
||||
throw new Exception($"main class '{clsName}' not found");
|
||||
}
|
||||
|
||||
return clsType;
|
||||
}
|
||||
|
||||
|
||||
void CallMainMethod(Type mainClass)
|
||||
{
|
||||
var method = mainClass.GetMethod("Main");
|
||||
if (method.IsStatic)
|
||||
{
|
||||
method.Invoke(null, new object[method.GetParameters().Length]);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"missing or invalid method 'Main' in type '{mainClass}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// MainLoop
|
||||
//
|
||||
|
||||
public void MainLoop(Game game)
|
||||
{
|
||||
int pauseCount = 0;
|
||||
|
||||
while (game.RunApplication)
|
||||
{
|
||||
|
||||
//
|
||||
// pause game if required
|
||||
//
|
||||
|
||||
if (shouldPause.get() != pauseCount)
|
||||
{
|
||||
pauseCount = shouldPause.incrementAndGet();
|
||||
|
||||
// FNA.Game calls game.OnDeactivated()
|
||||
game.IsActive = false;
|
||||
|
||||
if (shouldExit.get() != 0)
|
||||
break;
|
||||
|
||||
PauseGame(false);
|
||||
|
||||
shouldResume.incrementAndGet();
|
||||
waitForPause.open();
|
||||
waitForResume.block();
|
||||
waitForResume.close();
|
||||
|
||||
if (! ResumeGame(false))
|
||||
break;
|
||||
|
||||
// on resume from pause, reset input state
|
||||
Microsoft.Xna.Framework.Input.Mouse.WindowHandle =
|
||||
Microsoft.Xna.Framework.Input.Mouse.WindowHandle;
|
||||
|
||||
// FNA.Game calls game.OnActivated()
|
||||
game.IsActive = true;
|
||||
}
|
||||
|
||||
//
|
||||
// handle various events as indicated
|
||||
//
|
||||
|
||||
int eventBits = shouldEvents.get();
|
||||
if (eventBits != 0)
|
||||
{
|
||||
while (! shouldEvents.compareAndSet(eventBits, 0))
|
||||
eventBits = shouldEvents.get();
|
||||
|
||||
if ((eventBits & CONFIG_EVENT) != 0)
|
||||
UpdateConfiguration(true);
|
||||
|
||||
if ((eventBits & TOUCH_EVENT) != 0)
|
||||
{
|
||||
Microsoft.Xna.Framework.Input.Mouse
|
||||
.HandleEvents(clientWidth, clientHeight);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// run one game frame
|
||||
//
|
||||
|
||||
game.Tick();
|
||||
}
|
||||
|
||||
InModal = true;
|
||||
game.RunApplication = false;
|
||||
}
|
||||
|
||||
//
|
||||
// PauseGame
|
||||
//
|
||||
|
||||
public void PauseGame(bool enterModal)
|
||||
{
|
||||
Renderer.Pause(activity);
|
||||
if (enterModal)
|
||||
InModal = true;
|
||||
}
|
||||
|
||||
//
|
||||
// ResumeGame
|
||||
//
|
||||
|
||||
public bool ResumeGame(bool leaveModal)
|
||||
{
|
||||
if (leaveModal)
|
||||
InModal = false;
|
||||
|
||||
if (shouldExit.get() != 0)
|
||||
return false;
|
||||
|
||||
if (! Renderer.CanResume(activity))
|
||||
{
|
||||
// restart because we lost the GL context and state
|
||||
recreateActivity = true;
|
||||
|
||||
// in case we are called from MessageBox, make sure
|
||||
// the main loop sees that we need to exit.
|
||||
shouldExit.set(1);
|
||||
shouldPause.incrementAndGet();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Callbacks from Android activity UI thread:
|
||||
// onPause, onResume, onDestroy, onTouchEvent
|
||||
//
|
||||
|
||||
public void ActivityPause()
|
||||
{
|
||||
if (! InModal)
|
||||
{
|
||||
shouldPause.incrementAndGet();
|
||||
waitForPause.block();
|
||||
if (shouldExit.get() == 0)
|
||||
waitForPause.close();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// ActivityResume
|
||||
//
|
||||
|
||||
public void ActivityResume()
|
||||
{
|
||||
if (shouldResume.compareAndSet(1, 0))
|
||||
waitForResume.open();
|
||||
}
|
||||
|
||||
//
|
||||
// ActivityDestroy
|
||||
//
|
||||
|
||||
public void ActivityDestroy()
|
||||
{
|
||||
shouldExit.set(1);
|
||||
ActivityResume();
|
||||
ActivityPause();
|
||||
}
|
||||
|
||||
//
|
||||
// ActivityTouch
|
||||
//
|
||||
|
||||
public void ActivityTouch(android.view.MotionEvent motionEvent)
|
||||
{
|
||||
Microsoft.Xna.Framework.Input.Mouse.QueueEvent(motionEvent);
|
||||
for (;;)
|
||||
{
|
||||
int v = shouldEvents.get();
|
||||
if (shouldEvents.compareAndSet(v, v | TOUCH_EVENT))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// OnSurfaceChanged
|
||||
//
|
||||
|
||||
public void OnSurfaceChanged()
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
int v = shouldEvents.get();
|
||||
if (shouldEvents.compareAndSet(v, v | CONFIG_EVENT))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// UpdateConfiguration
|
||||
//
|
||||
|
||||
void UpdateConfiguration(bool withCallback)
|
||||
{
|
||||
var metrics = new android.util.DisplayMetrics();
|
||||
activity.getWindowManager().getDefaultDisplay().getRealMetrics(metrics);
|
||||
|
||||
clientWidth = metrics.widthPixels;
|
||||
clientHeight = metrics.heightPixels;
|
||||
clientBounds = new Rectangle(0, 0, clientWidth, clientHeight);
|
||||
|
||||
if (dict == null)
|
||||
dict = new System.Collections.Hashtable();
|
||||
|
||||
// int dpi - pixels per inch
|
||||
dict["dpi"] = (int) ((metrics.xdpi + metrics.ydpi) * 0.5f);
|
||||
|
||||
if (withCallback)
|
||||
{
|
||||
OnClientSizeChanged();
|
||||
OnOrientationChanged();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// GetService
|
||||
//
|
||||
|
||||
public object GetService(Type type)
|
||||
{
|
||||
if (object.ReferenceEquals(type, typeof(System.Collections.IDictionary)))
|
||||
return dict.Clone();
|
||||
return null;
|
||||
}
|
||||
|
||||
//
|
||||
// GameWindow interface
|
||||
//
|
||||
|
||||
public override Rectangle ClientBounds => clientBounds;
|
||||
|
||||
public override string ScreenDeviceName => "Android";
|
||||
|
||||
public override bool AllowUserResizing { get => false; set { } }
|
||||
|
||||
public override void SetSupportedOrientations(DisplayOrientation orientations)
|
||||
=> CurrentOrientation = orientations;
|
||||
|
||||
public override DisplayOrientation CurrentOrientation
|
||||
{
|
||||
get => (clientWidth < clientHeight) ? DisplayOrientation.Portrait
|
||||
: DisplayOrientation.LandscapeLeft;
|
||||
set
|
||||
{
|
||||
bool portrait = 0 != (value & DisplayOrientation.Portrait);
|
||||
bool landscape = 0 != (value & ( DisplayOrientation.LandscapeLeft
|
||||
| DisplayOrientation.LandscapeRight));
|
||||
int r;
|
||||
if (portrait && (! landscape))
|
||||
r = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT;
|
||||
else if (landscape && (! portrait))
|
||||
r = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE;
|
||||
else
|
||||
r = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_FULL_USER;
|
||||
activity.setRequestedOrientation(r);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Log(string s) => Microsoft.Xna.Framework.Activity.Log(s);
|
||||
|
||||
//
|
||||
// not implemented
|
||||
//
|
||||
|
||||
public override IntPtr Handle => IntPtr.Zero;
|
||||
|
||||
public override void SetTitle(string title) { }
|
||||
|
||||
public override void BeginScreenDeviceChange(bool willBeFullScreen) { }
|
||||
|
||||
public override void EndScreenDeviceChange(string screenDeviceName,
|
||||
int clientWidth, int clientHeight) { }
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
228
BNA/src/Import.cs
Normal file
228
BNA/src/Import.cs
Normal file
@ -0,0 +1,228 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
#pragma warning disable 0436
|
||||
|
||||
namespace Microsoft.Xna.Framework
|
||||
{
|
||||
|
||||
//
|
||||
// GameWindow
|
||||
//
|
||||
|
||||
[java.attr.Discard] // discard in output
|
||||
public abstract class GameWindow
|
||||
{
|
||||
public abstract IntPtr Handle { get; }
|
||||
public abstract bool AllowUserResizing { get; set; }
|
||||
public abstract Rectangle ClientBounds { get; }
|
||||
public abstract string ScreenDeviceName { get; }
|
||||
public abstract void SetSupportedOrientations(DisplayOrientation orientations);
|
||||
public abstract DisplayOrientation CurrentOrientation { get; set; }
|
||||
public abstract void BeginScreenDeviceChange(bool willBeFullScreen);
|
||||
public abstract void EndScreenDeviceChange(string screenDeviceName, int clientWidth, int clientHeight);
|
||||
public abstract void SetTitle(string title);
|
||||
protected void OnActivated() { }
|
||||
protected void OnDeactivated() { }
|
||||
protected void OnPaint() { }
|
||||
protected void OnScreenDeviceNameChanged() { }
|
||||
protected void OnClientSizeChanged() { }
|
||||
protected void OnOrientationChanged() { }
|
||||
public static readonly int DefaultClientWidth = 800;
|
||||
public static readonly int DefaultClientHeight = 600;
|
||||
}
|
||||
|
||||
//
|
||||
// Game
|
||||
//
|
||||
|
||||
[java.attr.Discard] // discard in output
|
||||
public abstract class Game
|
||||
{
|
||||
public bool IsActive { get; set; }
|
||||
public bool RunApplication;
|
||||
public abstract void Tick();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
namespace Microsoft.Xna.Framework.Graphics
|
||||
{
|
||||
|
||||
//
|
||||
// GraphicsDevice
|
||||
//
|
||||
|
||||
[java.attr.Discard] // discard in output
|
||||
public class GraphicsDevice
|
||||
{
|
||||
public readonly IntPtr GLDevice;
|
||||
public TextureCollection Textures { get; }
|
||||
}
|
||||
|
||||
//
|
||||
// DisplayMode
|
||||
//
|
||||
|
||||
[java.attr.Discard] // discard in output
|
||||
public class DisplayMode
|
||||
{
|
||||
public DisplayMode(int width, int height, SurfaceFormat format) { }
|
||||
}
|
||||
|
||||
//
|
||||
// DisplayModeCollection
|
||||
//
|
||||
|
||||
[java.attr.Discard] // discard in output
|
||||
public class DisplayModeCollection
|
||||
{
|
||||
public DisplayModeCollection(List<DisplayMode> setmodes) { }
|
||||
}
|
||||
|
||||
//
|
||||
// GraphicsAdapter
|
||||
//
|
||||
|
||||
[java.attr.Discard] // discard in output
|
||||
public sealed class GraphicsAdapter
|
||||
{
|
||||
public GraphicsAdapter(DisplayModeCollection modes, string name, string description) { }
|
||||
public static ReadOnlyCollection<GraphicsAdapter> Adapters => null;
|
||||
}
|
||||
|
||||
//
|
||||
// GraphicsResource
|
||||
//
|
||||
|
||||
[java.attr.Discard] // discard in output
|
||||
public abstract class GraphicsResource
|
||||
{
|
||||
public GraphicsDevice GraphicsDevice { get; set; }
|
||||
protected virtual void Dispose(bool disposing) { }
|
||||
public virtual bool IsDisposed => false;
|
||||
}
|
||||
|
||||
//
|
||||
// SpriteBatch
|
||||
//
|
||||
|
||||
[java.attr.Discard] // discard in output
|
||||
public class SpriteBatch
|
||||
{
|
||||
public struct VertexPositionColorTexture4
|
||||
{
|
||||
public Vector3 Position0;
|
||||
public Color Color0;
|
||||
public Vector2 TextureCoordinate0;
|
||||
|
||||
public Vector3 Position1;
|
||||
public Color Color1;
|
||||
public Vector2 TextureCoordinate1;
|
||||
|
||||
public Vector3 Position2;
|
||||
public Color Color2;
|
||||
public Vector2 TextureCoordinate2;
|
||||
|
||||
public Vector3 Position3;
|
||||
public Color Color3;
|
||||
public Vector2 TextureCoordinate3;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// EffectParameterCollection
|
||||
//
|
||||
|
||||
[java.attr.Discard] // discard in output
|
||||
public class EffectParameterCollection
|
||||
{
|
||||
public EffectParameterCollection(List<EffectParameter> value) { }
|
||||
public EffectParameter this[int index] => null;
|
||||
public int Count { get; }
|
||||
}
|
||||
|
||||
//
|
||||
// EffectPass
|
||||
//
|
||||
|
||||
[java.attr.Discard] // discard in output
|
||||
public sealed class EffectPass
|
||||
{
|
||||
public EffectPass(string name, EffectAnnotationCollection annotations,
|
||||
Effect parent, IntPtr technique, uint passIndex) { }
|
||||
}
|
||||
|
||||
//
|
||||
// EffectPassCollection
|
||||
//
|
||||
|
||||
[java.attr.Discard] // discard in output
|
||||
public class EffectPassCollection
|
||||
{
|
||||
public EffectPassCollection(List<EffectPass> value) { }
|
||||
}
|
||||
|
||||
//
|
||||
// EffectTechnique
|
||||
//
|
||||
|
||||
[java.attr.Discard] // discard in output
|
||||
public sealed class EffectTechnique
|
||||
{
|
||||
public EffectTechnique(string name, IntPtr pointer, EffectPassCollection passes,
|
||||
EffectAnnotationCollection annotations) { }
|
||||
public string Name { get; }
|
||||
}
|
||||
|
||||
//
|
||||
// EffectTechniqueCollection
|
||||
//
|
||||
|
||||
[java.attr.Discard] // discard in output
|
||||
public class EffectTechniqueCollection
|
||||
{
|
||||
public EffectTechniqueCollection(List<EffectTechnique> value) { }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
namespace Microsoft.Xna.Framework.Input
|
||||
{
|
||||
[java.attr.Discard] // discard in output
|
||||
public struct MouseState
|
||||
{
|
||||
public int X { get; set; }
|
||||
public int Y { get; set; }
|
||||
public ButtonState LeftButton { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
namespace Microsoft.Xna.Framework.Input.Touch
|
||||
{
|
||||
|
||||
//
|
||||
// TouchPanelCapabilities
|
||||
//
|
||||
|
||||
[java.attr.Discard] // discard in output
|
||||
public struct TouchPanelCapabilities
|
||||
{
|
||||
public TouchPanelCapabilities(bool isConnected, int maximumTouchCount) { }
|
||||
}
|
||||
|
||||
[java.attr.Discard] // discard in output
|
||||
public class TouchPanel
|
||||
{
|
||||
public static void INTERNAL_onTouchEvent(int fingerId, TouchLocationState state,
|
||||
float x, float y, float dx, float dy) { }
|
||||
}
|
||||
|
||||
}
|
73
BNA/src/MessageBox.cs
Normal file
73
BNA/src/MessageBox.cs
Normal file
@ -0,0 +1,73 @@
|
||||
|
||||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace System.Windows.Forms
|
||||
{
|
||||
|
||||
public enum DialogResult
|
||||
{
|
||||
None, OK, Cancel, Abort, Retry, Ignore, Yes, No
|
||||
}
|
||||
|
||||
public class MessageBox
|
||||
{
|
||||
|
||||
public static volatile bool Showing;
|
||||
public static volatile bool Disable;
|
||||
|
||||
public static DialogResult Show(string text)
|
||||
{
|
||||
if (! Disable)
|
||||
{
|
||||
var gameRunner = GameRunner.Singleton;
|
||||
var activity = gameRunner.Activity;
|
||||
if ( gameRunner.InModal || activity == null
|
||||
|| android.os.Looper.getMainLooper().getThread()
|
||||
== java.lang.Thread.currentThread())
|
||||
{
|
||||
GameRunner.Log(text);
|
||||
}
|
||||
else
|
||||
{
|
||||
gameRunner.PauseGame(true);
|
||||
|
||||
var waitObj = new android.os.ConditionVariable();
|
||||
Show(activity, text, (_) => waitObj.open());
|
||||
waitObj.block();
|
||||
|
||||
gameRunner.ResumeGame(true);
|
||||
|
||||
}
|
||||
}
|
||||
return DialogResult.OK;
|
||||
}
|
||||
|
||||
static void Show(android.app.Activity activity, string text,
|
||||
System.Action<DialogResult> onClick)
|
||||
{
|
||||
activity.runOnUiThread(((java.lang.Runnable.Delegate) ( () => {
|
||||
|
||||
var dlg = new android.app.AlertDialog.Builder(activity);
|
||||
dlg.setPositiveButton((java.lang.CharSequence) (object) "Close",
|
||||
((android.content.DialogInterface.OnClickListener.Delegate)
|
||||
((dialog, which) =>
|
||||
{ Showing = false; onClick(DialogResult.Yes); }
|
||||
)).AsInterface());
|
||||
dlg.setOnDismissListener(
|
||||
((android.content.DialogInterface.OnDismissListener.Delegate)
|
||||
((dialog) =>
|
||||
{ Showing = false; onClick(DialogResult.Cancel); }
|
||||
)).AsInterface());
|
||||
dlg.create();
|
||||
dlg.setMessage((java.lang.CharSequence) (object) text);
|
||||
|
||||
Showing = true;
|
||||
dlg.show();
|
||||
|
||||
})).AsInterface());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
188
BNA/src/Mouse.cs
Normal file
188
BNA/src/Mouse.cs
Normal file
@ -0,0 +1,188 @@
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Xna.Framework.Input.Touch;
|
||||
#pragma warning disable 0436
|
||||
|
||||
namespace Microsoft.Xna.Framework.Input
|
||||
{
|
||||
|
||||
public static class Mouse
|
||||
{
|
||||
|
||||
private static MouseState state;
|
||||
|
||||
private static Queue motionEvents = new Queue();
|
||||
|
||||
private static List<(int id, float x, float y)> fingerXYs =
|
||||
new List<(int id, float x, float y)>();
|
||||
|
||||
public static java.util.concurrent.atomic.AtomicInteger NumTouchFingers =
|
||||
new java.util.concurrent.atomic.AtomicInteger();
|
||||
|
||||
|
||||
|
||||
public static IntPtr WindowHandle
|
||||
{
|
||||
get => IntPtr.Zero;
|
||||
set
|
||||
{
|
||||
state = default(MouseState);
|
||||
lock (motionEvents)
|
||||
{
|
||||
motionEvents.Clear();
|
||||
}
|
||||
fingerXYs.Clear();
|
||||
NumTouchFingers.set(0);
|
||||
}
|
||||
}
|
||||
public static int INTERNAL_BackBufferWidth;
|
||||
public static int INTERNAL_BackBufferHeight;
|
||||
|
||||
|
||||
|
||||
public static void QueueEvent(android.view.MotionEvent motionEvent)
|
||||
{
|
||||
lock (motionEvents)
|
||||
{
|
||||
motionEvents.Enqueue(
|
||||
android.view.MotionEvent.obtainNoHistory(motionEvent));
|
||||
}
|
||||
}
|
||||
|
||||
public static void HandleEvents(int clientWidth, int clientHeight)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
android.view.MotionEvent motionEvent;
|
||||
lock (motionEvents)
|
||||
{
|
||||
if (motionEvents.Count == 0)
|
||||
motionEvent = null;
|
||||
else
|
||||
{
|
||||
motionEvent =
|
||||
(android.view.MotionEvent) motionEvents.Dequeue();
|
||||
}
|
||||
}
|
||||
|
||||
if (motionEvent == null)
|
||||
return;
|
||||
|
||||
HandleOneEvent(motionEvent, clientWidth, clientHeight);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static void HandleOneEvent(android.view.MotionEvent motionEvent,
|
||||
int clientWidth, int clientHeight)
|
||||
{
|
||||
int action = motionEvent.getActionMasked();
|
||||
|
||||
if ( action == android.view.MotionEvent.ACTION_DOWN
|
||||
|| action == android.view.MotionEvent.ACTION_MOVE
|
||||
|| action == android.view.MotionEvent.ACTION_POINTER_DOWN)
|
||||
{
|
||||
state.LeftButton = ButtonState.Pressed;
|
||||
state.X = (int) Clamp(motionEvent.getX(),
|
||||
clientWidth, INTERNAL_BackBufferWidth);
|
||||
state.Y = (int) Clamp(motionEvent.getY(),
|
||||
clientHeight, INTERNAL_BackBufferHeight);
|
||||
|
||||
var which = (action == android.view.MotionEvent.ACTION_DOWN)
|
||||
? TouchLocationState.Pressed
|
||||
: TouchLocationState.Moved;
|
||||
|
||||
SendTouchEvents(motionEvent, which, clientWidth, clientHeight);
|
||||
}
|
||||
|
||||
else if ( action == android.view.MotionEvent.ACTION_UP
|
||||
|| action == android.view.MotionEvent.ACTION_POINTER_UP
|
||||
|| action == android.view.MotionEvent.ACTION_CANCEL)
|
||||
{
|
||||
state.LeftButton = ButtonState.Released;
|
||||
|
||||
SendTouchEvents(motionEvent, TouchLocationState.Released,
|
||||
clientWidth, clientHeight);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SendTouchEvents(android.view.MotionEvent motionEvent,
|
||||
TouchLocationState whichTouchEvent,
|
||||
int clientWidth, int clientHeight)
|
||||
{
|
||||
int pointerCount = motionEvent.getPointerCount();
|
||||
var eventArray =
|
||||
new (int id, float x, float y, float dx, float dy)[pointerCount];
|
||||
|
||||
for (int pointerIndex = 0; pointerIndex < pointerCount; pointerIndex++)
|
||||
{
|
||||
int id = motionEvent.getPointerId(pointerIndex);
|
||||
var x = Clamp(motionEvent.getX(pointerIndex), clientWidth, 1);
|
||||
var y = Clamp(motionEvent.getY(pointerIndex), clientHeight, 1);
|
||||
float dx, dy;
|
||||
|
||||
int fingerCount = fingerXYs.Count;
|
||||
int fingerIndex = 0;
|
||||
while (fingerIndex < fingerCount)
|
||||
{
|
||||
if (fingerXYs[fingerIndex].id == id)
|
||||
break;
|
||||
fingerIndex++;
|
||||
}
|
||||
|
||||
if (fingerIndex == fingerCount)
|
||||
{
|
||||
dx = dy = 0f;
|
||||
if (whichTouchEvent != TouchLocationState.Released)
|
||||
{
|
||||
fingerXYs.Add((id: id, x: x, y: y));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dx = x - fingerXYs[fingerIndex].x;
|
||||
dy = y - fingerXYs[fingerIndex].y;
|
||||
|
||||
if (whichTouchEvent != TouchLocationState.Released)
|
||||
{
|
||||
fingerXYs[fingerIndex] = (id: id, x: x, y: y);
|
||||
}
|
||||
else
|
||||
{
|
||||
fingerXYs.RemoveAt(fingerIndex);
|
||||
}
|
||||
}
|
||||
|
||||
eventArray[pointerIndex] = (id: id, x: x, y: y, dx: dx, dy: dy);
|
||||
}
|
||||
|
||||
NumTouchFingers.set(fingerXYs.Count);
|
||||
|
||||
for (int eventIndex = 0; eventIndex < pointerCount; eventIndex++)
|
||||
{
|
||||
var e = eventArray[eventIndex];
|
||||
TouchPanel.INTERNAL_onTouchEvent(e.id, whichTouchEvent, e.x, e.y, e.dx, e.dy);
|
||||
}
|
||||
}
|
||||
|
||||
private static float Clamp(float v, int clientSize, int backbufferSize)
|
||||
{
|
||||
if (v < 0)
|
||||
v = 0;
|
||||
if (v > clientSize)
|
||||
v = clientSize;
|
||||
return v * backbufferSize / clientSize;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static MouseState GetState() => state;
|
||||
|
||||
public static void SetPosition(int x, int y) { }
|
||||
|
||||
}
|
||||
|
||||
}
|
337
BNA/src/Renderer.cs
Normal file
337
BNA/src/Renderer.cs
Normal file
@ -0,0 +1,337 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Xna.Framework;
|
||||
using android.opengl;
|
||||
using GL10 = javax.microedition.khronos.opengles.GL10;
|
||||
using EGLConfig = javax.microedition.khronos.egl.EGLConfig;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Graphics
|
||||
{
|
||||
|
||||
public class Renderer : android.opengl.GLSurfaceView.Renderer
|
||||
{
|
||||
|
||||
//
|
||||
// renderer data
|
||||
//
|
||||
|
||||
private android.opengl.GLSurfaceView surface;
|
||||
private android.os.ConditionVariable waitObject;
|
||||
private java.util.concurrent.atomic.AtomicInteger paused;
|
||||
private Action actionOnChanged;
|
||||
|
||||
public object UserData;
|
||||
|
||||
//
|
||||
// surface configuration
|
||||
//
|
||||
|
||||
public int SurfaceWidth, SurfaceHeight;
|
||||
public DepthFormat SurfaceDepthFormat;
|
||||
public int TextureUnits;
|
||||
public int TextureSize;
|
||||
public int[] TextureFormats;
|
||||
|
||||
//
|
||||
// constructor
|
||||
//
|
||||
|
||||
private Renderer(android.app.Activity activity, Action onChanged,
|
||||
int redSize, int greenSize, int blueSize,
|
||||
int alphaSize, int depthSize, int stencilSize)
|
||||
{
|
||||
waitObject = new android.os.ConditionVariable();
|
||||
paused = new java.util.concurrent.atomic.AtomicInteger();
|
||||
actionOnChanged = onChanged;
|
||||
|
||||
activity.runOnUiThread(((java.lang.Runnable.Delegate) (() =>
|
||||
{
|
||||
surface = new android.opengl.GLSurfaceView(activity);
|
||||
surface.setEGLContextClientVersion(3); // OpenGL ES 3.0
|
||||
surface.setEGLConfigChooser(redSize, greenSize, blueSize,
|
||||
alphaSize, depthSize, stencilSize);
|
||||
surface.setPreserveEGLContextOnPause(true);
|
||||
surface.setRenderer(this);
|
||||
surface.setRenderMode(android.opengl.GLSurfaceView.RENDERMODE_WHEN_DIRTY);
|
||||
activity.setContentView(surface);
|
||||
|
||||
})).AsInterface());
|
||||
|
||||
// wait for one onDrawFrame callback, which tells us that
|
||||
// GLSurfaceView finished initializing the GL context
|
||||
if (! waitObject.block(8000))
|
||||
throw new NoSuitableGraphicsDeviceException("cannot create GLSurfaceView");
|
||||
|
||||
var clientBounds = GameRunner.Singleton.ClientBounds;
|
||||
if (SurfaceWidth != clientBounds.Width || SurfaceHeight != clientBounds.Height)
|
||||
{
|
||||
// while not common, it is possible for the screen to rotate,
|
||||
// between the time the Window/GameRunner is created, and the
|
||||
// time the renderer is created. we want to identify this.
|
||||
if (actionOnChanged != null)
|
||||
actionOnChanged();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Send
|
||||
//
|
||||
|
||||
public void Send(Action action)
|
||||
{
|
||||
Exception exc = null;
|
||||
if (paused.get() == 0)
|
||||
{
|
||||
var cond = new android.os.ConditionVariable();
|
||||
surface.queueEvent(((java.lang.Runnable.Delegate) (() =>
|
||||
{
|
||||
var error = GLES20.glGetError();
|
||||
if (error == GLES20.GL_NO_ERROR)
|
||||
{
|
||||
try
|
||||
{
|
||||
action();
|
||||
}
|
||||
catch (Exception exc2)
|
||||
{
|
||||
exc = exc2;
|
||||
}
|
||||
error = GLES20.glGetError();
|
||||
}
|
||||
if (error != GLES20.GL_NO_ERROR)
|
||||
exc = new Exception($"GL Error {error}");
|
||||
cond.open();
|
||||
})).AsInterface());
|
||||
cond.block();
|
||||
}
|
||||
if (exc != null)
|
||||
{
|
||||
throw new AggregateException(exc.Message, exc);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Present
|
||||
//
|
||||
|
||||
public void Present()
|
||||
{
|
||||
waitObject.close();
|
||||
surface.requestRender();
|
||||
waitObject.block();
|
||||
}
|
||||
|
||||
//
|
||||
// Renderer interface
|
||||
//
|
||||
|
||||
[java.attr.RetainName]
|
||||
public void onSurfaceCreated(GL10 unused, EGLConfig config)
|
||||
{
|
||||
// if onSurfaceCreated is called while resuming from pause,
|
||||
// it means the GL context was lost
|
||||
paused.compareAndSet(1, -1);
|
||||
}
|
||||
|
||||
[java.attr.RetainName]
|
||||
public void onSurfaceChanged(GL10 unused, int width, int height)
|
||||
{
|
||||
bool changed = (SurfaceWidth != width || SurfaceHeight != height)
|
||||
&& (SurfaceWidth != 0 || SurfaceHeight != 0);
|
||||
|
||||
SurfaceWidth = width;
|
||||
SurfaceHeight = height;
|
||||
InitConfig();
|
||||
|
||||
if (changed && actionOnChanged != null)
|
||||
actionOnChanged();
|
||||
}
|
||||
|
||||
[java.attr.RetainName]
|
||||
public void onDrawFrame(GL10 unused) => waitObject.open();
|
||||
|
||||
//
|
||||
// InitConfig
|
||||
//
|
||||
|
||||
private void InitConfig()
|
||||
{
|
||||
var data = new int[5];
|
||||
GLES20.glGetIntegerv(GLES20.GL_DEPTH_BITS, data, 0);
|
||||
GLES20.glGetIntegerv(GLES20.GL_STENCIL_BITS, data, 1);
|
||||
GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_IMAGE_UNITS, data, 2);
|
||||
GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, data, 3);
|
||||
GLES20.glGetIntegerv(GLES20.GL_NUM_COMPRESSED_TEXTURE_FORMATS, data, 4);
|
||||
|
||||
if (data[0] /* DEPTH_BITS */ >= 24)
|
||||
{
|
||||
SurfaceDepthFormat = (data[1] /* STENCIL_BITS */ >= 8)
|
||||
? DepthFormat.Depth24Stencil8
|
||||
: DepthFormat.Depth24;
|
||||
}
|
||||
else if (data[0] /* DEPTH_BITS */ >= 16)
|
||||
SurfaceDepthFormat = DepthFormat.Depth16;
|
||||
else
|
||||
SurfaceDepthFormat = DepthFormat.None;
|
||||
|
||||
TextureUnits = data[2]; // GL_MAX_TEXTURE_IMAGE_UNITS
|
||||
TextureSize = data[3]; // GL_MAX_TEXTURE_SIZE
|
||||
|
||||
TextureFormats = new int[data[4]]; // GL_NUM_COMPRESSED_TEXTURE_FORMATS
|
||||
GLES20.glGetIntegerv(GLES20.GL_COMPRESSED_TEXTURE_FORMATS, TextureFormats, 0);
|
||||
}
|
||||
|
||||
//
|
||||
// Create
|
||||
//
|
||||
|
||||
public static IntPtr Create(android.app.Activity activity, Action onChanged,
|
||||
int redSize, int greenSize, int blueSize,
|
||||
int alphaSize, int depthSize, int stencilSize)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
lock (RendererObjects)
|
||||
{
|
||||
var deviceId = java.lang.System.nanoTime();
|
||||
|
||||
foreach (var oldRendererObject in RendererObjects)
|
||||
{
|
||||
if (oldRendererObject.deviceId == deviceId)
|
||||
{
|
||||
deviceId = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (deviceId == 0)
|
||||
{
|
||||
java.lang.Thread.sleep(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
RendererObjects.Insert(0, new RendererObject()
|
||||
{
|
||||
deviceId = deviceId,
|
||||
renderer = new Renderer(activity, onChanged,
|
||||
redSize, greenSize, blueSize,
|
||||
alphaSize, depthSize, stencilSize),
|
||||
activity = new java.lang.@ref.WeakReference(activity),
|
||||
});
|
||||
|
||||
return (IntPtr) deviceId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// GetRenderer
|
||||
//
|
||||
|
||||
public static Renderer Get(IntPtr deviceId)
|
||||
{
|
||||
var longDeviceId = (long) deviceId;
|
||||
lock (RendererObjects)
|
||||
{
|
||||
foreach (var renderer in RendererObjects)
|
||||
{
|
||||
if (renderer.deviceId == longDeviceId)
|
||||
return renderer.renderer;
|
||||
}
|
||||
}
|
||||
throw new ArgumentException("invalid device ID");
|
||||
}
|
||||
|
||||
//
|
||||
// Release
|
||||
//
|
||||
|
||||
public void Release()
|
||||
{
|
||||
lock (RendererObjects)
|
||||
{
|
||||
for (int i = RendererObjects.Count; i-- > 0; )
|
||||
{
|
||||
if (RendererObjects[i].renderer == this)
|
||||
{
|
||||
RendererObjects[i].renderer.surface = null;
|
||||
RendererObjects[i].renderer = null;
|
||||
RendererObjects.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// GetRenderersForActivity
|
||||
//
|
||||
|
||||
private static List<Renderer> GetRenderersForActivity(android.app.Activity activity)
|
||||
{
|
||||
var list = new List<Renderer>();
|
||||
lock (RendererObjects)
|
||||
{
|
||||
foreach (var renderer in RendererObjects)
|
||||
{
|
||||
if (renderer.activity.get() == activity)
|
||||
list.Add(renderer.renderer);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
//
|
||||
// Pause
|
||||
//
|
||||
|
||||
public static void Pause(android.app.Activity activity)
|
||||
{
|
||||
foreach (var renderer in GetRenderersForActivity(activity))
|
||||
{
|
||||
renderer.surface.onPause();
|
||||
renderer.paused.set(1);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// CanResume
|
||||
//
|
||||
|
||||
public static bool CanResume(android.app.Activity activity)
|
||||
{
|
||||
foreach (var renderer in GetRenderersForActivity(activity))
|
||||
{
|
||||
if (renderer.paused.get() != 0)
|
||||
{
|
||||
renderer.waitObject.close();
|
||||
renderer.surface.onResume();
|
||||
renderer.waitObject.block();
|
||||
|
||||
if (! renderer.paused.compareAndSet(1, 0))
|
||||
{
|
||||
// cannot resume because we lost the GL context,
|
||||
// see also PauseRenderers and onSurfaceCreated
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// data
|
||||
//
|
||||
|
||||
private static List<RendererObject> RendererObjects = new List<RendererObject>();
|
||||
|
||||
private class RendererObject
|
||||
{
|
||||
public long deviceId;
|
||||
public Renderer renderer;
|
||||
public java.lang.@ref.WeakReference activity;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
188
BNA/src/Resources.cs
Normal file
188
BNA/src/Resources.cs
Normal file
@ -0,0 +1,188 @@
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Graphics
|
||||
{
|
||||
public class Resources
|
||||
{
|
||||
|
||||
//
|
||||
// SpriteEffect
|
||||
//
|
||||
|
||||
public static byte[] SpriteEffect => System.Text.Encoding.ASCII.GetBytes(@"
|
||||
|
||||
#technique SpriteBatch
|
||||
|
||||
--- vertex ---
|
||||
|
||||
layout (location = 0) in vec3 pos;
|
||||
layout (location = 1) in vec4 color;
|
||||
layout (location = 2) in vec2 uv;
|
||||
out vec4 f_color;
|
||||
out vec2 f_uv;
|
||||
uniform mat4 MatrixTransform;
|
||||
uniform float MultiplierY;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = MatrixTransform * vec4(pos, 1.0);
|
||||
gl_Position.y *= MultiplierY;
|
||||
f_color = color;
|
||||
f_uv = uv;
|
||||
}
|
||||
|
||||
--- fragment ---
|
||||
|
||||
in vec4 f_color;
|
||||
in vec2 f_uv;
|
||||
out vec4 o_color;
|
||||
uniform sampler2D image;
|
||||
|
||||
void main()
|
||||
{
|
||||
o_color = texture(image, f_uv) * f_color;
|
||||
}
|
||||
|
||||
--- end ---
|
||||
");
|
||||
|
||||
//
|
||||
// BasicEffect
|
||||
//
|
||||
|
||||
public static byte[] BasicEffect => System.Text.Encoding.ASCII.GetBytes(@"
|
||||
|
||||
#technique BasicEffect
|
||||
|
||||
--- vertex ---
|
||||
|
||||
layout (location = 0) in vec3 pos;
|
||||
layout (location = 1) in vec3 norm;
|
||||
layout (location = 2) in vec2 uv;
|
||||
|
||||
uniform int ShaderIndex;
|
||||
uniform float MultiplierY;
|
||||
|
||||
uniform mat4 WorldViewProj;
|
||||
uniform mat4 World;
|
||||
uniform mat3 WorldInverseTranspose;
|
||||
|
||||
uniform vec4 DiffuseColor;
|
||||
uniform vec4 FogVector;
|
||||
|
||||
out vec3 f_Position;
|
||||
out vec3 f_Normal;
|
||||
out vec2 f_TexCoord;
|
||||
out float f_FogFactor;
|
||||
out vec2 f_uv;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 pos4 = vec4(pos, 1.0);
|
||||
|
||||
f_FogFactor = clamp(dot(pos4, FogVector), 0.0, 1.0);
|
||||
|
||||
f_Position = vec3(World * pos4);
|
||||
f_Normal = normalize(WorldInverseTranspose * norm);
|
||||
|
||||
gl_Position = WorldViewProj * pos4;
|
||||
gl_Position.y *= MultiplierY;
|
||||
f_TexCoord = uv;
|
||||
}
|
||||
|
||||
--- fragment ---
|
||||
|
||||
in vec3 f_Position;
|
||||
in vec3 f_Normal;
|
||||
in vec2 f_TexCoord;
|
||||
in float f_FogFactor;
|
||||
|
||||
uniform vec3 DirLight0Direction;
|
||||
uniform vec3 DirLight0DiffuseColor;
|
||||
uniform vec3 DirLight0SpecularColor;
|
||||
|
||||
uniform vec3 DirLight1Direction;
|
||||
uniform vec3 DirLight1DiffuseColor;
|
||||
uniform vec3 DirLight1SpecularColor;
|
||||
|
||||
uniform vec3 DirLight2Direction;
|
||||
uniform vec3 DirLight2DiffuseColor;
|
||||
uniform vec3 DirLight2SpecularColor;
|
||||
|
||||
uniform vec4 DiffuseColor;
|
||||
uniform vec3 EmissiveColor;
|
||||
uniform vec3 SpecularColor;
|
||||
uniform float SpecularPower;
|
||||
uniform vec3 FogColor;
|
||||
|
||||
uniform vec3 EyePosition;
|
||||
|
||||
uniform sampler2D Texture;
|
||||
uniform int ShaderIndex;
|
||||
|
||||
out vec4 o_color;
|
||||
|
||||
void ComputeOneLight(vec3 eyeVector, vec3 worldNormal, out vec3 o_diffuse, out vec3 o_specular)
|
||||
{
|
||||
float dotL = dot(worldNormal, -DirLight0Direction);
|
||||
float dotH = dot(worldNormal, normalize(eyeVector - DirLight0Direction));
|
||||
|
||||
float zeroL = step(0.0, dotL);
|
||||
|
||||
float diffuse = zeroL * dotL;
|
||||
float specular = pow(max(dotH, 0.0) * zeroL, SpecularPower);
|
||||
|
||||
o_diffuse = (DirLight0DiffuseColor * diffuse) * DiffuseColor.rgb + EmissiveColor;
|
||||
o_specular = (DirLight0SpecularColor * specular) * SpecularColor;
|
||||
}
|
||||
|
||||
void ComputeThreeLights(vec3 eyeVector, vec3 worldNormal, out vec3 o_diffuse, out vec3 o_specular)
|
||||
{
|
||||
mat3 lightDirections = mat3(DirLight0Direction, DirLight1Direction, DirLight2Direction);
|
||||
mat3 lightDiffuse = mat3(DirLight0DiffuseColor, DirLight1DiffuseColor, DirLight2DiffuseColor);
|
||||
mat3 lightSpecular = mat3(DirLight0SpecularColor, DirLight1SpecularColor, DirLight2SpecularColor);
|
||||
mat3 halfVectors = mat3(normalize(eyeVector - DirLight0Direction),
|
||||
normalize(eyeVector - DirLight1Direction),
|
||||
normalize(eyeVector - DirLight2Direction));
|
||||
|
||||
vec3 dotL = worldNormal * -lightDirections;
|
||||
vec3 dotH = worldNormal * halfVectors;
|
||||
|
||||
vec3 zeroL = step(vec3(0.0), dotL);
|
||||
|
||||
vec3 diffuse = zeroL * dotL;
|
||||
vec3 specular = pow(max(dotH, vec3(0.0)) * zeroL, vec3(SpecularPower));
|
||||
|
||||
o_diffuse = (lightDiffuse * diffuse) * DiffuseColor.rgb + EmissiveColor;
|
||||
o_specular = (lightSpecular * specular) * SpecularColor;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 eyeVector = normalize(EyePosition - f_Position);
|
||||
vec3 worldNormal = normalize(f_Normal);
|
||||
vec3 diffuse, specular;
|
||||
if ((ShaderIndex & 24) == 24)
|
||||
ComputeThreeLights(eyeVector, worldNormal, diffuse, specular);
|
||||
else if ((ShaderIndex & 24) != 0)
|
||||
ComputeOneLight(eyeVector, worldNormal, diffuse, specular);
|
||||
else
|
||||
{
|
||||
diffuse = DiffuseColor.rgb;
|
||||
specular = vec3(0.0);
|
||||
}
|
||||
|
||||
vec4 color = mix(vec4(1.0), texture(Texture, f_TexCoord), bvec4(ShaderIndex & 4));
|
||||
color.rgb *= diffuse;
|
||||
color.rgb += specular * color.a;
|
||||
color.rgb = mix(color.rgb, FogColor * color.a, f_FogFactor);
|
||||
color.a *= DiffuseColor.a;
|
||||
o_color = color;
|
||||
}
|
||||
|
||||
--- end ---
|
||||
");
|
||||
|
||||
}
|
||||
}
|
62
BNA/src/TitleContainer.cs
Normal file
62
BNA/src/TitleContainer.cs
Normal file
@ -0,0 +1,62 @@
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
#pragma warning disable 0436
|
||||
|
||||
namespace Microsoft.Xna.Framework
|
||||
{
|
||||
|
||||
internal static class TitleContainer
|
||||
{
|
||||
|
||||
public static Stream OpenStream(string name)
|
||||
{
|
||||
var stream = GameRunner.Singleton.Activity
|
||||
.getAssets().open(name.Replace('\\', '/'));
|
||||
if (stream == null)
|
||||
throw new System.IO.FileNotFoundException(name);
|
||||
return new TitleStream(stream, name);
|
||||
}
|
||||
|
||||
public class TitleStream : Stream
|
||||
{
|
||||
public java.io.InputStream JavaStream;
|
||||
public string Name;
|
||||
|
||||
public TitleStream(java.io.InputStream javaStream, string name)
|
||||
{
|
||||
JavaStream = javaStream;
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public override bool CanRead => true;
|
||||
public override bool CanWrite => false;
|
||||
public override bool CanSeek => false;
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
=> JavaStream.read((sbyte[]) (object) buffer, offset, count);
|
||||
|
||||
//
|
||||
// unused methods and properties
|
||||
//
|
||||
|
||||
public override long Length => throw new System.PlatformNotSupportedException();
|
||||
public override long Position
|
||||
{
|
||||
get => throw new System.PlatformNotSupportedException();
|
||||
set => throw new System.PlatformNotSupportedException();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
=> throw new System.PlatformNotSupportedException();
|
||||
public override long Seek(long offset, System.IO.SeekOrigin origin)
|
||||
=> throw new System.PlatformNotSupportedException();
|
||||
public override void SetLength(long value)
|
||||
=> throw new System.PlatformNotSupportedException();
|
||||
public override void Flush() => throw new System.PlatformNotSupportedException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
56
Demo1/AndroidManifest.xml
Normal file
56
Demo1/AndroidManifest.xml
Normal file
@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!-- specify the lowercase namespace of the project
|
||||
as the package name in the manifest element -->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.spaceflint.bluebonnet.xnademo1">
|
||||
|
||||
<!-- API 18, GLES 3.0 -->
|
||||
<uses-sdk android:minSdkVersion="18" android:targetSdkVersion="29" />
|
||||
<uses-feature android:glEsVersion="0x00030000" android:required="true" />
|
||||
|
||||
<!-- modify android:label to set the application name.
|
||||
this name appears in the Settings / Apps listing -->
|
||||
|
||||
<application android:label="BNA_Demo1"
|
||||
android:icon="@drawable/icon"
|
||||
android:isGame="true" >
|
||||
|
||||
<!-- set android:screenOrientation if you need to lock orientation:
|
||||
https://developer.android.com/guide/topics/manifest/activity-element#screen -->
|
||||
|
||||
<!-- modify android:label to set the activity name.
|
||||
this is the name that appears below the app icon. -->
|
||||
|
||||
<activity android:name="microsoft.xna.framework.Activity"
|
||||
android:label="BNA Demo1"
|
||||
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
|
||||
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
|
||||
android:immersive="true"
|
||||
android:launchMode="singleTask"
|
||||
>
|
||||
|
||||
<!-- microsoft.xna.framework.log.tag sets the log identifier
|
||||
for log messages printed via android.util.Log by the app.
|
||||
if omitted, the default is 'BNA_Game' -->
|
||||
|
||||
<meta-data android:name="microsoft.xna.framework.log.tag"
|
||||
android:value="BNA_Demo1"/>
|
||||
|
||||
<!-- microsoft.xna.framework.main.class sets the name of the main
|
||||
or entrypoint class. if starts with a dot, it is appended
|
||||
to the namespace specifies in the package attribute. -->
|
||||
|
||||
<meta-data android:name="microsoft.xna.framework.main.class"
|
||||
android:value=".Program"/>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
47
Demo1/Demo1.sln
Normal file
47
Demo1/Demo1.sln
Normal file
@ -0,0 +1,47 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.30503.244
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Demo1", "Demo1\Demo1.csproj", "{223C1770-AB2F-4278-B8E3-349580A51644}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Demo1Content", "Demo1Content\Demo1Content.contentproj", "{E50B6FE1-C91C-4226-BA4B-75DEFDEF4CA3}"
|
||||
EndProject
|
||||
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Demo1FSharp", "Demo1FSharp\Demo1FSharp.fsproj", "{E3649229-B2DD-4CE8-AF10-7814CD306EA5}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{223C1770-AB2F-4278-B8E3-349580A51644}.Debug|Any CPU.ActiveCfg = Debug|x86
|
||||
{223C1770-AB2F-4278-B8E3-349580A51644}.Debug|Any CPU.Build.0 = Debug|x86
|
||||
{223C1770-AB2F-4278-B8E3-349580A51644}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{223C1770-AB2F-4278-B8E3-349580A51644}.Debug|x86.Build.0 = Debug|x86
|
||||
{223C1770-AB2F-4278-B8E3-349580A51644}.Release|Any CPU.ActiveCfg = Release|x86
|
||||
{223C1770-AB2F-4278-B8E3-349580A51644}.Release|Any CPU.Build.0 = Release|x86
|
||||
{223C1770-AB2F-4278-B8E3-349580A51644}.Release|x86.ActiveCfg = Release|x86
|
||||
{223C1770-AB2F-4278-B8E3-349580A51644}.Release|x86.Build.0 = Release|x86
|
||||
{E50B6FE1-C91C-4226-BA4B-75DEFDEF4CA3}.Debug|Any CPU.ActiveCfg = Debug|x86
|
||||
{E50B6FE1-C91C-4226-BA4B-75DEFDEF4CA3}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{E50B6FE1-C91C-4226-BA4B-75DEFDEF4CA3}.Release|Any CPU.ActiveCfg = Release|x86
|
||||
{E50B6FE1-C91C-4226-BA4B-75DEFDEF4CA3}.Release|x86.ActiveCfg = Release|x86
|
||||
{E3649229-B2DD-4CE8-AF10-7814CD306EA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E3649229-B2DD-4CE8-AF10-7814CD306EA5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E3649229-B2DD-4CE8-AF10-7814CD306EA5}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{E3649229-B2DD-4CE8-AF10-7814CD306EA5}.Debug|x86.Build.0 = Debug|x86
|
||||
{E3649229-B2DD-4CE8-AF10-7814CD306EA5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E3649229-B2DD-4CE8-AF10-7814CD306EA5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E3649229-B2DD-4CE8-AF10-7814CD306EA5}.Release|x86.ActiveCfg = Release|x86
|
||||
{E3649229-B2DD-4CE8-AF10-7814CD306EA5}.Release|x86.Build.0 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {4E3915BB-BD1B-4790-A50A-63DE2AEB9DAE}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
74
Demo1/Demo1/Config.cs
Normal file
74
Demo1/Demo1/Config.cs
Normal file
@ -0,0 +1,74 @@
|
||||
|
||||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Demo1
|
||||
{
|
||||
public static class Config
|
||||
{
|
||||
|
||||
public static int ClientWidth;
|
||||
public static int ClientHeight;
|
||||
public static int PixelsPerInch;
|
||||
|
||||
public static void InitGraphics(Game game)
|
||||
{
|
||||
// by default, if the field DisplayOrientation is left as default,
|
||||
// BNA will fit the game 'window' to the Android screen size.
|
||||
// this may or not be appropriate. it may be preferrable to use
|
||||
// a PreparingDeviceSettings hook, as shown below, to explicitly
|
||||
// set the size and orientation.
|
||||
|
||||
new GraphicsDeviceManager(game).PreparingDeviceSettings += ((sender, args) =>
|
||||
{
|
||||
var pp = args.GraphicsDeviceInformation.PresentationParameters;
|
||||
pp.BackBufferWidth = game.Window.ClientBounds.Width;
|
||||
pp.BackBufferHeight = game.Window.ClientBounds.Height;
|
||||
pp.DisplayOrientation = game.Window.CurrentOrientation;
|
||||
pp.RenderTargetUsage = RenderTargetUsage.PreserveContents;
|
||||
});
|
||||
}
|
||||
|
||||
public static void InitWindow(GameWindow window)
|
||||
{
|
||||
// request notification as the 'window' changes orientation.
|
||||
// if AndroidManifest.xml specifies a locked orientation,
|
||||
// this may not be needed.
|
||||
window.ClientSizeChanged += WindowResized;
|
||||
|
||||
// initialize the window configuration
|
||||
WindowResized(window, null);
|
||||
}
|
||||
|
||||
private static void WindowResized(object sender, EventArgs eventArgs)
|
||||
{
|
||||
if (sender is GameWindow window)
|
||||
{
|
||||
ClientWidth = window.ClientBounds.Width;
|
||||
ClientHeight = window.ClientBounds.Height;
|
||||
|
||||
// the BNA GameWindow object (the sender parameter) provides
|
||||
// an IDictionary object through an IServiceProvider interface.
|
||||
// the dictionary can be used to query information that is not
|
||||
// otherwise accessible via XNA interfaces. at this time, only
|
||||
// the screen DPI (dots per inch) value is provided.
|
||||
|
||||
PixelsPerInch = 144;
|
||||
if (((object) window) is IServiceProvider windowServiceProvider)
|
||||
{
|
||||
var windowDict = (System.Collections.IDictionary)
|
||||
windowServiceProvider.GetService(
|
||||
typeof(System.Collections.IDictionary));
|
||||
if (windowDict != null)
|
||||
{
|
||||
PixelsPerInch = (int) windowDict["dpi"];
|
||||
}
|
||||
}
|
||||
}
|
||||
Console.WriteLine($">>> WINDOW CONFIG {ClientWidth} x {ClientHeight} @ {PixelsPerInch} ppi");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
184
Demo1/Demo1/CubeDemo.cs
Normal file
184
Demo1/Demo1/CubeDemo.cs
Normal file
@ -0,0 +1,184 @@
|
||||
|
||||
|
||||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input.Touch;
|
||||
|
||||
namespace Demo1
|
||||
{
|
||||
|
||||
public class CubeDemo : DrawableGameComponent
|
||||
{
|
||||
|
||||
#if CUSTOM_VERTEX_BUFFER
|
||||
// custom vertex buffers are not supported at this time
|
||||
private VertexPositionNormalTextureColor[] cube;
|
||||
#else
|
||||
private VertexPositionNormalTexture[] cube;
|
||||
#endif
|
||||
private BasicEffect theEffect;
|
||||
private float angle;
|
||||
private int shaderCycler;
|
||||
|
||||
|
||||
public CubeDemo(Game game) : base(game)
|
||||
{
|
||||
var face = new Vector3[]
|
||||
{
|
||||
//TopLeft-BottomLeft-TopRight
|
||||
new Vector3(-1f, 1f, 0f), new Vector3(-1f, -1f, 0f), new Vector3( 1f, 1f, 0f),
|
||||
//BottomLeft-BottomRight-TopRight
|
||||
new Vector3(-1f, -1f, 0f), new Vector3( 1f, -1f, 0f), new Vector3( 1f, 1f, 0f),
|
||||
};
|
||||
|
||||
var faceNormals = new Vector3[]
|
||||
{
|
||||
Vector3.UnitZ, -Vector3.UnitZ, //Front & Back faces
|
||||
Vector3.UnitX, -Vector3.UnitX, //Left & Right faces
|
||||
Vector3.UnitY, -Vector3.UnitY, //Top & Bottom faces
|
||||
};
|
||||
|
||||
var ang90 = (float) Math.PI / 2f;
|
||||
var faceRotations = new Matrix[]
|
||||
{
|
||||
Matrix.CreateRotationY(2f * ang90),
|
||||
Matrix.CreateRotationY(0f),
|
||||
Matrix.CreateRotationY(-ang90),
|
||||
Matrix.CreateRotationY(ang90),
|
||||
Matrix.CreateRotationX(ang90),
|
||||
Matrix.CreateRotationX(-ang90)
|
||||
};
|
||||
|
||||
#if CUSTOM_VERTEX_BUFFER
|
||||
cube = new VertexPositionNormalTextureColor[36];
|
||||
for (int x = 0; x < cube.Length; x++)
|
||||
{
|
||||
var i = x % 6;
|
||||
var j = x / 6;
|
||||
cube[x] = new VertexPositionNormalTextureColor(
|
||||
Vector3.Transform(face[i], faceRotations[j]) + faceNormals[j],
|
||||
faceNormals[j], Vector2.Zero, Color.Red);
|
||||
}
|
||||
#else
|
||||
var uvCoords = new Vector2[] {
|
||||
Vector2.Zero, Vector2.UnitY, Vector2.UnitX, Vector2.UnitY, Vector2.One, Vector2.UnitX };
|
||||
cube = new VertexPositionNormalTexture[36];
|
||||
for (int x = 0; x < cube.Length; x++)
|
||||
{
|
||||
var i = x % 6;
|
||||
var j = x / 6;
|
||||
cube[x] = new VertexPositionNormalTexture(
|
||||
Vector3.Transform(face[i], faceRotations[j]) + faceNormals[j],
|
||||
faceNormals[j], uvCoords[i] * 2f);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
theEffect = new BasicEffect(Game.GraphicsDevice)
|
||||
{
|
||||
#if CUSTOM_VERTEX_BUFFER
|
||||
VertexColorEnabled = true,
|
||||
#endif
|
||||
AmbientLightColor = new Vector3(0f, 0.2f, 0f),
|
||||
LightingEnabled = true,
|
||||
TextureEnabled = true,
|
||||
PreferPerPixelLighting = true,
|
||||
FogStart = -10f, FogEnd = 20f,
|
||||
View = Matrix.CreateTranslation(0f, 0f, -10f),
|
||||
};
|
||||
|
||||
theEffect.Texture = Game.Content.Load<Texture2D>("4x4");
|
||||
|
||||
theEffect.DirectionalLight0.Enabled = true;
|
||||
theEffect.DirectionalLight0.DiffuseColor = new Vector3(1f, 1f, 0f);
|
||||
theEffect.DirectionalLight0.Direction = Vector3.Down;
|
||||
|
||||
theEffect.DirectionalLight1.Enabled = true;
|
||||
theEffect.DirectionalLight1.DiffuseColor = new Vector3(0f, 1f, 0f);
|
||||
theEffect.DirectionalLight1.SpecularColor = new Vector3(0f, 0f, 1f);
|
||||
theEffect.DirectionalLight1.Direction = Vector3.Right;
|
||||
|
||||
theEffect.DirectionalLight2.Enabled = true;
|
||||
theEffect.DirectionalLight2.DiffuseColor = new Vector3(1f, 0f, 0f);
|
||||
theEffect.DirectionalLight2.SpecularColor = new Vector3(0f, 0f, 1f);
|
||||
theEffect.DirectionalLight2.Direction = Vector3.Left;
|
||||
|
||||
shaderCycler = Storage.GetInt("CubeDemo_ShaderCycler", 1);
|
||||
UpdateEffect();
|
||||
|
||||
angle = Storage.GetFloat("CubeDemo_Angle", 0f);
|
||||
|
||||
base.Initialize();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override void Update(GameTime gameTime)
|
||||
{
|
||||
angle += 0.005f;
|
||||
if (angle > 2f * (float) Math.PI)
|
||||
angle = 0f;
|
||||
var R = Matrix.CreateRotationY(angle) * Matrix.CreateRotationX(0.4f);
|
||||
var T = Matrix.CreateTranslation(0f, 0f, 5f);
|
||||
theEffect.World = R * T;
|
||||
|
||||
if (Touch.LastGesture.GestureType == Microsoft.Xna.Framework.Input.Touch.GestureType.FreeDrag)
|
||||
{
|
||||
angle += Touch.LastGesture.Delta.X * 0.01f;
|
||||
Touch.LastGesture = default(GestureSample);
|
||||
}
|
||||
|
||||
Storage.Set("CubeDemo_Angle", angle);
|
||||
base.Update(gameTime);
|
||||
}
|
||||
|
||||
|
||||
public override void Draw(GameTime gameTime)
|
||||
{
|
||||
theEffect.Projection = Matrix.CreatePerspectiveFieldOfView(
|
||||
(float)Math.PI / 4.0f,
|
||||
(float)Config.ClientWidth / (float)Config.ClientHeight,
|
||||
1f, 10f);
|
||||
|
||||
GraphicsDevice.SamplerStates[0] = ((shaderCycler & 16) == 0) ? SamplerState.PointWrap
|
||||
: SamplerState.LinearWrap;
|
||||
|
||||
Game.GraphicsDevice.RasterizerState = new RasterizerState();
|
||||
foreach (var pass in theEffect.CurrentTechnique.Passes)
|
||||
{
|
||||
pass.Apply();
|
||||
Game.GraphicsDevice.DrawUserPrimitives(
|
||||
PrimitiveType.TriangleList, cube, 0, 12);
|
||||
}
|
||||
|
||||
var shaderIndex = theEffect.Parameters["ShaderIndex"].GetValueInt32();
|
||||
var rect = ((Game1) Game).DrawText(
|
||||
$"SHADER INDEX {shaderIndex}\nTAP HERE TO CYCLE", 0.2f, 0.4f, 1.5f, 0.7f);
|
||||
|
||||
if (Touch.Clicked(rect))
|
||||
{
|
||||
Storage.Set("CubeDemo_ShaderCycler", ++shaderCycler);
|
||||
UpdateEffect();
|
||||
}
|
||||
|
||||
base.Draw(gameTime);
|
||||
}
|
||||
|
||||
|
||||
void UpdateEffect()
|
||||
{
|
||||
theEffect.FogEnabled = ((shaderCycler & 1) != 0) ? false : true;
|
||||
theEffect.TextureEnabled = ((shaderCycler & 2) != 0) ? false : true;
|
||||
theEffect.PreferPerPixelLighting = ((shaderCycler & 4) != 0) ? false : true;
|
||||
theEffect.DirectionalLight1.Enabled = ((shaderCycler & 8) != 0) ? false : true;
|
||||
theEffect.DirectionalLight2.Enabled = ((shaderCycler & 8) != 0) ? false : true;
|
||||
theEffect.LightingEnabled = ((shaderCycler & 16) != 0) ? false : true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
110
Demo1/Demo1/Demo1.csproj
Normal file
110
Demo1/Demo1/Demo1.csproj
Normal file
@ -0,0 +1,110 @@
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{223C1770-AB2F-4278-B8E3-349580A51644}</ProjectGuid>
|
||||
<ProjectTypeGuids>{6D335F3A-9D43-41b4-9D22-F6F17C4BE596};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Demo1</RootNamespace>
|
||||
<AssemblyName>Demo1</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||
<TargetFrameworkProfile>
|
||||
</TargetFrameworkProfile>
|
||||
<XnaFrameworkVersion>v4.0</XnaFrameworkVersion>
|
||||
<XnaPlatform>Windows</XnaPlatform>
|
||||
<XnaProfile>HiDef</XnaProfile>
|
||||
<XnaCrossPlatformGroupID>ed304386-2da8-41d9-bfb0-3d6469f2ace6</XnaCrossPlatformGroupID>
|
||||
<XnaOutputType>Game</XnaOutputType>
|
||||
<ApplicationIcon>Game.ico</ApplicationIcon>
|
||||
<Thumbnail>GameThumbnail.png</Thumbnail>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\..\.obj\Demo1\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;WINDOWS</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<NoStdLib>true</NoStdLib>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<XnaCompressContent>false</XnaCompressContent>
|
||||
<LangVersion>8.0</LangVersion>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>..\..\.obj\Demo1\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;WINDOWS</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<NoStdLib>true</NoStdLib>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<XnaCompressContent>true</XnaCompressContent>
|
||||
<LangVersion>8.0</LangVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<IntermediateOutputPath>$(OutputPath)\Intermediate</IntermediateOutputPath>
|
||||
<StartupObject />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<AutoGenerateBindingRedirects>false</AutoGenerateBindingRedirects>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.Xna.Framework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" />
|
||||
<Reference Include="Microsoft.Xna.Framework.Game, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" />
|
||||
<Reference Include="Microsoft.Xna.Framework.Graphics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" />
|
||||
<Reference Include="Microsoft.Xna.Framework.Input.Touch, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL" />
|
||||
<Reference Include="Microsoft.Xna.Framework.Storage, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Config.cs" />
|
||||
<Compile Include="CubeDemo.cs" />
|
||||
<Compile Include="Font.cs" />
|
||||
<Compile Include="RenderDemo.cs" />
|
||||
<Compile Include="SpriteDemo.cs" />
|
||||
<Compile Include="Storage.cs" />
|
||||
<Compile Include="Touch.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Game1.cs" />
|
||||
<Compile Include="VertexPositionNormalTextureColor.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Game.ico" />
|
||||
<Content Include="GameThumbnail.png">
|
||||
<XnaPlatformSpecific>true</XnaPlatformSpecific>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Demo1Content\Demo1Content.contentproj">
|
||||
<Name>Demo1Content</Name>
|
||||
<XnaReferenceType>Content</XnaReferenceType>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Demo1FSharp\Demo1FSharp.fsproj">
|
||||
<Project>{e3649229-b2dd-4ce8-af10-7814cd306ea5}</Project>
|
||||
<Name>Demo1FSharp</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(MSBuildExtensionsPath)\Microsoft\XNA Game Studio\Microsoft.Xna.GameStudio.targets" />
|
||||
<!--
|
||||
To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
<Target Name="Install" DependsOnTargets="AfterBuild">
|
||||
<MSBuild Projects="../../Android.project" Targets="Install" Properties="Configuration=Release;
 GameAssembly=.obj\Demo1\Debug\Demo1.exe;
 AndroidManifest=Demo1\AndroidManifest.xml;
 KeystoreFile=my.keystore;KeystorePassword=123456;
 OutputApk=.obj\Demo1.apk" />
|
||||
</Target>
|
||||
</Project>
|
85
Demo1/Demo1/Font.cs
Normal file
85
Demo1/Demo1/Font.cs
Normal file
@ -0,0 +1,85 @@
|
||||
|
||||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Demo1
|
||||
{
|
||||
|
||||
public class Font
|
||||
{
|
||||
|
||||
private SpriteBatch spriteBatch;
|
||||
private SpriteFont spriteFont;
|
||||
|
||||
|
||||
public Font(Game game, SpriteBatch spriteBatch, string fontName)
|
||||
{
|
||||
spriteFont = game.Content.Load<SpriteFont>(fontName);
|
||||
this.spriteBatch = spriteBatch;
|
||||
}
|
||||
|
||||
|
||||
public Rectangle Measure(Vector4 pos, Vector4 size, string text)
|
||||
{
|
||||
var mm = spriteFont.MeasureString(text);
|
||||
|
||||
var wh = new Vector2(size.X, size.Y) * Config.PixelsPerInch;
|
||||
var xy = new Vector2(pos.X, pos.Y) * Config.PixelsPerInch;
|
||||
xy.X += pos.Z * Config.ClientWidth - size.Z * wh.X;
|
||||
xy.Y += pos.W * Config.ClientHeight - size.W * wh.Y;
|
||||
|
||||
return new Rectangle((int) xy.X, (int) xy.Y, (int) wh.X, (int) wh.Y);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Rectangle Measure(Vector2 pos, Vector2 size, string text)
|
||||
{
|
||||
var pos4 = new Vector4(pos.X, pos.Y,
|
||||
pos.X >= 0f ? 0f : 1f,
|
||||
pos.Y >= 0f ? 0f : 1f);
|
||||
var size4 = new Vector4(size.X, size.Y, 0f, 0f);
|
||||
return Measure(pos4, size4, text);
|
||||
}
|
||||
|
||||
|
||||
public void Draw(Rectangle rect, Color color, string text)
|
||||
{
|
||||
var mm = spriteFont.MeasureString(text);
|
||||
|
||||
var xy = new Vector2(rect.Left, rect.Top);
|
||||
var wh = new Vector2(rect.Width, rect.Height);
|
||||
var scl = wh / mm;
|
||||
|
||||
spriteBatch.DrawString(spriteFont, text, xy, color, 0f, Vector2.Zero, scl,
|
||||
SpriteEffects.None, 0f);
|
||||
}
|
||||
|
||||
|
||||
public void Draw(Vector4 pos, Vector4 size, Color color, string text)
|
||||
{
|
||||
var mm = spriteFont.MeasureString(text);
|
||||
|
||||
var wh = new Vector2(size.X, size.Y) * Config.PixelsPerInch;
|
||||
var xy = new Vector2(pos.X, pos.Y) * Config.PixelsPerInch;
|
||||
xy.X += pos.Z * Config.ClientWidth- size.Z * wh.X;
|
||||
xy.Y += pos.W * Config.ClientHeight - size.W * wh.Y;
|
||||
|
||||
var scl = wh / mm;
|
||||
|
||||
spriteBatch.DrawString(spriteFont, text, xy, color, 0f, Vector2.Zero, scl,
|
||||
SpriteEffects.None, 0f);
|
||||
}
|
||||
|
||||
public void Draw(Vector2 pos, Vector2 size, Color color, string text)
|
||||
{
|
||||
var pos4 = new Vector4(pos.X, pos.Y,
|
||||
pos.X >= 0f ? 0f : 1f,
|
||||
pos.Y >= 0f ? 0f : 1f);
|
||||
var size4 = new Vector4(size.X, size.Y, 0f, 0f);
|
||||
Draw(pos4, size4, color, text);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
BIN
Demo1/Demo1/Game.ico
Normal file
BIN
Demo1/Demo1/Game.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
248
Demo1/Demo1/Game1.cs
Normal file
248
Demo1/Demo1/Game1.cs
Normal file
@ -0,0 +1,248 @@
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Demo1
|
||||
{
|
||||
|
||||
public class Game1 : Microsoft.Xna.Framework.Game
|
||||
{
|
||||
|
||||
private DrawableGameComponent pageComponent;
|
||||
public Texture2D white;
|
||||
private SpriteBatch spriteBatch;
|
||||
private Font myFont;
|
||||
private int pageNumber, pageNumberOld;
|
||||
private bool paused;
|
||||
private bool anyDrawText;
|
||||
|
||||
private float framesPerSecond = 60f;
|
||||
private float countSeconds;
|
||||
private int countFrames;
|
||||
|
||||
|
||||
public Game1()
|
||||
{
|
||||
Content.RootDirectory = "Content";
|
||||
|
||||
// set up callbacks for window creation and resizing
|
||||
Config.InitGraphics(this);
|
||||
}
|
||||
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
Config.InitWindow(Window);
|
||||
Storage.Init();
|
||||
IsMouseVisible = true;
|
||||
pageNumber = Storage.GetInt("Game_PageNumber", 1);
|
||||
Components.Add(new Touch(this));
|
||||
}
|
||||
|
||||
protected override void LoadContent()
|
||||
{
|
||||
spriteBatch = new SpriteBatch(GraphicsDevice);
|
||||
myFont = new Font(this, spriteBatch, "MyFont");
|
||||
white = Content.Load<Texture2D>("white");
|
||||
|
||||
// Android does not have universal support for DXT compression,
|
||||
// and DXT compression generally creates larger files than PNG.
|
||||
// thus it may be preferrable to avoid DXT compression altogether
|
||||
// and load the unprocessed PNG as below:
|
||||
//
|
||||
// var stream = TitleContainer.OpenStream(
|
||||
// Content.RootDirectory + "/image.png");
|
||||
// Texture2D.FromStream(GraphicsDevice, stream);
|
||||
//
|
||||
// to disable processing, open Properties on the image in the
|
||||
// Content project, set "Build Action: None", and
|
||||
// "Copy to Output Directory: Copy if newer".
|
||||
}
|
||||
|
||||
|
||||
protected override void UnloadContent()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
protected override void Update(GameTime gameTime)
|
||||
{
|
||||
// when resuming the Android activity after it was paused, Update()
|
||||
// may be invoked multiple times to "catch up" within a time span
|
||||
// of up to half a second, and then Draw() will be called, and then
|
||||
// normal processing is resumed.
|
||||
// if this "catch up" is not desireable, a simple workaround is to
|
||||
// set a "paused" flag in OnDeactivated(), and clear it in Draw().
|
||||
|
||||
if (paused)
|
||||
return;
|
||||
|
||||
// basic scene management for the purpose of this demo:
|
||||
// after either arrow at the top of the screen is clicked, and
|
||||
// the current page number has changed, create the new 'page'.
|
||||
|
||||
if (pageNumber != pageNumberOld)
|
||||
{
|
||||
const int LAST_PAGE = 4;
|
||||
if (pageNumber <= 0)
|
||||
pageNumber = LAST_PAGE;
|
||||
else if (pageNumber > LAST_PAGE)
|
||||
pageNumber = 1;
|
||||
|
||||
pageNumberOld = pageNumber;
|
||||
Storage.Set("Game_PageNumber", pageNumber);
|
||||
|
||||
if (pageComponent != null)
|
||||
Components.Remove(pageComponent);
|
||||
|
||||
pageComponent = pageNumber switch
|
||||
{
|
||||
1 => new SpriteDemo(this),
|
||||
2 => new CubeDemo(this),
|
||||
3 => new RenderDemo(this),
|
||||
// the F# example is in project Demo1FSharp
|
||||
4 => new Demo1FSharp.StencilDemo(this),
|
||||
_ => throw new InvalidOperationException(),
|
||||
};
|
||||
Components.Add(pageComponent);
|
||||
}
|
||||
|
||||
base.Update(gameTime);
|
||||
}
|
||||
|
||||
|
||||
protected override void Draw(GameTime gameTime)
|
||||
{
|
||||
// BNA does not clear the screen at the start of the frame.
|
||||
// since we are alpha blending, we need to make sure we clear.
|
||||
|
||||
GraphicsDevice.Clear(Color.CornflowerBlue);
|
||||
spriteBatch.Begin();
|
||||
|
||||
// display frames per second at the bottom of the screen.
|
||||
|
||||
countSeconds += (float) gameTime.ElapsedGameTime.TotalSeconds;
|
||||
if (countSeconds > 1f)
|
||||
{
|
||||
framesPerSecond = countFrames / countSeconds;
|
||||
countSeconds -= 1f;
|
||||
countFrames = 0;
|
||||
}
|
||||
else
|
||||
countFrames++;
|
||||
|
||||
var fps = framesPerSecond;
|
||||
myFont.Draw(new Vector2(-0.7f, -0.2f), new Vector2(0.6f, 0.2f), Color.Yellow, $"FPS {fps:N2}");
|
||||
|
||||
// display the logo at the top of the screen
|
||||
|
||||
var ttl = $"BNA Demo (pg. {pageNumber})";
|
||||
var rect = myFont.Measure(new Vector4(0f, 0f, 0.5f, 0f), new Vector4(1.25f, 0.3f, 0.5f, 0f), ttl);
|
||||
spriteBatch.Draw(white, new Rectangle(0, 0, Window.ClientBounds.Width, (int) (rect.Height * 0.9f)), Color.Yellow);
|
||||
myFont.Draw(rect, Color.Green, ttl);
|
||||
|
||||
// display the arrows at the top of the screen, and check for
|
||||
// for taps. the Touch class uses the XNA Mouse class, which is
|
||||
// simulated from the touch screen on Android. see Touch class.
|
||||
|
||||
var btn = "<<<";
|
||||
rect = myFont.Measure(new Vector2(0f, 0.05f), new Vector2(0.5f, 0.2f), btn);
|
||||
myFont.Draw(rect, Color.Black, btn);
|
||||
if (Touch.Clicked(rect))
|
||||
pageNumber--;
|
||||
|
||||
btn = ">>>";
|
||||
rect = myFont.Measure(new Vector2(-0.5f, 0.05f), new Vector2(0.5f, 0.2f), btn);
|
||||
myFont.Draw(rect, Color.Black, btn);
|
||||
if (Touch.Clicked(rect))
|
||||
pageNumber++;
|
||||
|
||||
spriteBatch.End();
|
||||
base.Draw(gameTime);
|
||||
|
||||
// reset the "paused" flag. see comment at top of Update()
|
||||
paused = false;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// utility methods for the 'page' components
|
||||
//
|
||||
|
||||
|
||||
public Rectangle DrawText(string txt, float x, float y, float w, float h)
|
||||
{
|
||||
if (! anyDrawText)
|
||||
{
|
||||
spriteBatch.Begin();
|
||||
anyDrawText = true;
|
||||
}
|
||||
|
||||
var rect = myFont.Measure(new Vector2(x, y), new Vector2(w, h), txt);
|
||||
myFont.Draw(rect, Color.Black, txt);
|
||||
rect.Offset(3, 3);
|
||||
myFont.Draw(rect, Color.White, txt);
|
||||
return rect;
|
||||
}
|
||||
|
||||
|
||||
public void DrawSprite(Texture2D sprite, Rectangle rect)
|
||||
{
|
||||
if (! anyDrawText)
|
||||
{
|
||||
spriteBatch.Begin();
|
||||
anyDrawText = true;
|
||||
}
|
||||
|
||||
spriteBatch.Draw(sprite, rect, Color.White);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void DrawFlushBatch()
|
||||
{
|
||||
if (anyDrawText)
|
||||
{
|
||||
spriteBatch.End();
|
||||
anyDrawText = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected override void EndDraw()
|
||||
{
|
||||
DrawFlushBatch();
|
||||
base.EndDraw();
|
||||
}
|
||||
|
||||
|
||||
protected override void OnActivated(object sender, EventArgs args)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
protected override void OnDeactivated(object sender, EventArgs args)
|
||||
{
|
||||
// set the "paused" flag. see comment at top of Update()
|
||||
paused = true;
|
||||
|
||||
// it is important to save the state when Android is pausing,
|
||||
// and restore the state on start up, because we never know
|
||||
// when we might get destroyed, but we know we will always
|
||||
// get the OnDeactivated callback before destruction.
|
||||
// see also: Storage::Init()
|
||||
Storage.Sync();
|
||||
}
|
||||
|
||||
|
||||
protected override void OnExiting(object sender, EventArgs args)
|
||||
{
|
||||
// Storage.Clear();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
BIN
Demo1/Demo1/GameThumbnail.png
Normal file
BIN
Demo1/Demo1/GameThumbnail.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.6 KiB |
31
Demo1/Demo1/Program.cs
Normal file
31
Demo1/Demo1/Program.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Security.Principal;
|
||||
|
||||
namespace com.spaceflint.bluebonnet.xnademo1
|
||||
{
|
||||
#if WINDOWS || XBOX
|
||||
static class Program
|
||||
{
|
||||
/// <summary>
|
||||
/// The main entry point for the application.
|
||||
/// </summary>
|
||||
static void Main()
|
||||
{
|
||||
using (var game = new Demo1.Game1())
|
||||
{
|
||||
try
|
||||
{
|
||||
game.Run();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e.ToString());
|
||||
System.Windows.Forms.MessageBox.Show(e.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
34
Demo1/Demo1/Properties/AssemblyInfo.cs
Normal file
34
Demo1/Demo1/Properties/AssemblyInfo.cs
Normal file
@ -0,0 +1,34 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("Demo1")]
|
||||
[assembly: AssemblyProduct("Demo1")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2020")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type. Only Windows
|
||||
// assemblies support COM.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// On Windows, the following GUID is for the ID of the typelib if this
|
||||
// project is exposed to COM. On other platforms, it unique identifies the
|
||||
// title storage container when deploying this assembly to the device.
|
||||
[assembly: Guid("223c1770-ab2f-4278-b8e3-349580a51644")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
102
Demo1/Demo1/RenderDemo.cs
Normal file
102
Demo1/Demo1/RenderDemo.cs
Normal file
@ -0,0 +1,102 @@
|
||||
|
||||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
|
||||
namespace Demo1
|
||||
{
|
||||
public class RenderDemo : DrawableGameComponent
|
||||
{
|
||||
|
||||
private bool renderToTexture;
|
||||
private int renderTargetWidth, renderTargetHeight;
|
||||
private RenderTarget2D renderTarget;
|
||||
private Rectangle clickRectangle;
|
||||
|
||||
public RenderDemo(Game game) : base(game)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
renderToTexture = Storage.GetInt("RenderDemo_RenderToTexture", 0) != 0;
|
||||
base.Initialize();
|
||||
}
|
||||
|
||||
|
||||
public override void Draw(GameTime gameTime)
|
||||
{
|
||||
// multiple draw calls, particular for drawing fonts, are slow.
|
||||
// this example shows rendering a lot of text to one texture,
|
||||
// and drawing just a single texture.
|
||||
|
||||
if (renderToTexture && renderTargetWidth == Config.ClientWidth
|
||||
&& renderTargetHeight == Config.ClientHeight)
|
||||
{
|
||||
((Game1)Game).DrawSprite(renderTarget,
|
||||
new Rectangle(0, 0, renderTargetWidth, renderTargetHeight));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (renderToTexture)
|
||||
{
|
||||
((Game1)Game).DrawFlushBatch();
|
||||
renderTargetWidth = Config.ClientWidth;
|
||||
renderTargetHeight = Config.ClientHeight;
|
||||
if (renderTarget != null)
|
||||
renderTarget.Dispose();
|
||||
renderTarget = new RenderTarget2D(GraphicsDevice,
|
||||
renderTargetWidth, renderTargetHeight,
|
||||
true, SurfaceFormat.Color, DepthFormat.Depth24Stencil8);
|
||||
GraphicsDevice.SetRenderTarget(renderTarget);
|
||||
GraphicsDevice.Clear(Color.Transparent);
|
||||
}
|
||||
|
||||
ReallyDraw();
|
||||
|
||||
if (renderToTexture)
|
||||
{
|
||||
((Game1)Game).DrawFlushBatch();
|
||||
GraphicsDevice.SetRenderTarget(null);
|
||||
((Game1)Game).DrawSprite(renderTarget,
|
||||
new Rectangle(0, 0, renderTargetWidth, renderTargetHeight));
|
||||
}
|
||||
}
|
||||
|
||||
if (Touch.Clicked(clickRectangle))
|
||||
{
|
||||
renderToTexture = ! renderToTexture;
|
||||
renderTargetWidth = -1;
|
||||
renderTargetHeight = -1;
|
||||
Storage.Set("RenderDemo_RenderToTexture", renderToTexture ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void ReallyDraw()
|
||||
{
|
||||
float widthInInches = Config.ClientWidth / (float)Config.PixelsPerInch;
|
||||
float heightInInches = Config.ClientHeight / (float)Config.PixelsPerInch - 1f;
|
||||
|
||||
for (int y = 0; y < 20; y++)
|
||||
{
|
||||
for (int x = 0; x < 20; x++)
|
||||
{
|
||||
string s = char.ConvertFromUtf32((int)'A' + (x + y) % 26);
|
||||
float sx = 0.05f + widthInInches * 0.05f * x;
|
||||
float sy = 0.7f + heightInInches * 0.05f * y;
|
||||
((Game1)Game).DrawText(s, sx, sy, 0.1f, 0.1f);
|
||||
}
|
||||
}
|
||||
|
||||
string what = renderToTexture ? "DISABLE" : "ENABLE";
|
||||
clickRectangle = ((Game1)Game).DrawText(
|
||||
$" TAP TO {what} RENDER TEXTURE ", 0f, 0.35f, widthInInches, 0.2f);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
60
Demo1/Demo1/SpriteDemo.cs
Normal file
60
Demo1/Demo1/SpriteDemo.cs
Normal file
@ -0,0 +1,60 @@
|
||||
|
||||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
|
||||
namespace Demo1
|
||||
{
|
||||
public class SpriteDemo : DrawableGameComponent
|
||||
{
|
||||
|
||||
private Texture2D ball;
|
||||
private SpriteBatch spriteBatch;
|
||||
private int x, y;
|
||||
private int dx, dy;
|
||||
|
||||
|
||||
public SpriteDemo(Game game) : base(game)
|
||||
{
|
||||
spriteBatch = new SpriteBatch(game.GraphicsDevice);
|
||||
}
|
||||
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
ball = Game.Content.Load<Texture2D>("circle");
|
||||
|
||||
x = Storage.GetInt("SpriteDemo_X", Config.ClientWidth / 2);
|
||||
y = Storage.GetInt("SpriteDemo_Y", Config.ClientHeight / 2);
|
||||
dx = Storage.GetInt("SpriteDemo_DX", 1);
|
||||
dy = Storage.GetInt("SpriteDemo_DY", 1);
|
||||
}
|
||||
|
||||
|
||||
public override void Update(GameTime gameTime)
|
||||
{
|
||||
if (x < 0 || x + Config.PixelsPerInch > Config.ClientWidth)
|
||||
dx = -dx;
|
||||
if (y < 0 || y + Config.PixelsPerInch > Config.ClientHeight)
|
||||
dy = -dy;
|
||||
x += dx * 2;
|
||||
y += dy * 2;
|
||||
|
||||
Storage.Set("SpriteDemo_X", x);
|
||||
Storage.Set("SpriteDemo_Y", y);
|
||||
Storage.Set("SpriteDemo_DX", dx);
|
||||
Storage.Set("SpriteDemo_DY", dy);
|
||||
}
|
||||
|
||||
|
||||
public override void Draw(GameTime gameTime)
|
||||
{
|
||||
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend);
|
||||
spriteBatch.Draw(ball, new Rectangle(x, y, Config.PixelsPerInch, Config.PixelsPerInch), Color.Red);
|
||||
spriteBatch.End();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
155
Demo1/Demo1/Storage.cs
Normal file
155
Demo1/Demo1/Storage.cs
Normal file
@ -0,0 +1,155 @@
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Xna.Framework.Storage;
|
||||
|
||||
namespace Demo1
|
||||
{
|
||||
public static class Storage
|
||||
{
|
||||
|
||||
private const int VERSION_1 = 0x10101;
|
||||
private static readonly int INTEGER = BitConverter.ToInt32(Encoding.ASCII.GetBytes("int "), 0);
|
||||
private static readonly int FLOAT = BitConverter.ToInt32(Encoding.ASCII.GetBytes("flt "), 0);
|
||||
private static readonly int STRING = BitConverter.ToInt32(Encoding.ASCII.GetBytes("str "), 0);
|
||||
|
||||
private static Stream file;
|
||||
private static Dictionary<string, object> dict;
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
dict = new Dictionary<string, object>();
|
||||
|
||||
// on Android, StorageDevice and StorageContainer are "thin"
|
||||
// objects that do little more than translate the relative path
|
||||
// specified in StorageContainer::OpenFile, to a full path in
|
||||
// the app folder, which is then passed to System.IO.File.Open.
|
||||
|
||||
// the basic persistence model here is: game components update
|
||||
// a dictionary with current values. the dictionary is written
|
||||
// to a file on pause (Game::OnDeactivated calls Storage::Sync),
|
||||
// and read from the file on startup (Game::Initialize calls
|
||||
// Storage::Init).
|
||||
|
||||
try
|
||||
{
|
||||
var result = StorageDevice.BeginShowSelector(null, null);
|
||||
result.AsyncWaitHandle.WaitOne();
|
||||
var device = StorageDevice.EndShowSelector(result);
|
||||
result.AsyncWaitHandle.Close();
|
||||
|
||||
result = device.BeginOpenContainer("Demo1", null, null);
|
||||
result.AsyncWaitHandle.WaitOne();
|
||||
var container = device.EndOpenContainer(result);
|
||||
result.AsyncWaitHandle.Close();
|
||||
|
||||
file = container.OpenFile("state-v1.bin", FileMode.OpenOrCreate);
|
||||
if (file.Length > 4)
|
||||
Read(file);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine("Exception while reading state: " + e);
|
||||
dict.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public static void Sync()
|
||||
{
|
||||
file.Position = 0;
|
||||
try
|
||||
{
|
||||
Write(file);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine("Exception while writing state: " + e);
|
||||
file.SetLength(0);
|
||||
}
|
||||
file.Flush();
|
||||
}
|
||||
|
||||
public static void Clear()
|
||||
{
|
||||
dict.Clear();
|
||||
Sync();
|
||||
}
|
||||
|
||||
static void Read(Stream file)
|
||||
{
|
||||
using (var reader = new BinaryReader(file, Encoding.UTF8, true))
|
||||
{
|
||||
var version = reader.ReadInt32();
|
||||
if (version == VERSION_1)
|
||||
{
|
||||
for (var count = reader.ReadInt32(); count > 0; count--)
|
||||
{
|
||||
var name = reader.ReadString();
|
||||
var type = reader.ReadInt32();
|
||||
var obj = (type == INTEGER) ? (object) reader.ReadInt32()
|
||||
: (type == FLOAT) ? (object) reader.ReadSingle()
|
||||
: (type == STRING) ? (object) reader.ReadString()
|
||||
: null;
|
||||
if (obj == null)
|
||||
throw new NullReferenceException();
|
||||
dict[name] = obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Write(Stream file)
|
||||
{
|
||||
using (var writer = new BinaryWriter(file, Encoding.UTF8, true))
|
||||
{
|
||||
writer.Write(VERSION_1);
|
||||
writer.Write(dict.Count);
|
||||
foreach (var kvp in dict)
|
||||
{
|
||||
writer.Write(kvp.Key);
|
||||
if (kvp.Value is int intValue)
|
||||
{
|
||||
writer.Write(INTEGER);
|
||||
writer.Write(intValue);
|
||||
}
|
||||
else if (kvp.Value is float floatValue)
|
||||
{
|
||||
writer.Write(FLOAT);
|
||||
writer.Write(floatValue);
|
||||
}
|
||||
else if (kvp.Value is string stringValue)
|
||||
{
|
||||
writer.Write(STRING);
|
||||
writer.Write(stringValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int GetInt(string name, int defValue = 0)
|
||||
{
|
||||
return dict.TryGetValue(name, out var v)
|
||||
&& (v is int intValue) ? intValue : defValue;
|
||||
}
|
||||
|
||||
public static float GetFloat(string name, float defValue = 0f)
|
||||
{
|
||||
return dict.TryGetValue(name, out var v)
|
||||
&& (v is float floatValue) ? floatValue : defValue;
|
||||
}
|
||||
|
||||
public static string GetString(string name, string defValue = "")
|
||||
{
|
||||
return dict.TryGetValue(name, out var v)
|
||||
&& (v is string stringValue) ? stringValue : defValue;
|
||||
}
|
||||
|
||||
public static void Set(string name, object value)
|
||||
{
|
||||
dict[name] = value;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
80
Demo1/Demo1/Touch.cs
Normal file
80
Demo1/Demo1/Touch.cs
Normal file
@ -0,0 +1,80 @@
|
||||
|
||||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using Microsoft.Xna.Framework.Input.Touch;
|
||||
|
||||
namespace Demo1
|
||||
{
|
||||
public class Touch : DrawableGameComponent
|
||||
{
|
||||
|
||||
private int pressX, pressY;
|
||||
private int releaseX, releaseY;
|
||||
private static Touch instance;
|
||||
public static GestureSample LastGesture;
|
||||
|
||||
|
||||
public Touch(Game game) : base(game)
|
||||
{
|
||||
pressX = int.MinValue;
|
||||
releaseX = int.MinValue;
|
||||
instance = this;
|
||||
|
||||
// TouchPanel is functional when running on Android
|
||||
TouchPanel.EnabledGestures = GestureType.Tap | GestureType.FreeDrag;
|
||||
}
|
||||
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
instance = null;
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
public override void Draw(GameTime gameTime)
|
||||
{
|
||||
// this code is in Draw because Update may be invoked multiple times
|
||||
// per frame, which might cause the loss of the occasional click
|
||||
|
||||
// on Android, the Mouse class tracks single-finger taps, so it can
|
||||
// be used on both Windows and Android for simple input. for more
|
||||
// advanced touch tracking, use TouchPanel and gestures.
|
||||
|
||||
var state = Mouse.GetState();
|
||||
if (state.LeftButton == ButtonState.Pressed)
|
||||
{
|
||||
if (pressX == int.MinValue)
|
||||
{
|
||||
pressX = state.X;
|
||||
pressY = state.Y;
|
||||
}
|
||||
}
|
||||
else if (pressX != int.MinValue && releaseX == int.MinValue)
|
||||
{
|
||||
releaseX = state.X;
|
||||
releaseY = state.Y;
|
||||
}
|
||||
else
|
||||
{
|
||||
pressX = int.MinValue;
|
||||
releaseX = int.MinValue;
|
||||
}
|
||||
|
||||
if (TouchPanel.IsGestureAvailable)
|
||||
{
|
||||
LastGesture = TouchPanel.ReadGesture();
|
||||
Console.WriteLine($"Gesture {LastGesture.GestureType} at {LastGesture.Position}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public bool _Clicked(Rectangle rect)
|
||||
=> rect.Contains(pressX, pressY) && rect.Contains(releaseX, releaseY);
|
||||
|
||||
public static bool Clicked(Rectangle rect) => instance._Clicked(rect);
|
||||
|
||||
}
|
||||
|
||||
}
|
118
Demo1/Demo1/VertexPositionNormalTextureColor.cs
Normal file
118
Demo1/Demo1/VertexPositionNormalTextureColor.cs
Normal file
@ -0,0 +1,118 @@
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Demo1
|
||||
{
|
||||
[Serializable]
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct VertexPositionNormalTextureColor : IVertexType
|
||||
{
|
||||
VertexDeclaration IVertexType.VertexDeclaration
|
||||
{
|
||||
get
|
||||
{
|
||||
return VertexDeclaration;
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3 Position;
|
||||
public Vector3 Normal;
|
||||
public Vector2 TextureCoordinate;
|
||||
public Color Color;
|
||||
|
||||
public static readonly VertexDeclaration VertexDeclaration;
|
||||
|
||||
static VertexPositionNormalTextureColor()
|
||||
{
|
||||
VertexDeclaration = new VertexDeclaration(
|
||||
new VertexElement[]
|
||||
{
|
||||
new VertexElement(
|
||||
0,
|
||||
VertexElementFormat.Vector3,
|
||||
VertexElementUsage.Position,
|
||||
0
|
||||
),
|
||||
new VertexElement(
|
||||
12,
|
||||
VertexElementFormat.Vector3,
|
||||
VertexElementUsage.Normal,
|
||||
0
|
||||
),
|
||||
new VertexElement(
|
||||
24,
|
||||
VertexElementFormat.Vector2,
|
||||
VertexElementUsage.TextureCoordinate,
|
||||
0
|
||||
),
|
||||
new VertexElement(
|
||||
32,
|
||||
VertexElementFormat.Color,
|
||||
VertexElementUsage.Color,
|
||||
0
|
||||
),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public VertexPositionNormalTextureColor(
|
||||
Vector3 position,
|
||||
Vector3 normal,
|
||||
Vector2 textureCoordinate,
|
||||
Color color)
|
||||
{
|
||||
Position = position;
|
||||
Normal = normal;
|
||||
TextureCoordinate = textureCoordinate;
|
||||
Color = color;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
// TODO: Fix GetHashCode
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return (
|
||||
"{{Position:" + Position.ToString() +
|
||||
" Normal:" + Normal.ToString() +
|
||||
" TextureCoordinate:" + TextureCoordinate.ToString() +
|
||||
" Color:" + Color.ToString() +
|
||||
"}}"
|
||||
);
|
||||
}
|
||||
|
||||
public static bool operator ==(VertexPositionNormalTextureColor left, VertexPositionNormalTextureColor right)
|
||||
{
|
||||
return ( (left.Position == right.Position) &&
|
||||
(left.Normal == right.Normal) &&
|
||||
(left.TextureCoordinate == right.TextureCoordinate) &&
|
||||
(left.Color == right.Color)
|
||||
);
|
||||
}
|
||||
|
||||
public static bool operator !=(VertexPositionNormalTextureColor left, VertexPositionNormalTextureColor right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (obj.GetType() != base.GetType())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return (this == ((VertexPositionNormalTextureColor) obj));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
3
Demo1/Demo1/app.config
Normal file
3
Demo1/Demo1/app.config
Normal file
@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1"/></startup></configuration>
|
BIN
Demo1/Demo1Content/4x4.png
Normal file
BIN
Demo1/Demo1Content/4x4.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 129 B |
75
Demo1/Demo1Content/Demo1Content.contentproj
Normal file
75
Demo1/Demo1Content/Demo1Content.contentproj
Normal file
@ -0,0 +1,75 @@
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{E50B6FE1-C91C-4226-BA4B-75DEFDEF4CA3}</ProjectGuid>
|
||||
<ProjectTypeGuids>{96E2B04D-8817-42c6-938A-82C39BA4D311};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||
<XnaFrameworkVersion>v4.0</XnaFrameworkVersion>
|
||||
<OutputPath>..\..\.obj\Demo1\Content</OutputPath>
|
||||
<IntermediateOutputPath>$(OutputPath)\Intermediate</IntermediateOutputPath>
|
||||
<ContentRootDirectory>Content</ContentRootDirectory>
|
||||
<DisableContentItemWarning>true</DisableContentItemWarning>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RootNamespace>Demo1Content</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.Xna.Framework.Content.Pipeline.EffectImporter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL" />
|
||||
<Reference Include="Microsoft.Xna.Framework.Content.Pipeline.FBXImporter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL" />
|
||||
<Reference Include="Microsoft.Xna.Framework.Content.Pipeline.TextureImporter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL" />
|
||||
<Reference Include="Microsoft.Xna.Framework.Content.Pipeline.XImporter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL" />
|
||||
<Reference Include="Microsoft.Xna.Framework.Content.Pipeline.AudioImporters, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL" />
|
||||
<Reference Include="Microsoft.Xna.Framework.Content.Pipeline.VideoImporters, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="circle.png">
|
||||
<Name>circle</Name>
|
||||
<Importer>TextureImporter</Importer>
|
||||
<Processor>TextureProcessor</Processor>
|
||||
</Compile>
|
||||
<Compile Include="4x4.png">
|
||||
<Name>4x4</Name>
|
||||
<Importer>TextureImporter</Importer>
|
||||
<Processor>TextureProcessor</Processor>
|
||||
<ProcessorParameters_ColorKeyEnabled>False</ProcessorParameters_ColorKeyEnabled>
|
||||
<ProcessorParameters_PremultiplyAlpha>False</ProcessorParameters_PremultiplyAlpha>
|
||||
<ProcessorParameters_TextureFormat>NoChange</ProcessorParameters_TextureFormat>
|
||||
</Compile>
|
||||
<Compile Include="white.png">
|
||||
<Name>white</Name>
|
||||
<Importer>TextureImporter</Importer>
|
||||
<Processor>TextureProcessor</Processor>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="MyFont.spritefont">
|
||||
<Name>MyFont</Name>
|
||||
<Importer>FontDescriptionImporter</Importer>
|
||||
<Processor>FontDescriptionProcessor</Processor>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="fsharp256.png">
|
||||
<Name>fsharp256</Name>
|
||||
<Importer>TextureImporter</Importer>
|
||||
<Processor>TextureProcessor</Processor>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Microsoft\XNA Game Studio\$(XnaFrameworkVersion)\Microsoft.Xna.GameStudio.ContentPipeline.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
60
Demo1/Demo1Content/MyFont.spritefont
Normal file
60
Demo1/Demo1Content/MyFont.spritefont
Normal file
@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This file contains an xml description of a font, and will be read by the XNA
|
||||
Framework Content Pipeline. Follow the comments to customize the appearance
|
||||
of the font in your game, and to change the characters which are available to draw
|
||||
with.
|
||||
-->
|
||||
<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
|
||||
<Asset Type="Graphics:FontDescription">
|
||||
|
||||
<!--
|
||||
Modify this string to change the font that will be imported.
|
||||
-->
|
||||
<FontName>Consolas</FontName>
|
||||
|
||||
<!--
|
||||
Size is a float value, measured in points. Modify this value to change
|
||||
the size of the font.
|
||||
-->
|
||||
<Size>64</Size>
|
||||
|
||||
<!--
|
||||
Spacing is a float value, measured in pixels. Modify this value to change
|
||||
the amount of spacing in between characters.
|
||||
-->
|
||||
<Spacing>0</Spacing>
|
||||
|
||||
<!--
|
||||
UseKerning controls the layout of the font. If this value is true, kerning information
|
||||
will be used when placing characters.
|
||||
-->
|
||||
<UseKerning>true</UseKerning>
|
||||
|
||||
<!--
|
||||
Style controls the style of the font. Valid entries are "Regular", "Bold", "Italic",
|
||||
and "Bold, Italic", and are case sensitive.
|
||||
-->
|
||||
<Style>Regular</Style>
|
||||
|
||||
<!--
|
||||
If you uncomment this line, the default character will be substituted if you draw
|
||||
or measure text that contains characters which were not included in the font.
|
||||
-->
|
||||
<!-- <DefaultCharacter>*</DefaultCharacter> -->
|
||||
|
||||
<!--
|
||||
CharacterRegions control what letters are available in the font. Every
|
||||
character from Start to End will be built and made available for drawing. The
|
||||
default range is from 32, (ASCII space), to 126, ('~'), covering the basic Latin
|
||||
character set. The characters are ordered according to the Unicode standard.
|
||||
See the documentation for more information.
|
||||
-->
|
||||
<CharacterRegions>
|
||||
<CharacterRegion>
|
||||
<Start> </Start>
|
||||
<End>~</End>
|
||||
</CharacterRegion>
|
||||
</CharacterRegions>
|
||||
</Asset>
|
||||
</XnaContent>
|
BIN
Demo1/Demo1Content/circle.png
Normal file
BIN
Demo1/Demo1Content/circle.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 472 B |
BIN
Demo1/Demo1Content/fsharp256.png
Normal file
BIN
Demo1/Demo1Content/fsharp256.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.3 KiB |
BIN
Demo1/Demo1Content/white.png
Normal file
BIN
Demo1/Demo1Content/white.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 119 B |
30
Demo1/Demo1FSharp/Demo1FSharp.fsproj
Normal file
30
Demo1/Demo1FSharp/Demo1FSharp.fsproj
Normal file
@ -0,0 +1,30 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>net461</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<OutputPath>..\..\.obj\Demo1\$(Configuration)\</OutputPath>
|
||||
<IntermediateOutputPath>$(OutputPath)\Intermediate</IntermediateOutputPath>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<Platforms>AnyCPU;x86</Platforms>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
<Compile Include="Library1.fs" />
|
||||
|
||||
<Reference Include="Microsoft.Xna.Framework">
|
||||
<HintPath>C:\Program Files (x86)\Microsoft XNA\XNA Game Studio\v4.0\References\Windows\x86\Microsoft.Xna.Framework.dll</HintPath>
|
||||
</Reference>
|
||||
|
||||
<Reference Include="Microsoft.Xna.Framework.Game">
|
||||
<HintPath>C:\Program Files (x86)\Microsoft XNA\XNA Game Studio\v4.0\References\Windows\x86\Microsoft.Xna.Framework.Game.dll</HintPath>
|
||||
</Reference>
|
||||
|
||||
<Reference Include="Microsoft.Xna.Framework.Graphics">
|
||||
<HintPath>C:\Program Files (x86)\Microsoft XNA\XNA Game Studio\v4.0\References\Windows\x86\Microsoft.Xna.Framework.Graphics.dll</HintPath>
|
||||
</Reference>
|
||||
|
||||
</ItemGroup>
|
||||
</Project>
|
5
Demo1/Demo1FSharp/Directory.Build.props
Normal file
5
Demo1/Demo1FSharp/Directory.Build.props
Normal file
@ -0,0 +1,5 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<MSBuildProjectExtensionsPath>..\..\.obj\Demo1\packages</MSBuildProjectExtensionsPath>
|
||||
</PropertyGroup>
|
||||
</Project>
|
37
Demo1/Demo1FSharp/Library1.fs
Normal file
37
Demo1/Demo1FSharp/Library1.fs
Normal file
@ -0,0 +1,37 @@
|
||||
namespace Demo1FSharp
|
||||
|
||||
open System
|
||||
open Microsoft.Xna.Framework
|
||||
open Microsoft.Xna.Framework.Graphics
|
||||
|
||||
type StencilDemo (game : Game) =
|
||||
inherit DrawableGameComponent(game)
|
||||
|
||||
let mutable spriteBatch = null
|
||||
let mutable texture = null
|
||||
|
||||
// using option here solely to force a dependency on FSharp.Core.dll
|
||||
let mutable rectFunc : (Game -> Rectangle) option = None
|
||||
let mutable colorFunc : (GameTime -> Color) option = None
|
||||
|
||||
let logoRect (game : Game) =
|
||||
let (sw, sh) = (game.Window.ClientBounds.Width, game.Window.ClientBounds.Height)
|
||||
let (iw, ih) = ((int) ((single) sw * 0.75f), (int) ((single) sh * 0.75f))
|
||||
let (ix, iy) = ((sw - iw) / 2, (sh - ih) / 2)
|
||||
Rectangle(ix, iy, iw, ih)
|
||||
|
||||
let logoColor (gameTime : GameTime) =
|
||||
Color(0.f,
|
||||
(float32) (Math.Sin(gameTime.TotalGameTime.TotalMilliseconds * 0.001)),
|
||||
(float32) (Math.Cos(gameTime.TotalGameTime.TotalMilliseconds * 0.001)))
|
||||
|
||||
override Game.Initialize() =
|
||||
spriteBatch <- new SpriteBatch (game.GraphicsDevice)
|
||||
texture <- game.Content.Load("fsharp256")
|
||||
rectFunc <- Some logoRect
|
||||
colorFunc <- Some logoColor
|
||||
|
||||
override Game.Draw gameTime =
|
||||
spriteBatch.Begin ()
|
||||
spriteBatch.Draw (texture, rectFunc.Value game, colorFunc.Value gameTime)
|
||||
spriteBatch.End ()
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 spaceflint7
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
104
MakeAPK.project
Normal file
104
MakeAPK.project
Normal file
@ -0,0 +1,104 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
|
||||
<!-- REQUIRED PROPERTIES / ENVIRONMENT VARIABLES
|
||||
|
||||
INPUT_DLL = semi-colon separated list of DLLs to convert using Bluebonnet
|
||||
e.g. /path/to/MyGame.exe;/path/to/MySupportLib.dll
|
||||
|
||||
INPUT_DLL_1 .. INPUT_DLL_9 = same as INPUT_DLL
|
||||
|
||||
EXTRA_JAR = semi-colon separated list of additional JARs to bundle in the APK
|
||||
e.g. /path/to/Baselib.jar;/path/to/BNA.jar
|
||||
|
||||
EXTRA_JAR_1 .. EXTRA_JAR_9 = same as EXTRA_JAR
|
||||
|
||||
CONTENT_DIR = path to Content directory
|
||||
e.g. $(OutputDir)/MyGame/Debug/Content
|
||||
|
||||
APK_OUTPUT = path to copy the final APK
|
||||
e.g. $(OutputDir)/MyGame.apk
|
||||
|
||||
APK_TEMP_DIR = directory where APK processing occurs
|
||||
e.g. $(OutputDir)/MyGame/Debug/Content
|
||||
|
||||
KEYSTORE_FILE = path to a keystore file used in APK signing
|
||||
|
||||
KEYSTORE_PWD = password for keystore file
|
||||
|
||||
ANDROID_MANIFEST = path to AndroidManifest.xml file
|
||||
|
||||
BLUEBONNET_EXE = path to Bluebonnet.exe program file
|
||||
|
||||
BLUEBONNET_JAR = path to Baselib.jar file from Bluebonnet
|
||||
|
||||
ANDROID_JAR = path to Android.jar file, for desired API level
|
||||
e.g. $(ANDROID_HOME)/android-28/android.jar
|
||||
|
||||
ANDROID_BUILD = path to a build tools directory, for desired tools version
|
||||
e.g. $(ANDROID_HOME)/build-tools/30.0.2
|
||||
|
||||
-->
|
||||
|
||||
<RunPostBuildEvent>OnOutputUpdated</RunPostBuildEvent>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<InputDll Include="$(INPUT_DLL);$(INPUT_DLL_1);$(INPUT_DLL_2);$(INPUT_DLL_3);$(INPUT_DLL_4);$(INPUT_DLL_5);$(INPUT_DLL_6);$(INPUT_DLL_7);$(INPUT_DLL_8);$(INPUT_DLL_9)" />
|
||||
<ExtraJar Include="$(EXTRA_JAR);$(EXTRA_JAR_1);$(EXTRA_JAR_2);$(EXTRA_JAR_3);$(EXTRA_JAR_4);$(EXTRA_JAR_5);$(EXTRA_JAR_6);$(EXTRA_JAR_7);$(EXTRA_JAR_8);$(EXTRA_JAR_9)" />
|
||||
<ContentDir Include="$(CONTENT_DIR)">
|
||||
<DirName>%(ContentDir.Filename)%(ContentDir.Extension)</DirName>
|
||||
</ContentDir>
|
||||
<ContentFiles Include="$(CONTENT_DIR)/**/*" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="CoreCompile">
|
||||
|
||||
<Error Condition="(! Exists($(ICON_PNG)))"
|
||||
Text="Cannot find icon PNG file at '$(ICON_PNG)' using property ICON_PNG."/>
|
||||
<Error Condition="(! Exists($(CONTENT_DIR)))"
|
||||
Text="Cannot find game content folder at '$(CONTENT_DIR)' using property CONTENT_DIR."/>
|
||||
<Error Condition="(! Exists($(ANDROID_MANIFEST)))"
|
||||
Text="Cannot find Android manifest file at '$(ANDROID_MANIFEST)' using property ANDROID_MANIFEST."/>
|
||||
<Error Condition="(! Exists($(KEYSTORE_FILE)))"
|
||||
Text="Cannot find keystroke file at '$(KEYSTORE_FILE)' using property KEYSTORE_FILE."/>
|
||||
<Error Condition="(! Exists($(BLUEBONNET_EXE)))"
|
||||
Text="Cannot find Bluebonnet program file at '$(BLUEBONNET_EXE)' using property BLUEBONNET_EXE."/>
|
||||
<Error Condition="(! Exists($(ANDROID_JAR)))"
|
||||
Text="Cannot find Android platform JAR file at '$(ANDROID_JAR)' using property ANDROID_JAR."/>
|
||||
<Error Condition="(! Exists('$(ANDROID_BUILD)/aapt.exe')) and (! Exists('$(ANDROID_BUILD)/aapt'))"
|
||||
Text="Cannot find Android build tools at '$(ANDROID_BUILD)' using property ANDROID_BUILD."/>
|
||||
|
||||
<Error Condition="'$(APK_OUTPUT)' == ''"
|
||||
Text="The APK_OUTPUT property should specify the output location."/>
|
||||
<Error Condition="'$(APK_TEMP_DIR)' == ''"
|
||||
Text="The APK_TEMP_DIR property should specify a temporary directory."/>
|
||||
<Error Condition="'$(KEYSTORE_PWD)' == ''"
|
||||
Text="The KEYSTORE_PWD property should specify a the keystore password."/>
|
||||
|
||||
<CreateProperty Value="%(ContentDir.DirName)">
|
||||
<Output TaskParameter="Value" PropertyName="CONTENT_SUBDIR" />
|
||||
</CreateProperty>
|
||||
|
||||
<RemoveDir Directories="$(APK_TEMP_DIR)" />
|
||||
<MakeDir Directories="$(APK_TEMP_DIR)" />
|
||||
|
||||
<Exec Command=""$(BLUEBONNET_EXE)" "%(InputDll.FullPath)" "$(APK_TEMP_DIR)/classes.jar"" />
|
||||
<Exec Command=""$(ANDROID_BUILD)/d8" --release --lib "$(ANDROID_JAR)" "$(APK_TEMP_DIR)/classes.jar" "@(ExtraJar,'" "')" --output "$(APK_TEMP_DIR)""
|
||||
Condition="'@(ExtraJar)' != ''" />
|
||||
<Exec Command=""$(ANDROID_BUILD)/d8" --release --lib "$(ANDROID_JAR)" "$(APK_TEMP_DIR)/classes.jar" --output "$(APK_TEMP_DIR)""
|
||||
Condition="'@(ExtraJar)' == ''" />
|
||||
<Copy SourceFiles="@(ContentFiles)" DestinationFolder="$(APK_TEMP_DIR)/assets/$(CONTENT_SUBDIR)" SkipUnchangedFiles="true" />
|
||||
<Copy SourceFiles="$(ICON_PNG)" DestinationFiles="$(APK_TEMP_DIR)/res/drawable/icon.png" SkipUnchangedFiles="true" />
|
||||
<Exec Command=""$(ANDROID_BUILD)/aapt" package -f -F "$(APK_TEMP_DIR)/unaligned.apk" -M "$(ANDROID_MANIFEST)" -S "$(APK_TEMP_DIR)/res" -I "$(ANDROID_JAR)"" />
|
||||
<Exec Command=""$(ANDROID_BUILD)/aapt" add "unaligned.apk" classes.dex" WorkingDirectory="$(APK_TEMP_DIR)" />
|
||||
<Exec Command=""$(ANDROID_BUILD)/aapt" add "unaligned.apk" assets/$(CONTENT_SUBDIR)/%(ContentFiles.RecursiveDir)%(ContentFiles.Filename)%(ContentFiles.Extension)" WorkingDirectory="$(APK_TEMP_DIR)" />
|
||||
<Exec Command=""$(ANDROID_BUILD)/zipalign" -f 4 "$(APK_TEMP_DIR)/unaligned.apk" "$(APK_TEMP_DIR)/aligned.apk"" />
|
||||
<Exec Command=""$(ANDROID_BUILD)/apksigner" sign --ks "$(KEYSTORE_FILE)" --ks-pass "pass:$(KEYSTORE_PWD)" "$(APK_TEMP_DIR)/aligned.apk"" />
|
||||
<!-- copy the final APK -->
|
||||
<Copy SourceFiles="$(APK_TEMP_DIR)/aligned.apk" DestinationFiles="$(APK_OUTPUT)" />
|
||||
|
||||
</Target>
|
||||
|
||||
</Project>
|
69
README.md
Normal file
69
README.md
Normal file
@ -0,0 +1,69 @@
|
||||
|
||||
# Bluebonnet BNA
|
||||
|
||||
This is a port of [FNA](https://fna-xna.github.io/) for use with [Bluebonnet](https://github.com/spaceflint7/bluebonnet) to build games for Android using the [XNA 4.0](https://en.wikipedia.org/wiki/Microsoft_XNA) libraries.
|
||||
|
||||
**Bluebonnet** is an Android-compatible implementation of the .NET platform on top of the Java Virtual Machine. **Bluebonnet BNA** makes it possible to compile XNA games written in C# or F# to Android Java without any dependencies on native code libraries.
|
||||
|
||||
## Building
|
||||
|
||||
- Download and build the `Bluebonnet` compiler and its runtime library, `Baselib.jar`. For instructions, see [Bluebonnet README](https://github.com/spaceflint7/bluebonnet/blob/master/README.md).
|
||||
|
||||
- Download the [FNA](https://github.com/FNA-XNA/FNA/archive/master.zip) source code. Build by typing the following command in the FNA root directory:
|
||||
|
||||
- `MSBuild FNA.csproj -p:Configuration=Release`
|
||||
|
||||
- If the build is successful, the file `FNA.DLL` will be generated in the `bin/Release` sub-directory of the FNA root directory.
|
||||
|
||||
- Download this `BNA` project and build it by typing the following command in the BNA root directory:
|
||||
|
||||
- `MSBuild BNA -p:Configuration=Release -p:ANDROID_JAR=/path/to/Android.jar -p:BLUEBONNET_EXE=/path/to/Bluebonnet/executable -p:FNA_DLL=/path/to/FNA.DLL`
|
||||
|
||||
- The `ANDROID_JAR` property specifies the full path to an `Android.jar` file from the Android SDK distribution. `BNA` requires Android SDK version 18 or later.
|
||||
|
||||
- The `BLUEBONNET_EXE` property specifies the full path to the Bluebonnet compiler that you built in an earlier step.
|
||||
|
||||
- The `FNA_DLL` property specifies the full path to the FNA.DLL that you built in an earlier step. As noted earlier, this path should be `(FNA_DIR)/bin/Release/FNA.dll`.
|
||||
|
||||
- If the build is successful, the file `BNA.jar` will be generated in the `.obj` sub-directory of the repository root directory.
|
||||
|
||||
## Building the Demo
|
||||
|
||||
An example application `Demo1` is provided, which demonstrates some XNA functionality in C# and F#. It can be built using Visual Studio (solution file `Demo1.sln`), or from the command line:
|
||||
|
||||
- Type `nuget restore Demo1` to restore packages using [nuget](https://www.nuget.org/downloads).
|
||||
|
||||
- Type `msbuild Demo1 -p:Configuration=Release -p:Platform="x86"`
|
||||
|
||||
- Test the program: `.obj\Demo1\Release\Demo1.exe`. (Note that this will create the directory `SavedGames\Demo1` in the `Documents` directory.)
|
||||
|
||||
- To convert the built application to an Android APK, type the following command: (Note that this is a single-line command; line breaks were added for clarity.)
|
||||
|
||||
- <code>MSBuild MakeAPK.project<br>
|
||||
-p:INPUT_DLL=.obj\Demo1\Release\Demo1.exe<br>
|
||||
-p:INPUT_DLL_2=.obj\Demo1\Release\Demo1FSharp.dll<br>
|
||||
-p:INPUT_DLL_3=.obj\Demo1\Release\FSharp.Core.dll<br>
|
||||
-p:CONTENT_DIR=.obj\Demo1\Release\Content<br>
|
||||
-p:ICON_PNG=Demo1\Demo1\GameThumbnail.png<br>
|
||||
-p:ANDROID_MANIFEST=Demo1\AndroidManifest.xml<br>
|
||||
-p:KEYSTORE_FILE=.\my.keystore<br>
|
||||
-p:KEYSTORE_PWD=123456<br>
|
||||
-p:APK_OUTPUT=.obj\Demo1.apk<br>
|
||||
-p:APK_TEMP_DIR=.obj\Demo1\Release\TempApk<br>
|
||||
-p:EXTRA_JAR_1=.obj\BNA.jar<br>
|
||||
-p:EXTRA_JAR_2=\path\to\Bluebonnet\Baselib.jar<br>
|
||||
-p:BLUEBONNET_EXE=\path\to\Bluebonnet.exe<br>
|
||||
-p:ANDROID_JAR=\path\to\Android\platforms\android-XX\android.jar<br>
|
||||
-p:ANDROID_BUILD=\path\to\Android\build-tools\30.0.2</code>
|
||||
|
||||
- Make sure to specify the right paths for the Bluebonnet compiler (via the `BLUEBONNET_EXE` property), the Baselib support library (via the `EXTRA_JAR_2` property), the Android.jar file (via the `ANDROID_JAR` property) and the Android build-tools directory (via the `ANDROID_BUILD` property).
|
||||
|
||||
- The parameters are detailed at the top of the [MakeAPK.project](MakeAPK.project) file. See also the comments in the [AndroidManifest.xml](Demo1/AndroidManifest.xml) file, and comments throughout the `Demo1` source files.
|
||||
|
||||
- If the build is successful, the file `Demo1.apk` will be generated in the `.obj` sub-directory of the repository root directory.
|
||||
|
||||
- The batch file `build_demo.bat` runs the steps discussed in this "Building the Demo" section.
|
||||
|
||||
- Install the built APK to an Android device:
|
||||
|
||||
- `\path\to\Android\platform-tools\adb install -r .obj\Demo1.apk`
|
64
Solution.project
Normal file
64
Solution.project
Normal file
@ -0,0 +1,64 @@
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props"
|
||||
Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
|
||||
<PropertyGroup>
|
||||
|
||||
<SolutionDir Condition="'$(SolutionDir)' == ''">$(MSBuildThisFileDirectory)</SolutionDir>
|
||||
<SolutionDir Condition="'$(SolutionDir)' == '*Undefined*'">$(MSBuildThisFileDirectory)</SolutionDir>
|
||||
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Release</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<Deterministic>true</Deterministic>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<TargetFrameworkProfile></TargetFrameworkProfile>
|
||||
<LangVersion>8.0</LangVersion>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
|
||||
<!-- skip temporary directory when building -->
|
||||
<SkipCopyBuildProduct>true</SkipCopyBuildProduct>
|
||||
|
||||
<ObjDir>$(MSBuildThisFileDirectory).obj\</ObjDir>
|
||||
<OutputPath>$(ObjDir)$(AssemblyName)\$(Configuration)\</OutputPath>
|
||||
<IntermediateOutputPath>$(ObjDir)$(AssemblyName)\$(Configuration)\</IntermediateOutputPath>
|
||||
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(ProjectLanguage)' == 'FSharp' ">
|
||||
<LangVersion>4.7</LangVersion>
|
||||
<DebugType>portable</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="CleanProjectInSolutionOutputDirectory" AfterTargets="Clean">
|
||||
<RemoveDir Directories="$(ObjDir)$(AssemblyName)\$(Configuration)" />
|
||||
<RemoveDir Directories="$(ObjDir)$(AssemblyName)" />
|
||||
</Target>
|
||||
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets"
|
||||
Condition=" '$(ProjectLanguage)' == 'CSharp' or '$(ProjectLanguage)' == '' " />
|
||||
<Import Project="$(FSHARPINSTALLDIR)\Microsoft.FSharp.targets"
|
||||
Condition=" '$(ProjectLanguage)' == 'FSharp' and '$(FSHARPINSTALLDIR)' != '' " />
|
||||
<Import Project="$(FSharpCompilerPath)\Microsoft.FSharp.targets"
|
||||
Condition=" '$(ProjectLanguage)' == 'FSharp' and '$(FSHARPINSTALLDIR)' == '' and $(FSharpCompilerPath) != '' " />
|
||||
|
||||
</Project>
|
54
build_demo.bat
Normal file
54
build_demo.bat
Normal file
@ -0,0 +1,54 @@
|
||||
@echo off
|
||||
if "%ANDROID_JAR%" == "" (
|
||||
echo Missing environment variable ANDROID_JAR.
|
||||
echo It should specify the full path to an Android.jar file in the platforms directory of the Android SDK.
|
||||
goto :EOF
|
||||
)
|
||||
if "%ANDROID_BUILD%" == "" (
|
||||
echo Missing environment variable ANDROID_BUILD.
|
||||
echo It should specify the full path to a build-tools directory in the Android SDK.
|
||||
goto :EOF
|
||||
)
|
||||
if "%FNA_DLL%" == "" (
|
||||
echo Missing environment variable FNA_DLL.
|
||||
echo It should specify the full path to the FNA.DLL file.
|
||||
goto :EOF
|
||||
)
|
||||
if "%BLUEBONNET_EXE%" == "" (
|
||||
echo Missing environment variable BLUEBONNET_EXE.
|
||||
echo It should specify the full path to the Bluebonnet executable.
|
||||
goto :EOF
|
||||
)
|
||||
if "%BLUEBONNET_LIB%" == "" (
|
||||
echo Missing environment variable BLUEBONNET_LIB.
|
||||
echo It should specify the full path to the Bluebonnet Baselib.jar file.
|
||||
goto :EOF
|
||||
)
|
||||
|
||||
echo ========================================
|
||||
echo Building BNA. Command:
|
||||
echo MSBuild BNA -p:Configuration=Release
|
||||
echo ========================================
|
||||
MSBuild BNA -p:Configuration=Release
|
||||
pause
|
||||
|
||||
echo ========================================
|
||||
echo Building Demo1. Command:
|
||||
echo nuget restore Demo1
|
||||
echo msbuild Demo1 -p:Configuration=Release -p:Platform="x86"
|
||||
echo ========================================
|
||||
nuget restore Demo1
|
||||
msbuild Demo1 -p:Configuration=Release -p:Platform="x86"
|
||||
pause
|
||||
|
||||
echo ========================================
|
||||
echo Converting Demo1 to APK. Command:
|
||||
echo MSBuild MakeAPK.project -p:INPUT_DLL=.obj\Demo1\Release\Demo1.exe -p:INPUT_DLL_2=.obj\Demo1\Release\Demo1FSharp.dll -p:INPUT_DLL_3=.obj\Demo1\Release\FSharp.Core.dll -p:CONTENT_DIR=.obj\Demo1\Release\Content -p:ICON_PNG=Demo1\Demo1\GameThumbnail.png -p:ANDROID_MANIFEST=Demo1\AndroidManifest.xml -p:KEYSTORE_FILE=.\my.keystore -p:KEYSTORE_PWD=123456 -p:APK_OUTPUT=.obj\Demo1.apk -p:APK_TEMP_DIR=.obj\Demo1\Release\TempApk -p:EXTRA_JAR_1=.obj\BNA.jar -p:EXTRA_JAR_2=%BLUEBONNET_LIB%
|
||||
echo ========================================
|
||||
MSBuild MakeAPK.project -p:INPUT_DLL=.obj\Demo1\Release\Demo1.exe -p:INPUT_DLL_2=.obj\Demo1\Release\Demo1FSharp.dll -p:INPUT_DLL_3=.obj\Demo1\Release\FSharp.Core.dll -p:CONTENT_DIR=.obj\Demo1\Release\Content -p:ICON_PNG=Demo1\Demo1\GameThumbnail.png -p:ANDROID_MANIFEST=Demo1\AndroidManifest.xml -p:KEYSTORE_FILE=.\my.keystore -p:KEYSTORE_PWD=123456 -p:APK_OUTPUT=.obj\Demo1.apk -p:APK_TEMP_DIR=.obj\Demo1\Release\TempApk -p:EXTRA_JAR_1=.obj\BNA.jar -p:EXTRA_JAR_2=%BLUEBONNET_LIB%
|
||||
|
||||
echo ========================================
|
||||
echo All done
|
||||
echo ========================================
|
||||
|
||||
:EOF
|
BIN
my.keystore
Normal file
BIN
my.keystore
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user