init commit after fatal local git file corruption
This commit is contained in:
parent
08406de6fd
commit
14d9add9e6
36 changed files with 1639 additions and 0 deletions
166
Arts.cs
Normal file
166
Arts.cs
Normal file
|
@ -0,0 +1,166 @@
|
|||
using System.Collections.Generic;
|
||||
using StereoKit;
|
||||
|
||||
namespace snake;
|
||||
|
||||
static class Arts
|
||||
{
|
||||
static Model assets_model = Model.FromFile("meshes/assets.glb", Shader.Unlit);
|
||||
static Dictionary<string, Mesh> meshes = new();
|
||||
static Material mat_mono = new Material("mono.hlsl");
|
||||
static Material mat_unlit = new Material("unlit.hlsl");
|
||||
static Material mat_backbox = new Material("backbox.hlsl");
|
||||
|
||||
static Quat food_ori = Quat.Identity;
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
foreach (ModelNode node in assets_model.Nodes)
|
||||
{
|
||||
if (node.Mesh != null)
|
||||
{
|
||||
meshes.Add(node.Name, node.Mesh);
|
||||
}
|
||||
}
|
||||
|
||||
mat_backbox.Transparency = Transparency.Add;
|
||||
mat_backbox.FaceCull = Cull.Front;
|
||||
}
|
||||
|
||||
public static void Frame()
|
||||
{
|
||||
// background
|
||||
if (Device.DisplayBlend == DisplayBlend.Opaque)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// fullstick
|
||||
Mesh.Sphere.Draw(
|
||||
mat_unlit,
|
||||
Matrix.TS(
|
||||
Mono.r_con_stick.position,
|
||||
5 * U.mm
|
||||
),
|
||||
Color.White
|
||||
);
|
||||
Lines.Add(
|
||||
Mono.r_con_stick.position + V.XYZ(0, 0, 0),
|
||||
Mono.r_con_stick.position + Mono.fullstick * U.cm,
|
||||
Color.White,
|
||||
2 * U.mm
|
||||
);
|
||||
|
||||
// box
|
||||
Hierarchy.Push(Mono.box_pose.ToMatrix(Mono.box_scale));
|
||||
meshes["InsideOut"].Draw(mat_unlit, Matrix.Identity);
|
||||
meshes["Corrugation"].Draw(mat_unlit, Matrix.Identity);
|
||||
meshes["Corrugation"].Draw(mat_backbox, Matrix.Identity);
|
||||
|
||||
// snake
|
||||
float fall_t = 0.0f;
|
||||
if (Mono.snake_fall.state)
|
||||
{
|
||||
// if (Mono.snake_fall.delta == +1) // start falling
|
||||
fall_t = 1.0f - (float)Mono.step_t;
|
||||
}
|
||||
Vec3 fall = V.XYZ(0, fall_t, 0);
|
||||
bool food_next = (Mono.snake[0] + Mono.snake_dir) == Mono.food;
|
||||
if (!food_next)
|
||||
{
|
||||
meshes["Tongue"].Draw(
|
||||
mat_mono,
|
||||
Matrix.TRS(
|
||||
Mono.snake[0].ToVec3 + fall,
|
||||
Quat.LookDir(Mono.fullstick),
|
||||
V.XYZ(1, 1, 0.666f + Maths.smooth_stop((float)Mono.step_t) * 0.333f)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
string face = food_next ? "Face1Eat" : "Face0Default";
|
||||
meshes[face].Draw(
|
||||
mat_mono,
|
||||
Matrix.TRS(
|
||||
Mono.snake[0].ToVec3 + fall,
|
||||
Quat.LookDir(Mono.snake_dir.ToVec3),
|
||||
V.XYZ(1, 1, 1)// * (float)(Mono.step_t))
|
||||
)
|
||||
);
|
||||
|
||||
for (int i = 1; i < Mono.snake_len; i++)
|
||||
{
|
||||
float scale = 1.0f;
|
||||
if ((int)((Time.Total - Mono.eat_timestamp) * Mono.snake_len / Mono.step_step) == i)
|
||||
{
|
||||
scale = 1.1f;
|
||||
}
|
||||
meshes["Segment"].Draw(
|
||||
mat_mono,
|
||||
Matrix.TRS(
|
||||
Mono.snake[i].ToVec3 + fall,
|
||||
Quat.LookAt(Mono.snake[i].ToVec3, Mono.snake[i - 1].ToVec3),
|
||||
scale
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// holes
|
||||
foreach (KeyValuePair<XYZi, XYZi> hole in Mono.holes)
|
||||
{
|
||||
meshes["Hole"].Draw(
|
||||
mat_unlit,
|
||||
Matrix.TRS(
|
||||
hole.Key.ToVec3,
|
||||
Quat.LookDir(hole.Value.ToVec3),
|
||||
1
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// food
|
||||
float food_fall_t = 0.0f;
|
||||
if (Mono.food_fall.state)
|
||||
{
|
||||
// if (Mono.food_fall.delta == +1) // start falling
|
||||
food_fall_t = 1.0f - (float)Mono.step_t;
|
||||
}
|
||||
if (!food_next)
|
||||
{
|
||||
food_ori *= Quat.FromAngles(
|
||||
90 * Time.Stepf,
|
||||
30 * Time.Stepf,
|
||||
10 * Time.Stepf
|
||||
);
|
||||
}
|
||||
meshes["Food"].Draw(
|
||||
mat_mono,
|
||||
Matrix.TR(
|
||||
Mono.food.ToVec3 + V.XYZ(0, food_fall_t, 0),
|
||||
food_ori
|
||||
)
|
||||
);
|
||||
|
||||
Hierarchy.Pop();
|
||||
|
||||
// for (int sx = -Mono.head_fill.Xslen; Mono.head_fill.InX(sx); sx++)
|
||||
// {
|
||||
// for (int sy = -Mono.head_fill.Yslen; Mono.head_fill.InY(sy); sy++)
|
||||
// {
|
||||
// for (int sz = -Mono.head_fill.Zslen; Mono.head_fill.InZ(sz); sz++)
|
||||
// {
|
||||
// Vec3 pos = Mono.box_pose.ToMatrix(U.cm) * V.XYZ(sx, sy, sz);
|
||||
// Text.Add(
|
||||
// (Mono.head_fill[new XYZi(sx, sy, sz)] + Mono.tail_fill[new XYZi(sx, sy, sz)]).ToString(),
|
||||
// Matrix.TRS(
|
||||
// pos,
|
||||
// Quat.LookAt(pos, Input.Head.position),
|
||||
// 0.1f
|
||||
// )
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
43
Assets/backbox.hlsl
Normal file
43
Assets/backbox.hlsl
Normal file
|
@ -0,0 +1,43 @@
|
|||
#include "stereokit.hlsli"
|
||||
|
||||
//--name = dofdev/backbox
|
||||
//--color:color = 1, 1, 1, 1
|
||||
|
||||
float4 color;
|
||||
|
||||
struct vsIn {
|
||||
float4 pos : SV_Position;
|
||||
float3 norm : NORMAL0;
|
||||
float4 col : COLOR0;
|
||||
};
|
||||
struct psIn {
|
||||
float4 pos : SV_POSITION;
|
||||
float4 color : COLOR0;
|
||||
uint view_id : SV_RenderTargetArrayIndex;
|
||||
};
|
||||
|
||||
psIn vs(vsIn input, uint id : SV_InstanceID) {
|
||||
psIn o;
|
||||
o.view_id = id % sk_view_count;
|
||||
id = id / sk_view_count;
|
||||
|
||||
float3 world = mul(float4(input.pos.xyz, 1), sk_inst[id].world).xyz;
|
||||
o.pos = mul(float4(world, 1), sk_viewproj[o.view_id]);
|
||||
|
||||
float3 normal = normalize(mul(input.norm, (float3x3)sk_inst[id].world));
|
||||
|
||||
|
||||
float3 norm_shade = float3(0.5) + (normal * 0.5);
|
||||
// float3 norm_shade = float3(0.5) + (norm_color * 0.5);
|
||||
float3 view_dir = normalize(world - sk_camera_pos[o.view_id]);
|
||||
// o.color = norm_shaed * clamp(dot(o.normal, -view_dir), 0, 0.2);
|
||||
|
||||
// rim lighting
|
||||
float rim = 1.0 - saturate(dot(normalize(view_dir), normal));
|
||||
o.color = float4(norm_shade * rim * rim * 0.2, 1);
|
||||
|
||||
return o;
|
||||
}
|
||||
float4 ps(psIn input) : SV_TARGET {
|
||||
return input.color;
|
||||
}
|
53
Assets/floor.hlsl
Normal file
53
Assets/floor.hlsl
Normal file
|
@ -0,0 +1,53 @@
|
|||
#include <stereokit.hlsli>
|
||||
|
||||
//--name = app/floor
|
||||
|
||||
//--color:color = 0,0,0,1
|
||||
float4 color;
|
||||
//--radius = 5,10,0,0
|
||||
float4 radius;
|
||||
|
||||
struct vsIn {
|
||||
float4 pos : SV_POSITION;
|
||||
};
|
||||
struct psIn {
|
||||
float4 pos : SV_POSITION;
|
||||
float4 world : TEXCOORD0;
|
||||
uint view_id : SV_RenderTargetArrayIndex;
|
||||
};
|
||||
|
||||
psIn vs(vsIn input, uint id : SV_InstanceID) {
|
||||
psIn o;
|
||||
o.view_id = id % sk_view_count;
|
||||
id = id / sk_view_count;
|
||||
|
||||
o.world = mul(input.pos, sk_inst[id].world);
|
||||
o.pos = mul(o.world, sk_viewproj[o.view_id]);
|
||||
|
||||
return o;
|
||||
}
|
||||
float4 ps(psIn input) : SV_TARGET{
|
||||
// This line algorithm is inspired by :
|
||||
// http://madebyevan.com/shaders/grid/
|
||||
|
||||
// Minor grid lines
|
||||
float2 step = 1 / fwidth(input.world.xz);
|
||||
float2 grid = abs(frac(input.world.xz - 0.5) - 0.5) * step; // minor grid lines
|
||||
float dist = sqrt(dot(input.world.xz, input.world.xz)); // transparency falloff
|
||||
float size = min(grid.x, grid.y);
|
||||
float val = 1.0 - min(size, 1.0);
|
||||
val *= saturate(1 - ((dist - radius.x)/radius.y));
|
||||
|
||||
// Major axis lines
|
||||
const float extraThickness = 0.5;
|
||||
float2 axes = (abs(input.world.xz)) * step - extraThickness;
|
||||
size = min(axes.x, axes.y);
|
||||
float axisVal = 1.0 - min(size, 1.0);
|
||||
axisVal *= saturate(1 - ((dist - radius.x*1.5)/(radius.y*1.5)));
|
||||
|
||||
// combine, and drop transparent pixels
|
||||
val = max(val*0.6, axisVal);
|
||||
if (val <= 0) discard;
|
||||
|
||||
return float4(color.rgb, val);
|
||||
}
|
BIN
Assets/meshes/assets.glb
(Stored with Git LFS)
Normal file
BIN
Assets/meshes/assets.glb
(Stored with Git LFS)
Normal file
Binary file not shown.
48
Assets/mono.hlsl
Normal file
48
Assets/mono.hlsl
Normal file
|
@ -0,0 +1,48 @@
|
|||
#include "stereokit.hlsli"
|
||||
|
||||
//--name = dofdev/mono
|
||||
//--color:color = 1, 1, 1, 1
|
||||
//--tex_scale = 1
|
||||
//--diffuse = white
|
||||
|
||||
float4 color;
|
||||
float tex_scale;
|
||||
Texture2D diffuse : register(t0);
|
||||
SamplerState diffuse_s : register(s0);
|
||||
|
||||
struct vsIn {
|
||||
float4 pos : SV_Position;
|
||||
float3 norm : NORMAL0;
|
||||
float2 uv : TEXCOORD0;
|
||||
float4 col : COLOR0;
|
||||
};
|
||||
struct psIn {
|
||||
float4 pos : SV_POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
float4 color : COLOR0;
|
||||
uint view_id : SV_RenderTargetArrayIndex;
|
||||
};
|
||||
|
||||
psIn vs(vsIn input, uint id : SV_InstanceID) {
|
||||
psIn o;
|
||||
o.view_id = id % sk_view_count;
|
||||
id = id / sk_view_count;
|
||||
|
||||
float3 world = mul(float4(input.pos.xyz, 1), sk_inst[id].world).xyz;
|
||||
o.pos = mul(float4(world, 1), sk_viewproj[o.view_id]);
|
||||
|
||||
float3 normal = normalize(mul(input.norm, (float3x3)sk_inst[id].world));
|
||||
|
||||
o.uv = input.uv * tex_scale;
|
||||
float3 norm_color = float3(0.5) + (normal * 0.5);
|
||||
float3 norm_shade = float3(0.5) + (norm_color * 0.5);
|
||||
o.color = float4(norm_shade, 1) * input.col; // input.col * color * sk_inst[id].color;
|
||||
return o;
|
||||
}
|
||||
float4 ps(psIn input) : SV_TARGET {
|
||||
float4 col = diffuse.Sample(diffuse_s, input.uv);
|
||||
|
||||
col = col * input.color;
|
||||
|
||||
return col;
|
||||
}
|
BIN
Assets/sfx/crisp_nom.mp3
(Stored with Git LFS)
Normal file
BIN
Assets/sfx/crisp_nom.mp3
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Assets/sfx/punch_through.mp3
(Stored with Git LFS)
Normal file
BIN
Assets/sfx/punch_through.mp3
(Stored with Git LFS)
Normal file
Binary file not shown.
46
Assets/unlit.hlsl
Normal file
46
Assets/unlit.hlsl
Normal file
|
@ -0,0 +1,46 @@
|
|||
#include "stereokit.hlsli"
|
||||
|
||||
//--name = dofdev/unlit
|
||||
//--color:color = 1, 1, 1, 1
|
||||
//--tex_scale = 1
|
||||
//--diffuse = white
|
||||
|
||||
float4 color;
|
||||
float tex_scale;
|
||||
Texture2D diffuse : register(t0);
|
||||
SamplerState diffuse_s : register(s0);
|
||||
|
||||
struct vsIn {
|
||||
float4 pos : SV_Position;
|
||||
float3 norm : NORMAL0;
|
||||
float2 uv : TEXCOORD0;
|
||||
float4 col : COLOR0;
|
||||
};
|
||||
struct psIn {
|
||||
float4 pos : SV_POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
float4 color : COLOR0;
|
||||
uint view_id : SV_RenderTargetArrayIndex;
|
||||
};
|
||||
|
||||
psIn vs(vsIn input, uint id : SV_InstanceID) {
|
||||
psIn o;
|
||||
o.view_id = id % sk_view_count;
|
||||
id = id / sk_view_count;
|
||||
|
||||
float3 world = mul(float4(input.pos.xyz, 1), sk_inst[id].world).xyz;
|
||||
o.pos = mul(float4(world, 1), sk_viewproj[o.view_id]);
|
||||
|
||||
float3 normal = normalize(mul(input.norm, (float3x3)sk_inst[id].world));
|
||||
|
||||
o.uv = input.uv * tex_scale;
|
||||
o.color = input.col * color * sk_inst[id].color;
|
||||
return o;
|
||||
}
|
||||
float4 ps(psIn input) : SV_TARGET {
|
||||
float4 col = diffuse.Sample(diffuse_s, input.uv);
|
||||
|
||||
col = col * input.color;
|
||||
|
||||
return col;
|
||||
}
|
193
Maths.cs
Normal file
193
Maths.cs
Normal file
|
@ -0,0 +1,193 @@
|
|||
using System;
|
||||
|
||||
namespace snake;
|
||||
|
||||
public static class Maths
|
||||
{
|
||||
public static int u_length(int s_len) => (1 + s_len) * 2;
|
||||
public static int s_length(int u_len) => (u_len - 1) / 2;
|
||||
|
||||
public static int u_index(int s_index, int s_len) => s_len + s_index;
|
||||
public static int s_index(int u_index, int u_len) => u_index - u_len;
|
||||
|
||||
public static float s_scalar(float u_scalar) => (u_scalar * 2.0f) - 1.0f;
|
||||
public static float u_scalar(float s_scalar) => (1.0f + s_scalar) * 0.5f;
|
||||
|
||||
public static int min(int a, int b) => Math.Min(a, b);
|
||||
public static float min(float a, float b) => Math.Min(a, b);
|
||||
public static double min(double a, double b) => Math.Min(a, b);
|
||||
public static int max(int a, int b) => Math.Max(a, b);
|
||||
public static float max(float a, float b) => Math.Max(a, b);
|
||||
public static double max(double a, double b) => Math.Max(a, b);
|
||||
|
||||
public static int abs(int x) => Math.Abs(x);
|
||||
public static float abs(float x) => Math.Abs(x);
|
||||
public static double abs(double x) => Math.Abs(x);
|
||||
|
||||
public static int sign(float x) => x < 0 ? -1 : +1;
|
||||
|
||||
public static float round(float x) => MathF.Round(x);
|
||||
// public static int roundi(float value) => MathF.Round(value)
|
||||
|
||||
public static float precision(float x, float p)
|
||||
=> round(x * p) / p;
|
||||
|
||||
public static float smooth_start(float t) => (t * t * t);
|
||||
public static float smooth_stop(float t)
|
||||
{
|
||||
float s = sign(t);
|
||||
return (s - ((s - t) * (s - t) * (s - t)));
|
||||
}
|
||||
|
||||
public static float lerp(float a, float b, float t) => a + (b - a) * t;
|
||||
}
|
||||
|
||||
public class DeltaBool
|
||||
{
|
||||
public int delta = 0;
|
||||
public bool state = false;
|
||||
|
||||
public DeltaBool(bool state)
|
||||
{
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public void Step(bool next_state)
|
||||
{
|
||||
delta = 0;
|
||||
if (next_state != state)
|
||||
{
|
||||
delta = next_state ? +1 : -1;
|
||||
state = next_state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct XYZi
|
||||
{
|
||||
public int x { get; set; }
|
||||
public int y { get; set; }
|
||||
public int z { get; set; }
|
||||
|
||||
public XYZi(int x, int y, int z)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public static XYZi operator -(XYZi a, XYZi b)
|
||||
=> new(a.x - b.x, a.y - b.y, a.z - b.z);
|
||||
public static XYZi operator +(XYZi a, XYZi b)
|
||||
=> new(a.x + b.x, a.y + b.y, a.z + b.z);
|
||||
|
||||
public static bool operator ==(XYZi a, XYZi b)
|
||||
=> (a.x == b.x && a.y == b.y && a.z == b.z);
|
||||
public static bool operator !=(XYZi a, XYZi b)
|
||||
=> (a.x != b.x || a.y != b.y || a.z != b.z);
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is XYZi other)
|
||||
{
|
||||
return this == other;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public override int GetHashCode()
|
||||
=> HashCode.Combine(x, y, z);
|
||||
|
||||
public StereoKit.Vec3 ToVec3
|
||||
=> StereoKit.V.XYZ(x, y, z);
|
||||
|
||||
public override string ToString()
|
||||
=> string.Format("[{0:0.##}, {1:0.##}, {2:0.##}]", x, y, z);
|
||||
}
|
||||
|
||||
// public class SignedArray<T>
|
||||
|
||||
public class SpatialArray<T>
|
||||
{
|
||||
private T[,,] u_array;
|
||||
private T default_value;
|
||||
private int x_slen; // length past signed zero
|
||||
private int y_slen;
|
||||
private int z_slen;
|
||||
public int Xslen => x_slen;
|
||||
public int Yslen => y_slen;
|
||||
public int Zslen => z_slen;
|
||||
|
||||
public SpatialArray(
|
||||
int x_slen,
|
||||
int y_slen,
|
||||
int z_slen,
|
||||
T default_value = default(T)
|
||||
)
|
||||
{
|
||||
this.u_array = new T[
|
||||
Maths.u_length(x_slen),
|
||||
Maths.u_length(y_slen),
|
||||
Maths.u_length(z_slen)
|
||||
];
|
||||
this.x_slen = x_slen;
|
||||
this.y_slen = y_slen;
|
||||
this.z_slen = z_slen;
|
||||
this.default_value = default_value;
|
||||
|
||||
// initialize the arrays with the default value
|
||||
Clear(default_value);
|
||||
}
|
||||
|
||||
public T this[XYZi sv]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (InRange(sv))
|
||||
{
|
||||
return u_array[
|
||||
Maths.u_index(sv.x, x_slen),
|
||||
Maths.u_index(sv.y, y_slen),
|
||||
Maths.u_index(sv.z, z_slen)
|
||||
];
|
||||
}
|
||||
else
|
||||
{
|
||||
return default_value;
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
if (InRange(sv))
|
||||
{
|
||||
u_array[
|
||||
Maths.u_index(sv.x, x_slen),
|
||||
Maths.u_index(sv.y, y_slen),
|
||||
Maths.u_index(sv.z, z_slen)
|
||||
] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
// throw new IndexOutOfRangeException("set index is out of range.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool InX(int sx) => !(Math.Abs(sx) > x_slen);
|
||||
public bool InY(int sy) => !(Math.Abs(sy) > y_slen);
|
||||
public bool InZ(int sz) => !(Math.Abs(sz) > z_slen);
|
||||
public bool InRange(int sx, int sy, int sz) => InX(sx) && InY(sy) && InZ(sz);
|
||||
public bool InRange(XYZi sv) => InRange(sv.x, sv.y, sv.z);
|
||||
|
||||
public void Clear(T value)
|
||||
{
|
||||
for (int ux = 0; ux < u_array.GetLength(0); ux++)
|
||||
{
|
||||
for (int uy = 0; uy < u_array.GetLength(1); uy++)
|
||||
{
|
||||
for (int uz = 0; uz < u_array.GetLength(2); uz++)
|
||||
{
|
||||
u_array[ux, uy, uz] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
280
Mono.cs
Normal file
280
Mono.cs
Normal file
|
@ -0,0 +1,280 @@
|
|||
using System.Collections.Generic;
|
||||
using StereoKit;
|
||||
|
||||
namespace snake;
|
||||
|
||||
static class Mono
|
||||
{
|
||||
public static double step_step = 60.0 / 80; // 80|100|120 bpm
|
||||
public static double step_time = 0.0;
|
||||
public static double step_t = 0.0;
|
||||
public static Pose box_pose = new(0, 0, -10 * U.cm);
|
||||
public static float box_scale = 1.333f * U.cm;
|
||||
|
||||
public const int SD_X = 2, SD_Y = 1, SD_Z = 2;
|
||||
public static SpatialArray<int>
|
||||
s_array = new(SD_X, SD_Y, SD_Z, -1),
|
||||
head_fill = new(SD_X, SD_Y, SD_Z, -1),
|
||||
tail_fill = new(SD_X, SD_Y, SD_Z, -1);
|
||||
|
||||
public static XYZi[] snake = new XYZi[
|
||||
Maths.u_length(SD_X) * Maths.u_length(SD_Y) * Maths.u_length(SD_Z)
|
||||
];
|
||||
public static int snake_len = 4;
|
||||
public static int grow_buffer = 0;
|
||||
public static Vec3 fullstick = Vec3.Up;
|
||||
public static Pose r_con_stick = Pose.Identity;
|
||||
public static XYZi snake_dir = new(0, 0, 1), last_snake_dir = new(0, 0, 1);
|
||||
public static DeltaBool in_box = new(true);
|
||||
public static DeltaBool snake_fall = new(false);
|
||||
public static Dictionary<XYZi, XYZi> holes = new();
|
||||
public static XYZi food = new(2, 0, 0);
|
||||
public static DeltaBool food_fall = new(false);
|
||||
public static double eat_timestamp = 0.0;
|
||||
|
||||
static void PlayBox(this Sound sound, XYZi pos)
|
||||
{
|
||||
sound.Play(box_pose.ToMatrix(box_scale) * pos.ToVec3);
|
||||
}
|
||||
public static Sound sfx_crisp_nom = Sound.FromFile("sfx/crisp_nom.mp3");
|
||||
public static Sound sfx_punch_through = Sound.FromFile("sfx/punch_through.mp3");
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
for (int i = 0; i < snake.Length; i++)
|
||||
{
|
||||
snake[i] = new XYZi(0, 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Frame()
|
||||
{
|
||||
// flatscreen dev controls
|
||||
if (Device.Name == "Simulator")
|
||||
{
|
||||
if (Input.Key(Key.MouseLeft).IsActive())
|
||||
{
|
||||
float sx = Maths.s_scalar(Input.Mouse.pos.x / 640);
|
||||
float ssx = Maths.smooth_start(sx);
|
||||
box_pose.orientation *= Quat.FromAngles(0, sx * 180.0f * Time.Stepf, 0);
|
||||
}
|
||||
|
||||
if (Input.Key(Key.A).IsJustActive()) snake_dir = new(-1, 0, 0);
|
||||
if (Input.Key(Key.S).IsJustActive()) snake_dir = new(+1, 0, 0);
|
||||
if (Input.Key(Key.W).IsJustActive()) snake_dir = new(0, 0, -1);
|
||||
if (Input.Key(Key.R).IsJustActive()) snake_dir = new(0, 0, +1);
|
||||
if (Input.Key(Key.Shift).IsJustActive()) snake_dir = new(0, -1, 0);
|
||||
if (Input.Key(Key.Space).IsJustActive()) snake_dir = new(0, +1, 0);
|
||||
|
||||
fullstick = snake_dir.ToVec3;
|
||||
}
|
||||
else
|
||||
{
|
||||
Pose head = Input.Head;
|
||||
box_pose.position = head.position + head.orientation * V.XYZ(0, -(SD_Y + 0.5f) * box_scale, -32 * U.cm);
|
||||
|
||||
Hand r_hand = Input.Hand(Handed.Right);
|
||||
Controller r_con = Input.Controller(Handed.Right);
|
||||
bool con_tracked = r_con.trackedPos > TrackState.Lost;
|
||||
Input.HandVisible(Handed.Max, !con_tracked);
|
||||
if (con_tracked)
|
||||
{
|
||||
Vec2 stick = r_con.stick;
|
||||
Quat stick_rot = Quat.FromAngles(stick.y * -90, 0, stick.x * +90);
|
||||
float stick_sign = r_con.IsStickClicked ? -1 : +1;
|
||||
r_con_stick = r_con.pose;
|
||||
r_con_stick.position += r_con_stick.orientation * V.XYZ(0.0065f, -0.012f, -0.05f);
|
||||
r_con_stick.orientation *= Quat.FromAngles(-50, 0, 0);
|
||||
fullstick = r_con_stick.orientation * stick_rot * Vec3.Up * stick_sign;
|
||||
|
||||
// Vec3 fullstick = r_hand.palm.orientation * Vec3.Up;
|
||||
float ax = Maths.abs(fullstick.x);
|
||||
float ay = Maths.abs(fullstick.y);
|
||||
float az = Maths.abs(fullstick.z);
|
||||
if (ax > ay && ax > az) snake_dir = new(Maths.sign(fullstick.x), 0, 0);
|
||||
if (ay > ax && ay > az) snake_dir = new(0, Maths.sign(fullstick.y), 0);
|
||||
if (az > ax && az > ay) snake_dir = new(0, 0, Maths.sign(fullstick.z));
|
||||
}
|
||||
}
|
||||
|
||||
// catch invalid direction and revert to last_dir
|
||||
if (snake[0] + snake_dir == snake[1])
|
||||
{
|
||||
snake_dir = last_snake_dir;
|
||||
}
|
||||
last_snake_dir = snake_dir;
|
||||
}
|
||||
|
||||
public static void Step()
|
||||
{
|
||||
if (s_array[snake[0] + snake_dir] > -1)
|
||||
{
|
||||
// lose condition
|
||||
bool stuck = true;
|
||||
for (int i = 0; i < directions.Length; i++)
|
||||
{
|
||||
if (s_array[snake[0] + directions[i]] == -1)
|
||||
{
|
||||
stuck = false;
|
||||
}
|
||||
}
|
||||
if (stuck)
|
||||
{
|
||||
Log.Info("your stuck");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (snake_len == snake.Length)
|
||||
{
|
||||
// win condition
|
||||
Log.Info("full snake");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (grow_buffer > 0)
|
||||
{
|
||||
snake_len++;
|
||||
grow_buffer--;
|
||||
}
|
||||
}
|
||||
|
||||
// slither
|
||||
for (int i = snake.Length - 1; i > 0; i--)
|
||||
{
|
||||
snake[i] = snake[i - 1];
|
||||
}
|
||||
snake[0] += snake_dir;
|
||||
|
||||
// gravity
|
||||
bool grounded = false;
|
||||
for (int i = 0; i < snake_len; i++)
|
||||
{
|
||||
if (snake[i].y == -SD_Y)
|
||||
{
|
||||
grounded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
snake_fall.Step(!grounded);
|
||||
if (snake_fall.state)
|
||||
{
|
||||
for (int i = 0; i < snake_len; i++)
|
||||
{
|
||||
snake[i] -= new XYZi(0, 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool food_grounded = food.y == -SD_Y;
|
||||
XYZi below_food = new XYZi(food.x, food.y - 1, food.z);
|
||||
bool on_snake = s_array.InRange(below_food) && s_array[below_food] > -1;
|
||||
food_fall.Step(!food_grounded && !on_snake);
|
||||
if (food_fall.state)
|
||||
{
|
||||
food -= new XYZi(0, 1, 0);
|
||||
}
|
||||
|
||||
|
||||
in_box.Step(s_array.InRange(snake[0]));
|
||||
if (in_box.delta != 0) // 1 just in -1 just out
|
||||
{
|
||||
holes.Add(snake[0], snake_dir);
|
||||
sfx_punch_through.PlayBox(snake[0]);
|
||||
}
|
||||
if (holes.ContainsKey(snake[snake_len - 1]))
|
||||
{
|
||||
holes.Remove(snake[snake_len - 1]);
|
||||
}
|
||||
|
||||
s_array.Clear(-1);
|
||||
for (int i = 0; i < snake_len; i++)
|
||||
{
|
||||
s_array[snake[i]] = i;
|
||||
}
|
||||
|
||||
// eat
|
||||
if (food == snake[0])
|
||||
{
|
||||
eat_timestamp = Time.Total;
|
||||
grow_buffer += 3;
|
||||
|
||||
Feed();
|
||||
sfx_crisp_nom.PlayBox(snake[0]);
|
||||
}
|
||||
}
|
||||
|
||||
static void Feed()
|
||||
{
|
||||
head_fill.Clear(-1);
|
||||
Gas(head_fill, snake[0]);
|
||||
|
||||
// handle out of the box exception on tail
|
||||
tail_fill.Clear(-1);
|
||||
Gas(tail_fill, snake[snake_len - 1]);
|
||||
|
||||
XYZi farthest_cell = new XYZi(0, 0, 0);
|
||||
int farthest_dist = 0;
|
||||
// [*] design configurable (we basically want it to be as far away as it can reasonably be)
|
||||
const int max_dist = 6;
|
||||
// [!] given how it loops over the space it directionally biases the results
|
||||
for (int sx = -s_array.Xslen; s_array.InX(sx); sx++)
|
||||
{
|
||||
for (int sy = -s_array.Yslen; s_array.InY(sy); sy++)
|
||||
{
|
||||
for (int sz = -s_array.Zslen; s_array.InZ(sz); sz++)
|
||||
{
|
||||
XYZi sv = new XYZi(sx, sy, sz);
|
||||
int dist = head_fill[sv];
|
||||
bool good_dist = dist > farthest_dist && dist < max_dist;
|
||||
bool tail_access = tail_fill[sv] > 0; // help ensure completability
|
||||
bool snake_free = s_array[sv] == -1;
|
||||
if (good_dist && tail_access && snake_free)
|
||||
{
|
||||
farthest_dist = dist;
|
||||
farthest_cell = sv;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
food = farthest_cell;
|
||||
}
|
||||
|
||||
// space fill algorithm
|
||||
static void Gas(SpatialArray<int> fill_array, XYZi sv)
|
||||
{
|
||||
Queue<XYZi> queue = new Queue<XYZi>();
|
||||
queue.Enqueue(sv);
|
||||
fill_array[sv] = 0;
|
||||
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
XYZi _sv = queue.Dequeue();
|
||||
int currentDistance = fill_array[_sv];
|
||||
|
||||
// check all 4 directions
|
||||
foreach (XYZi dir in directions)
|
||||
{
|
||||
XYZi newV = _sv + dir;
|
||||
|
||||
if (fill_array.InRange(newV) && fill_array[newV] == -1 && s_array[newV] == -1)
|
||||
{
|
||||
fill_array[newV] = currentDistance + 1;
|
||||
queue.Enqueue(newV);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// directions for moving in the grid
|
||||
static readonly XYZi[] directions = new XYZi[]
|
||||
{
|
||||
new XYZi(-1, 0, 0), // lft
|
||||
new XYZi(+1, 0, 0), // rht
|
||||
new XYZi(0, -1, 0), // dwn
|
||||
new XYZi(0, +1, 0), // up
|
||||
new XYZi(0, 0, -1), // fwd
|
||||
new XYZi(0, 0, +1), // back
|
||||
};
|
||||
}
|
315
PassthroughFBExt.cs
Normal file
315
PassthroughFBExt.cs
Normal file
|
@ -0,0 +1,315 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// The authors below grant copyright rights under the MIT license:
|
||||
// Copyright (c) 2024 Nick Klingensmith
|
||||
// Copyright (c) 2024 Qualcomm Technologies, Inc.
|
||||
|
||||
// This requires an addition to the Android Manifest to work on quest:
|
||||
// <uses-feature android:name="com.oculus.feature.PASSTHROUGH" android:required="true" />
|
||||
// And adding this to the application section can also improve the passthrough
|
||||
// experience:
|
||||
// <meta-data android:name="com.oculus.ossplash.background" android:value="passthrough-contextual"/>
|
||||
//
|
||||
// To work on Quest+Link, you may need to enable beta features in the Oculus
|
||||
// app's settings.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace StereoKit.Framework
|
||||
{
|
||||
class PassthroughFBExt : IStepper
|
||||
{
|
||||
bool extAvailable;
|
||||
bool enabled;
|
||||
bool enableOnInitialize;
|
||||
XrPassthroughFB activePassthrough = new XrPassthroughFB();
|
||||
XrPassthroughLayerFB activeLayer = new XrPassthroughLayerFB();
|
||||
|
||||
Color oldColor;
|
||||
bool oldSky;
|
||||
|
||||
public bool Available => extAvailable;
|
||||
public bool Enabled { get => enabled; set {
|
||||
if (extAvailable == false || enabled == value) return;
|
||||
if (value)
|
||||
{
|
||||
enabled = StartPassthrough();
|
||||
}
|
||||
else
|
||||
{
|
||||
PausePassthrough();
|
||||
enabled = false;
|
||||
}
|
||||
} }
|
||||
|
||||
public PassthroughFBExt() : this(true) { }
|
||||
public PassthroughFBExt(bool enabled = true)
|
||||
{
|
||||
if (SK.IsInitialized)
|
||||
Log.Err("PassthroughFBExt must be constructed before StereoKit is initialized!");
|
||||
Backend.OpenXR.RequestExt("XR_FB_passthrough");
|
||||
enableOnInitialize = enabled;
|
||||
}
|
||||
|
||||
public bool Initialize()
|
||||
{
|
||||
extAvailable =
|
||||
Backend.XRType == BackendXRType.OpenXR &&
|
||||
Backend.OpenXR.ExtEnabled("XR_FB_passthrough") &&
|
||||
LoadBindings() &&
|
||||
InitPassthrough();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Step()
|
||||
{
|
||||
if (Enabled == false) return;
|
||||
|
||||
XrCompositionLayerPassthroughFB layer = new XrCompositionLayerPassthroughFB(
|
||||
XrCompositionLayerFlags.BLEND_TEXTURE_SOURCE_ALPHA_BIT, activeLayer);
|
||||
Backend.OpenXR.AddCompositionLayer(layer, -1);
|
||||
}
|
||||
|
||||
public void Shutdown()
|
||||
{
|
||||
if (!Enabled) return;
|
||||
Enabled = false;
|
||||
DestroyPassthrough();
|
||||
}
|
||||
|
||||
bool InitPassthrough()
|
||||
{
|
||||
XrPassthroughFlagsFB flags = enableOnInitialize
|
||||
? XrPassthroughFlagsFB.IS_RUNNING_AT_CREATION_BIT_FB
|
||||
: XrPassthroughFlagsFB.None;
|
||||
|
||||
XrResult result = xrCreatePassthroughFB(
|
||||
Backend.OpenXR.Session,
|
||||
new XrPassthroughCreateInfoFB(flags),
|
||||
out activePassthrough);
|
||||
if (result != XrResult.Success)
|
||||
{
|
||||
Log.Err($"xrCreatePassthroughFB failed: {result}");
|
||||
return false;
|
||||
}
|
||||
|
||||
result = xrCreatePassthroughLayerFB(
|
||||
Backend.OpenXR.Session,
|
||||
new XrPassthroughLayerCreateInfoFB(activePassthrough, flags, XrPassthroughLayerPurposeFB.RECONSTRUCTION_FB),
|
||||
out activeLayer);
|
||||
if (result != XrResult.Success)
|
||||
{
|
||||
Log.Err($"xrCreatePassthroughLayerFB failed: {result}");
|
||||
return false;
|
||||
}
|
||||
|
||||
enabled = enableOnInitialize;
|
||||
StartSky();
|
||||
return true;
|
||||
}
|
||||
|
||||
void DestroyPassthrough()
|
||||
{
|
||||
xrDestroyPassthroughLayerFB(activeLayer);
|
||||
xrDestroyPassthroughFB(activePassthrough);
|
||||
}
|
||||
|
||||
bool StartPassthrough()
|
||||
{
|
||||
XrResult result = xrPassthroughStartFB(activePassthrough);
|
||||
if (result != XrResult.Success)
|
||||
{
|
||||
Log.Err($"xrPassthroughStartFB failed: {result}");
|
||||
return false;
|
||||
}
|
||||
|
||||
result = xrPassthroughLayerResumeFB(activeLayer);
|
||||
if (result != XrResult.Success)
|
||||
{
|
||||
Log.Err($"xrPassthroughLayerResumeFB failed: {result}");
|
||||
return false;
|
||||
}
|
||||
|
||||
StartSky();
|
||||
return true;
|
||||
}
|
||||
|
||||
void StartSky()
|
||||
{
|
||||
oldColor = Renderer.ClearColor;
|
||||
oldSky = Renderer.EnableSky;
|
||||
Renderer.ClearColor = Color.BlackTransparent;
|
||||
Renderer.EnableSky = false;
|
||||
}
|
||||
|
||||
void PausePassthrough()
|
||||
{
|
||||
XrResult result = xrPassthroughLayerPauseFB(activeLayer);
|
||||
if (result != XrResult.Success)
|
||||
{
|
||||
Log.Err($"xrPassthroughLayerPauseFB failed: {result}");
|
||||
return;
|
||||
}
|
||||
|
||||
result = xrPassthroughPauseFB(activePassthrough);
|
||||
if (result != XrResult.Success)
|
||||
{
|
||||
Log.Err($"xrPassthroughPauseFB failed: {result}");
|
||||
return;
|
||||
}
|
||||
|
||||
Renderer.ClearColor = oldColor;
|
||||
Renderer.EnableSky = oldSky;
|
||||
}
|
||||
|
||||
#region OpenXR native bindings and types
|
||||
enum XrStructureType : UInt64
|
||||
{
|
||||
XR_TYPE_PASSTHROUGH_CREATE_INFO_FB = 1000118001,
|
||||
XR_TYPE_PASSTHROUGH_LAYER_CREATE_INFO_FB = 1000118002,
|
||||
XR_TYPE_PASSTHROUGH_STYLE_FB = 1000118020,
|
||||
XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_FB = 1000118003,
|
||||
}
|
||||
enum XrPassthroughFlagsFB : UInt64
|
||||
{
|
||||
None = 0,
|
||||
IS_RUNNING_AT_CREATION_BIT_FB = 0x00000001,
|
||||
LAYER_DEPTH_BIT_FB = 0x00000002
|
||||
}
|
||||
enum XrCompositionLayerFlags : UInt64
|
||||
{
|
||||
None = 0,
|
||||
CORRECT_CHROMATIC_ABERRATION_BIT = 0x00000001,
|
||||
BLEND_TEXTURE_SOURCE_ALPHA_BIT = 0x00000002,
|
||||
UNPREMULTIPLIED_ALPHA_BIT = 0x00000004,
|
||||
}
|
||||
enum XrPassthroughLayerPurposeFB : UInt32
|
||||
{
|
||||
RECONSTRUCTION_FB = 0,
|
||||
PROJECTED_FB = 1,
|
||||
TRACKED_KEYBOARD_HANDS_FB = 1000203001,
|
||||
MAX_ENUM_FB = 0x7FFFFFFF,
|
||||
}
|
||||
enum XrResult : Int32
|
||||
{
|
||||
Success = 0,
|
||||
}
|
||||
|
||||
#pragma warning disable 0169 // handle is not "used", but required for interop
|
||||
[StructLayout(LayoutKind.Sequential)] struct XrPassthroughFB { ulong handle; }
|
||||
[StructLayout(LayoutKind.Sequential)] struct XrPassthroughLayerFB { ulong handle; }
|
||||
#pragma warning restore 0169
|
||||
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct XrPassthroughCreateInfoFB
|
||||
{
|
||||
private XrStructureType type;
|
||||
public IntPtr next;
|
||||
public XrPassthroughFlagsFB flags;
|
||||
|
||||
public XrPassthroughCreateInfoFB(XrPassthroughFlagsFB passthroughFlags)
|
||||
{
|
||||
type = XrStructureType.XR_TYPE_PASSTHROUGH_CREATE_INFO_FB;
|
||||
next = IntPtr.Zero;
|
||||
flags = passthroughFlags;
|
||||
}
|
||||
}
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct XrPassthroughLayerCreateInfoFB
|
||||
{
|
||||
private XrStructureType type;
|
||||
public IntPtr next;
|
||||
public XrPassthroughFB passthrough;
|
||||
public XrPassthroughFlagsFB flags;
|
||||
public XrPassthroughLayerPurposeFB purpose;
|
||||
|
||||
public XrPassthroughLayerCreateInfoFB(XrPassthroughFB passthrough, XrPassthroughFlagsFB flags, XrPassthroughLayerPurposeFB purpose)
|
||||
{
|
||||
type = XrStructureType.XR_TYPE_PASSTHROUGH_LAYER_CREATE_INFO_FB;
|
||||
next = IntPtr.Zero;
|
||||
this.passthrough = passthrough;
|
||||
this.flags = flags;
|
||||
this.purpose = purpose;
|
||||
}
|
||||
}
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct XrPassthroughStyleFB
|
||||
{
|
||||
public XrStructureType type;
|
||||
public IntPtr next;
|
||||
public float textureOpacityFactor;
|
||||
public Color edgeColor;
|
||||
public XrPassthroughStyleFB(float textureOpacityFactor, Color edgeColor)
|
||||
{
|
||||
type = XrStructureType.XR_TYPE_PASSTHROUGH_STYLE_FB;
|
||||
next = IntPtr.Zero;
|
||||
this.textureOpacityFactor = textureOpacityFactor;
|
||||
this.edgeColor = edgeColor;
|
||||
}
|
||||
}
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct XrCompositionLayerPassthroughFB
|
||||
{
|
||||
public XrStructureType type;
|
||||
public IntPtr next;
|
||||
public XrCompositionLayerFlags flags;
|
||||
public ulong space;
|
||||
public XrPassthroughLayerFB layerHandle;
|
||||
public XrCompositionLayerPassthroughFB(XrCompositionLayerFlags flags, XrPassthroughLayerFB layerHandle)
|
||||
{
|
||||
type = XrStructureType.XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_FB;
|
||||
next = IntPtr.Zero;
|
||||
space = 0;
|
||||
this.flags = flags;
|
||||
this.layerHandle = layerHandle;
|
||||
}
|
||||
}
|
||||
|
||||
delegate XrResult del_xrCreatePassthroughFB (ulong session, [In] XrPassthroughCreateInfoFB createInfo, out XrPassthroughFB outPassthrough);
|
||||
delegate XrResult del_xrDestroyPassthroughFB (XrPassthroughFB passthrough);
|
||||
delegate XrResult del_xrPassthroughStartFB (XrPassthroughFB passthrough);
|
||||
delegate XrResult del_xrPassthroughPauseFB (XrPassthroughFB passthrough);
|
||||
delegate XrResult del_xrCreatePassthroughLayerFB (ulong session, [In] XrPassthroughLayerCreateInfoFB createInfo, out XrPassthroughLayerFB outLayer);
|
||||
delegate XrResult del_xrDestroyPassthroughLayerFB (XrPassthroughLayerFB layer);
|
||||
delegate XrResult del_xrPassthroughLayerPauseFB (XrPassthroughLayerFB layer);
|
||||
delegate XrResult del_xrPassthroughLayerResumeFB (XrPassthroughLayerFB layer);
|
||||
delegate XrResult del_xrPassthroughLayerSetStyleFB(XrPassthroughLayerFB layer, [In] XrPassthroughStyleFB style);
|
||||
|
||||
del_xrCreatePassthroughFB xrCreatePassthroughFB;
|
||||
del_xrDestroyPassthroughFB xrDestroyPassthroughFB;
|
||||
del_xrPassthroughStartFB xrPassthroughStartFB;
|
||||
del_xrPassthroughPauseFB xrPassthroughPauseFB;
|
||||
del_xrCreatePassthroughLayerFB xrCreatePassthroughLayerFB;
|
||||
del_xrDestroyPassthroughLayerFB xrDestroyPassthroughLayerFB;
|
||||
del_xrPassthroughLayerPauseFB xrPassthroughLayerPauseFB;
|
||||
del_xrPassthroughLayerResumeFB xrPassthroughLayerResumeFB;
|
||||
del_xrPassthroughLayerSetStyleFB xrPassthroughLayerSetStyleFB;
|
||||
|
||||
bool LoadBindings()
|
||||
{
|
||||
xrCreatePassthroughFB = Backend.OpenXR.GetFunction<del_xrCreatePassthroughFB> ("xrCreatePassthroughFB");
|
||||
xrDestroyPassthroughFB = Backend.OpenXR.GetFunction<del_xrDestroyPassthroughFB> ("xrDestroyPassthroughFB");
|
||||
xrPassthroughStartFB = Backend.OpenXR.GetFunction<del_xrPassthroughStartFB> ("xrPassthroughStartFB");
|
||||
xrPassthroughPauseFB = Backend.OpenXR.GetFunction<del_xrPassthroughPauseFB> ("xrPassthroughPauseFB");
|
||||
xrCreatePassthroughLayerFB = Backend.OpenXR.GetFunction<del_xrCreatePassthroughLayerFB> ("xrCreatePassthroughLayerFB");
|
||||
xrDestroyPassthroughLayerFB = Backend.OpenXR.GetFunction<del_xrDestroyPassthroughLayerFB> ("xrDestroyPassthroughLayerFB");
|
||||
xrPassthroughLayerPauseFB = Backend.OpenXR.GetFunction<del_xrPassthroughLayerPauseFB> ("xrPassthroughLayerPauseFB");
|
||||
xrPassthroughLayerResumeFB = Backend.OpenXR.GetFunction<del_xrPassthroughLayerResumeFB> ("xrPassthroughLayerResumeFB");
|
||||
xrPassthroughLayerSetStyleFB = Backend.OpenXR.GetFunction<del_xrPassthroughLayerSetStyleFB>("xrPassthroughLayerSetStyleFB");
|
||||
|
||||
return
|
||||
xrCreatePassthroughFB != null &&
|
||||
xrDestroyPassthroughFB != null &&
|
||||
xrPassthroughStartFB != null &&
|
||||
xrPassthroughPauseFB != null &&
|
||||
xrCreatePassthroughLayerFB != null &&
|
||||
xrDestroyPassthroughLayerFB != null &&
|
||||
xrPassthroughLayerPauseFB != null &&
|
||||
xrPassthroughLayerResumeFB != null &&
|
||||
xrPassthroughLayerSetStyleFB != null;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
55
Platforms/Android/AndroidManifest.xml
Normal file
55
Platforms/Android/AndroidManifest.xml
Normal file
|
@ -0,0 +1,55 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-sdk android:minSdkVersion="29" android:targetSdkVersion="32" />
|
||||
|
||||
<application android:allowBackup="true" android:icon="@mipmap/appicon" android:label="@string/app_name" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true">
|
||||
<!-- Oculus -->
|
||||
<meta-data android:name="com.oculus.supportedDevices" android:value="quest|quest2|quest3|questpro"/>
|
||||
<meta-data android:name="com.oculus.handtracking.version" android:value="V2.0"/>
|
||||
<meta-data android:name="com.oculus.ossplash.background" android:value="passthrough-contextual"/>
|
||||
<uses-native-library android:name="libopenxr_forwardloader.oculus.so" android:required="false"/>
|
||||
<!-- Pico -->
|
||||
<meta-data android:name="pvr.app.type" android:value="vr" />
|
||||
<meta-data android:name="handtracking" android:value="1" />
|
||||
<!-- Snapdragon -->
|
||||
<meta-data android:name="spaces.version" android:value="0.15.0"/>
|
||||
</application>
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT" />
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||
|
||||
<!-- Generic OpenXR items -->
|
||||
<uses-feature android:name="android.hardware.vr.headtracking" android:required="false" android:version="1" />
|
||||
<uses-permission android:name="org.khronos.openxr.permission.OPENXR_SYSTEM"/>
|
||||
<uses-permission android:name="org.khronos.openxr.permission.OPENXR" />
|
||||
<queries>
|
||||
<provider android:authorities="org.khronos.openxr.runtime_broker;org.khronos.openxr.system_runtime_broker" />
|
||||
<intent> <action android:name="org.khronos.openxr.OpenXRRuntimeService" /> </intent>
|
||||
<intent> <action android:name="org.khronos.openxr.OpenXRApiLayerService" /> </intent>
|
||||
</queries>
|
||||
|
||||
<!-- Vive specific items -->
|
||||
<uses-feature android:name="wave.feature.handtracking" android:required="false"/>
|
||||
<uses-feature android:name="wave.feature.tracker" android:required="false"/>
|
||||
|
||||
<!-- Snapdragon specific items -->
|
||||
<uses-permission android:name="com.qualcomm.qti.qxr.QXRServiceClientPermission" android:required="false"/>
|
||||
<queries>
|
||||
<package android:name="com.qualcomm.qti.spaces.services" />
|
||||
<package android:name="com.qualcomm.qti.openxrruntime" />
|
||||
<intent> <action android:name="com.qualcomm.qti.openxr.spaces.intent.action.BIND" /> </intent>
|
||||
</queries>
|
||||
|
||||
<!-- Oculus specific items -->
|
||||
<uses-permission android:name="com.oculus.permission.HAND_TRACKING"/>
|
||||
<uses-permission android:name="com.oculus.permission.BODY_TRACKING"/>
|
||||
<uses-permission android:name="com.oculus.permission.FACE_TRACKING"/>
|
||||
<uses-permission android:name="com.oculus.permission.EYE_TRACKING"/>
|
||||
<uses-feature android:name="com.oculus.feature.PASSTHROUGH" android:required="true"/>
|
||||
<uses-feature android:name="oculus.software.handtracking" android:required="false"/>
|
||||
<uses-feature android:name="com.oculus.software.body_tracking" android:required="false"/>
|
||||
<uses-feature android:name="oculus.software.face_tracking" android:required="false"/>
|
||||
<uses-feature android:name="oculus.software.eye_tracking" android:required="false"/>
|
||||
</manifest>
|
87
Platforms/Android/MainActivity.cs
Normal file
87
Platforms/Android/MainActivity.cs
Normal file
|
@ -0,0 +1,87 @@
|
|||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.Graphics;
|
||||
using Android.OS;
|
||||
using Android.Runtime;
|
||||
using Android.Views;
|
||||
using StereoKit;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
|
||||
namespace snake;
|
||||
|
||||
[Activity(Label = "@string/app_name", MainLauncher = true, Exported = true)]
|
||||
[IntentFilter(new[] { Intent.ActionMain }, Categories = new[] { "org.khronos.openxr.intent.category.IMMERSIVE_HMD", "com.oculus.intent.category.VR", Intent.CategoryLauncher })]
|
||||
public class MainActivity : Activity, ISurfaceHolderCallback2
|
||||
{
|
||||
View surface;
|
||||
|
||||
protected override void OnCreate(Bundle savedInstanceState)
|
||||
{
|
||||
base.OnCreate(savedInstanceState);
|
||||
Run();
|
||||
SetContentView(Resource.Layout.activity_main);
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
SK.Quit();
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
static bool running = false;
|
||||
void Run()
|
||||
{
|
||||
if (running) return;
|
||||
running = true;
|
||||
|
||||
// Before anything else, give StereoKit the Activity. This should
|
||||
// be set before any other SK calls, otherwise native library
|
||||
// loading may fail.
|
||||
SK.AndroidActivity = this;
|
||||
|
||||
// Set up a surface for StereoKit to draw on, this is only really
|
||||
// important for flatscreen experiences.
|
||||
Window.TakeSurface(this);
|
||||
Window.SetFormat(Format.Unknown);
|
||||
surface = new View(this);
|
||||
SetContentView(surface);
|
||||
surface.RequestFocus();
|
||||
|
||||
// Task.Run will eat exceptions, but Thread.Start doesn't seem to.
|
||||
new Thread(InvokeStereoKit).Start();
|
||||
}
|
||||
|
||||
static void InvokeStereoKit()
|
||||
{
|
||||
Type entryClass = typeof(Program);
|
||||
MethodInfo entryPoint = entryClass?.GetMethod("Main", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
|
||||
// There are a number of potential method signatures for Main, so
|
||||
// we need to check each one, and give it the correct values.
|
||||
//
|
||||
// Converting MethodInfo into an Action instead of calling Invoke on
|
||||
// it allows for exceptions to properly bubble up to the IDE.
|
||||
ParameterInfo[] entryParams = entryPoint?.GetParameters();
|
||||
if (entryParams == null || entryParams.Length == 0)
|
||||
{
|
||||
Action Program_Main = (Action)Delegate.CreateDelegate(typeof(Action), entryPoint);
|
||||
Program_Main();
|
||||
}
|
||||
else if (entryParams?.Length == 1 && entryParams[0].ParameterType == typeof(string[]))
|
||||
{
|
||||
Action<string[]> Program_Main = (Action<string[]>)Delegate.CreateDelegate(typeof(Action<string[]>), entryPoint);
|
||||
Program_Main(new string[] { });
|
||||
}
|
||||
else throw new Exception("Couldn't invoke Program.Main!");
|
||||
|
||||
Process.KillProcess(Process.MyPid());
|
||||
}
|
||||
|
||||
// Events related to surface state changes
|
||||
public void SurfaceChanged (ISurfaceHolder holder, [GeneratedEnum] Format format, int width, int height) => SK.SetWindow(holder.Surface.Handle);
|
||||
public void SurfaceCreated (ISurfaceHolder holder) => SK.SetWindow(holder.Surface.Handle);
|
||||
public void SurfaceDestroyed (ISurfaceHolder holder) => SK.SetWindow(IntPtr.Zero);
|
||||
public void SurfaceRedrawNeeded(ISurfaceHolder holder) { }
|
||||
}
|
47
Platforms/Android/Resources/AboutResources.txt
Normal file
47
Platforms/Android/Resources/AboutResources.txt
Normal file
|
@ -0,0 +1,47 @@
|
|||
Images, layout descriptions, binary blobs and string dictionaries can be included
|
||||
in your application as resource files. Various Android APIs are designed to
|
||||
operate on the resource IDs instead of dealing with images, strings or binary blobs
|
||||
directly.
|
||||
|
||||
For example, a sample Android app that contains a user interface layout (main.xml),
|
||||
an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png)
|
||||
would keep its resources in the "Resources" directory of the application:
|
||||
|
||||
Resources/
|
||||
drawable/
|
||||
icon.png
|
||||
|
||||
layout/
|
||||
main.xml
|
||||
|
||||
values/
|
||||
strings.xml
|
||||
|
||||
For icons, a tool like https://icon.kitchen can be helpful to accelerate the
|
||||
process of getting all the correct size variants!
|
||||
|
||||
In order to get the build system to recognize Android resources, set the build action to
|
||||
"AndroidResource". The native Android APIs do not operate directly with filenames, but
|
||||
instead operate on resource IDs. When you compile an Android application that uses resources,
|
||||
the build system will package the resources for distribution and generate a class called "Resource"
|
||||
(this is an Android convention) that contains the tokens for each one of the resources
|
||||
included. For example, for the above Resources layout, this is what the Resource class would expose:
|
||||
|
||||
public class Resource {
|
||||
public class Drawable {
|
||||
public const int icon = 0x123;
|
||||
}
|
||||
|
||||
public class Layout {
|
||||
public const int main = 0x456;
|
||||
}
|
||||
|
||||
public class Strings {
|
||||
public const int first_string = 0xabc;
|
||||
public const int second_string = 0xbcd;
|
||||
}
|
||||
}
|
||||
|
||||
You would then use Resource.Drawable.icon to reference the drawable/icon.png file, or
|
||||
Resource.Layout.main to reference the layout/main.xml file, or Resource.Strings.first_string
|
||||
to reference the first string in the dictionary file values/strings.xml.
|
13
Platforms/Android/Resources/layout/activity_main.xml
Normal file
13
Platforms/Android/Resources/layout/activity_main.xml
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:text="@string/app_text"
|
||||
/>
|
||||
</RelativeLayout>
|
|
@ -0,0 +1,4 @@
|
|||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@mipmap/appicon_background" />
|
||||
<foreground android:drawable="@mipmap/appicon_foreground" />
|
||||
</adaptive-icon>
|
|
@ -0,0 +1,4 @@
|
|||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@mipmap/appicon_background" />
|
||||
<foreground android:drawable="@mipmap/appicon_foreground" />
|
||||
</adaptive-icon>
|
BIN
Platforms/Android/Resources/mipmap-hdpi/appicon_background.png
(Stored with Git LFS)
Normal file
BIN
Platforms/Android/Resources/mipmap-hdpi/appicon_background.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Platforms/Android/Resources/mipmap-hdpi/appicon_foreground.png
(Stored with Git LFS)
Normal file
BIN
Platforms/Android/Resources/mipmap-hdpi/appicon_foreground.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Platforms/Android/Resources/mipmap-mdpi/appicon_background.png
(Stored with Git LFS)
Normal file
BIN
Platforms/Android/Resources/mipmap-mdpi/appicon_background.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Platforms/Android/Resources/mipmap-mdpi/appicon_foreground.png
(Stored with Git LFS)
Normal file
BIN
Platforms/Android/Resources/mipmap-mdpi/appicon_foreground.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Platforms/Android/Resources/mipmap-xhdpi/appicon_background.png
(Stored with Git LFS)
Normal file
BIN
Platforms/Android/Resources/mipmap-xhdpi/appicon_background.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Platforms/Android/Resources/mipmap-xhdpi/appicon_foreground.png
(Stored with Git LFS)
Normal file
BIN
Platforms/Android/Resources/mipmap-xhdpi/appicon_foreground.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Platforms/Android/Resources/mipmap-xxhdpi/appicon_background.png
(Stored with Git LFS)
Normal file
BIN
Platforms/Android/Resources/mipmap-xxhdpi/appicon_background.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Platforms/Android/Resources/mipmap-xxhdpi/appicon_foreground.png
(Stored with Git LFS)
Normal file
BIN
Platforms/Android/Resources/mipmap-xxhdpi/appicon_foreground.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Platforms/Android/Resources/mipmap-xxxhdpi/appicon_background.png
(Stored with Git LFS)
Normal file
BIN
Platforms/Android/Resources/mipmap-xxxhdpi/appicon_background.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Platforms/Android/Resources/mipmap-xxxhdpi/appicon_foreground.png
(Stored with Git LFS)
Normal file
BIN
Platforms/Android/Resources/mipmap-xxxhdpi/appicon_foreground.png
(Stored with Git LFS)
Normal file
Binary file not shown.
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#2C3E50</color>
|
||||
</resources>
|
4
Platforms/Android/Resources/values/strings.xml
Normal file
4
Platforms/Android/Resources/values/strings.xml
Normal file
|
@ -0,0 +1,4 @@
|
|||
<resources>
|
||||
<string name="app_name">snake</string>
|
||||
<string name="app_text">snake</string>
|
||||
</resources>
|
BIN
Platforms/Net/App.ico
Normal file
BIN
Platforms/Net/App.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 110 KiB |
BIN
Platforms/Net/SKApp.ico
Normal file
BIN
Platforms/Net/SKApp.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 38 KiB |
58
Program.cs
Normal file
58
Program.cs
Normal file
|
@ -0,0 +1,58 @@
|
|||
using StereoKit;
|
||||
using StereoKit.Framework;
|
||||
|
||||
|
||||
namespace snake;
|
||||
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
SK.AddStepper<PassthroughFBExt>();
|
||||
|
||||
// Initialize StereoKit
|
||||
SKSettings settings = new SKSettings
|
||||
{
|
||||
appName = "snake",
|
||||
assetsFolder = "Assets",
|
||||
blendPreference = DisplayBlend.Blend,
|
||||
// overlayApp = true,
|
||||
// overlayPriority = 1,
|
||||
depthMode = DepthMode.D32,
|
||||
disableFlatscreenMRSim = true,
|
||||
renderScaling = 2,
|
||||
renderMultisample = 0,
|
||||
};
|
||||
|
||||
if (!SK.Initialize(settings))
|
||||
return;
|
||||
|
||||
Renderer.Scaling = 2;
|
||||
// World.OcclusionEnabled = true;
|
||||
// Device.DisplayBlend = DisplayBlend.Blend;
|
||||
|
||||
Mono.Init();
|
||||
Arts.Init();
|
||||
|
||||
// Core application loop
|
||||
SK.Run(() =>
|
||||
{
|
||||
Mono.Frame();
|
||||
|
||||
// stepper
|
||||
if (Time.Total > 3.0)
|
||||
{
|
||||
Mono.step_time += Time.Step;
|
||||
Mono.step_t = Maths.min(Mono.step_time, Mono.step_step) / Mono.step_step;
|
||||
}
|
||||
if (Mono.step_time > Mono.step_step)
|
||||
{
|
||||
Mono.step_time -= Mono.step_step;
|
||||
|
||||
Mono.Step();
|
||||
}
|
||||
|
||||
Arts.Frame();
|
||||
});
|
||||
}
|
||||
}
|
50
Projects/Android/snake.Android.csproj
Normal file
50
Projects/Android/snake.Android.csproj
Normal file
|
@ -0,0 +1,50 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0-android</TargetFramework>
|
||||
<RuntimeIdentifiers>android-arm64</RuntimeIdentifiers>
|
||||
<SupportedOSPlatformVersion>29</SupportedOSPlatformVersion>
|
||||
<OutputType>Exe</OutputType>
|
||||
<ApplicationId>com.dofdev.snake</ApplicationId>
|
||||
<ApplicationVersion>1</ApplicationVersion>
|
||||
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
|
||||
<RootNamespace>snake</RootNamespace>
|
||||
<ReadOnlyProject>true</ReadOnlyProject>
|
||||
|
||||
<SKOpenXRLoader>Standard</SKOpenXRLoader>
|
||||
<SKAssetFolder>..\..\Assets</SKAssetFolder>
|
||||
<SKAssetDestination>Assets</SKAssetDestination>
|
||||
|
||||
<!--Suppress targetSdkVersion 29 warnings. XR devices use old versions
|
||||
of Android.-->
|
||||
<NoWarn>XA4211;XA1006;XA4301</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="StereoKit" Version="0.3.9" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--Point Android to the right folders, otherwise these default to the root
|
||||
directory.-->
|
||||
<PropertyGroup>
|
||||
<AndroidManifest >..\..\Platforms\Android\AndroidManifest.xml</AndroidManifest>
|
||||
<MonoAndroidResourcePrefix>..\..\Platforms\Android\Resources</MonoAndroidResourcePrefix>
|
||||
<MonoAndroidAssetsPrefix >..\..\Platforms\Android\Assets</MonoAndroidAssetsPrefix>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\..\**\*.cs" Exclude="..\..\obj\**;..\..\bin\**;..\..\Projects\**" />
|
||||
|
||||
<!--Don't compile .NET Core code in this project.-->
|
||||
<Compile Remove="..\..\**\*.Net.cs" />
|
||||
<Compile Remove="..\..\Platforms\Net\**\*.cs" />
|
||||
|
||||
<!--Hide everything in this project, otherwise it's a lot of
|
||||
duplication that creates noise.-->
|
||||
<Compile Update="..\..\**" Visible="false" />
|
||||
<None Update="..\..\**" Visible="false" />
|
||||
<Content Update="..\..\**" Visible="false" />
|
||||
|
||||
<Compile Update="..\..\**\*.Android.cs" Visible="true" />
|
||||
<Compile Update="..\..\Platforms\Android\**\*.cs" Visible="true" />
|
||||
</ItemGroup>
|
||||
</Project>
|
56
android_icons.sh
Executable file
56
android_icons.sh
Executable file
|
@ -0,0 +1,56 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Check if input file is provided
|
||||
if [ $# -eq 0 ]; then
|
||||
echo "Usage: $0 <input.svg>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
input_file="$1"
|
||||
# filename=$(basename "$input_file" .svg)
|
||||
filename="appicon_foreground"
|
||||
output_dir="Platforms/Android/Resources"
|
||||
|
||||
# DPI configurations
|
||||
# Format: [directory_name, scale_percentage]
|
||||
declare -A dpi_configs=(
|
||||
["mipmap-mdpi"]="100"
|
||||
["mipmap-hdpi"]="150"
|
||||
["mipmap-xhdpi"]="200"
|
||||
["mipmap-xxhdpi"]="300"
|
||||
["mipmap-xxxhdpi"]="400"
|
||||
)
|
||||
|
||||
# Check if required tools are installed
|
||||
if ! command -v inkscape &> /dev/null; then
|
||||
echo "Error: Inkscape is required but not installed. Install using:"
|
||||
echo "sudo pacman -S inkscape"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create output directory structure
|
||||
mkdir -p "$output_dir"
|
||||
for dpi in "${!dpi_configs[@]}"; do
|
||||
mkdir -p "$output_dir/$dpi"
|
||||
done
|
||||
|
||||
# Get base size - let's use 48px as default mdpi size for app icons
|
||||
# You can adjust this base size as needed
|
||||
BASE_SIZE=48
|
||||
|
||||
# Export for each DPI
|
||||
for dpi in "${!dpi_configs[@]}"; do
|
||||
scale=${dpi_configs[$dpi]}
|
||||
# Calculate new size using pure bash arithmetic
|
||||
new_size=$(( BASE_SIZE * scale / 100 ))
|
||||
|
||||
echo "Converting for $dpi (scale: ${scale}%)"
|
||||
|
||||
inkscape --export-type="png" \
|
||||
--export-filename="$output_dir/$dpi/${filename}.png" \
|
||||
--export-width="$new_size" \
|
||||
--export-height="$new_size" \
|
||||
"$input_file"
|
||||
done
|
||||
|
||||
echo "Conversion complete! Files exported to $output_dir/"
|
49
readme.md
Normal file
49
readme.md
Normal file
|
@ -0,0 +1,49 @@
|
|||
[lynx r1 resource](https://github.com/technobaboo/stereokit_lynx_dotnet_template)
|
||||
[fb passthrough resource](https://www.aesiio.com/blog/passthrough-dot-net-core)
|
||||
[fb passthrough tool source](https://github.com/StereoKit/StereoKit/blob/master/Examples/StereoKitTest/Tools/PassthroughFBExt.cs)
|
||||
|
||||
```shell
|
||||
# track lfs
|
||||
git lfs track "*.mp3"
|
||||
git lfs track "*.glb"
|
||||
git lfs track "*.png"
|
||||
|
||||
ffmpeg -i input.ogg output.wav
|
||||
|
||||
# update icon from svg
|
||||
magick -background none -density 256x256 Raw/icon.svg -define icon:auto-resize=256,128,64,48,32,16 Platforms/Net/App.ico
|
||||
./android_icons.sh Raw/icon.svg
|
||||
|
||||
adb devices
|
||||
adb tcpip 5555
|
||||
adb shell ip -f inet addr show wlan0
|
||||
adb connect 192.168.1.219
|
||||
|
||||
# publish build
|
||||
dotnet publish -c Release Projects/Android/snake.Android.csproj
|
||||
|
||||
# stream install
|
||||
adb install Projects/Android/bin/Release/net7.0-android/com.dofdev.snake-Signed.apk
|
||||
|
||||
# debug shell
|
||||
adb shell monkey -p com.dofdev.snake 1
|
||||
|
||||
# install a specific android platform
|
||||
sdkmanager "platforms;android-33" "build-tools;33.0.0"
|
||||
```
|
||||
|
||||
```
|
||||
todo
|
||||
track steps out of box
|
||||
if > x then snake get's pulled/slips out of the box
|
||||
|
||||
check to see how far out of box (with some warning)
|
||||
if (too far out || more segments out of box than in)than slip out of box from gravity
|
||||
if going up slipping out due to gravity doesn't make sense so
|
||||
just prevent the snake from going too far up instead (soft pause)?
|
||||
|
||||
this means gravity could affect things even when in box
|
||||
and breaking through the box and coiling up on yourself allows you to fill the space strategically
|
||||
the food could even be affected by gravity so everything kind of starts of 2d
|
||||
and 3d feels like an awesome treat/clever trick the user can play rather than an overwhelming paradigm shift
|
||||
```
|
25
snake.csproj
Normal file
25
snake.csproj
Normal file
|
@ -0,0 +1,25 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<OutputType>Exe</OutputType>
|
||||
|
||||
<ApplicationIcon>Platforms\Net\App.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!--Don't compile Android code in this project, but do show it in the
|
||||
explorer window.-->
|
||||
<Compile Remove="**\*.Android.cs" />
|
||||
<Compile Remove="Platforms\Android\**" />
|
||||
<None Include="**\*.Android.cs" />
|
||||
<None Include="Platforms\Android\**" />
|
||||
|
||||
<Compile Remove="Projects\**" />
|
||||
<EmbeddedResource Remove="Projects\**" />
|
||||
<None Remove="Projects\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="StereoKit" Version="0.3.9" />
|
||||
</ItemGroup>
|
||||
</Project>
|
Loading…
Add table
Reference in a new issue