Compare commits

...

15 commits

13 changed files with 237 additions and 118 deletions

10
.gitignore vendored
View file

@ -1,4 +1,12 @@
.env
signing.private.props
release.keystore
ovr-platform-util
bin/ bin/
obj/ obj/
Raw/ Raw/
anchors.txt

BIN
Assets/meshes/assets.glb (Stored with Git LFS)

Binary file not shown.

BIN
Assets/sfx/click.mp3 (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -1,55 +1,90 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest
<uses-sdk android:minSdkVersion="29" android:targetSdkVersion="32" /> xmlns:android="http://schemas.android.com/apk/res/android"
package="com.dofdev.snake"
<application android:allowBackup="true" android:icon="@mipmap/appicon" android:label="@string/app_name" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true"> android:versionCode="7"
<!-- Oculus --> android:versionName="1.07"
<meta-data android:name="com.oculus.supportedDevices" android:value="quest|quest2|quest3|questpro"/> android:installLocation="auto"
<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-sdk android:minSdkVersion="29" android:targetSdkVersion="32" />
<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" /> <!-- Note: 0x00030001 represents OpenGL ES 3.1 -->
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-feature android:glEsVersion="0x00030001" />
<uses-permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <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 --> <!-- Generic OpenXR items -->
<uses-feature android:name="android.hardware.vr.headtracking" android:required="false" android:version="1" /> <uses-feature android:name="android.hardware.vr.headtracking" android:required="true" android:version="1" />
<uses-permission android:name="org.khronos.openxr.permission.OPENXR_SYSTEM"/> <uses-permission android:name="org.khronos.openxr.permission.OPENXR_SYSTEM" />
<uses-permission android:name="org.khronos.openxr.permission.OPENXR" /> <uses-permission android:name="org.khronos.openxr.permission.OPENXR" />
<queries> <queries>
<provider android:authorities="org.khronos.openxr.runtime_broker;org.khronos.openxr.system_runtime_broker" /> <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.OpenXRRuntimeService" /> </intent>
<intent> <action android:name="org.khronos.openxr.OpenXRApiLayerService" /> </intent> <intent> <action android:name="org.khronos.openxr.OpenXRApiLayerService" /> </intent>
</queries> </queries>
<!-- Vive specific items --> <!-- Vive specific items -->
<uses-feature android:name="wave.feature.handtracking" android:required="false"/> <uses-feature android:name="wave.feature.handtracking" android:required="false" />
<uses-feature android:name="wave.feature.tracker" android:required="false"/> <uses-feature android:name="wave.feature.tracker" android:required="false" />
<!-- Snapdragon specific items --> <!-- Snapdragon specific items -->
<uses-permission android:name="com.qualcomm.qti.qxr.QXRServiceClientPermission" android:required="false"/> <uses-permission android:name="com.qualcomm.qti.qxr.QXRServiceClientPermission" android:required="false" />
<queries> <queries>
<package android:name="com.qualcomm.qti.spaces.services" /> <package android:name="com.qualcomm.qti.spaces.services" />
<package android:name="com.qualcomm.qti.openxrruntime" /> <package android:name="com.qualcomm.qti.openxrruntime" />
<intent> <action android:name="com.qualcomm.qti.openxr.spaces.intent.action.BIND" /> </intent> <intent> <action android:name="com.qualcomm.qti.openxr.spaces.intent.action.BIND" /> </intent>
</queries> </queries>
<!-- Oculus specific items --> <!-- Oculus specific items -->
<uses-permission android:name="com.oculus.permission.HAND_TRACKING"/> <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.BODY_TRACKING" />
<uses-permission android:name="com.oculus.permission.FACE_TRACKING"/> <uses-permission android:name="com.oculus.permission.FACE_TRACKING" />
<uses-permission android:name="com.oculus.permission.EYE_TRACKING"/> <uses-permission android:name="com.oculus.permission.EYE_TRACKING" />
<uses-feature android:name="com.oculus.feature.PASSTHROUGH" android:required="true"/> <uses-permission android:name="com.oculus.permission.USE_ANCHOR_API" />
<uses-feature android:name="oculus.software.handtracking" android:required="false"/> <uses-permission android:name="com.oculus.permission.USE_SCENE" />
<uses-feature android:name="com.oculus.software.body_tracking" android:required="false"/> <uses-feature android:name="com.oculus.experimental.enabled" android:required="true" />
<uses-feature android:name="oculus.software.face_tracking" android:required="false"/> <uses-feature android:name="com.oculus.feature.PASSTHROUGH" android:required="true" />
<uses-feature android:name="oculus.software.eye_tracking" android:required="false"/> <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" />
<application
android:allowBackup="false"
android:icon="@mipmap/appicon"
android:label="Snake in a Box"
android:roundIcon="@mipmap/appicon_round"
android:supportsRtl="true"
>
<!-- Oculus -->
<meta-data android:name="com.oculus.supportedDevices" android:value="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" />
<meta-data android:name="com.oculus.vr.focusaware" android:value="true" />
<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" />
<activity
android:name="snake.MainActivity"
android:launchMode="singleTask"
android:excludeFromRecents="false"
android:screenOrientation="landscape"
android:configChanges="screenSize|screenLayout|orientation|keyboardHidden|keyboard|navigation|uiMode"
android:exported="true"
>
<!-- This filter lets the apk show up as a launchable icon. -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="com.oculus.intent.category.VR" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest> </manifest>

View file

@ -26,11 +26,14 @@
<!--Point Android to the right folders, otherwise these default to the root <!--Point Android to the right folders, otherwise these default to the root
directory.--> directory.-->
<PropertyGroup> <PropertyGroup>
<AndroidManifest >..\..\Platforms\Android\AndroidManifest.xml</AndroidManifest> <AndroidManifest>..\..\Platforms\Android\AndroidManifest.xml</AndroidManifest>
<MonoAndroidResourcePrefix>..\..\Platforms\Android\Resources</MonoAndroidResourcePrefix> <MonoAndroidResourcePrefix>..\..\Platforms\Android\Resources</MonoAndroidResourcePrefix>
<MonoAndroidAssetsPrefix >..\..\Platforms\Android\Assets</MonoAndroidAssetsPrefix> <MonoAndroidAssetsPrefix>..\..\Platforms\Android\Assets</MonoAndroidAssetsPrefix>
</PropertyGroup> </PropertyGroup>
<!-- In .csproj -->
<Import Project="$(MSBuildProjectDirectory)\signing.private.props" Condition="Exists('$(MSBuildProjectDirectory)\signing.private.props')" />
<ItemGroup> <ItemGroup>
<Compile Include="..\..\**\*.cs" Exclude="..\..\obj\**;..\..\bin\**;..\..\Projects\**" /> <Compile Include="..\..\**\*.cs" Exclude="..\..\obj\**;..\..\bin\**;..\..\Projects\**" />
@ -41,7 +44,7 @@
<!--Hide everything in this project, otherwise it's a lot of <!--Hide everything in this project, otherwise it's a lot of
duplication that creates noise.--> duplication that creates noise.-->
<Compile Update="..\..\**" Visible="false" /> <Compile Update="..\..\**" Visible="false" />
<None Update="..\..\**" Visible="false" /> <None Update="..\..\**" Visible="false" />
<Content Update="..\..\**" Visible="false" /> <Content Update="..\..\**" Visible="false" />
<Compile Update="..\..\**\*.Android.cs" Visible="true" /> <Compile Update="..\..\**\*.Android.cs" Visible="true" />

View file

@ -25,12 +25,13 @@ adb connect 192.168.1.219
# publish build # publish build
dotnet publish -c Release Projects/Android/snake.Android.csproj 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 adb install Projects/Android/bin/Release/net7.0-android/com.dofdev.snake-Signed.apk
# debug shell
adb shell monkey -p com.dofdev.snake 1 adb shell monkey -p com.dofdev.snake 1
# upload quest
source .env
./ovr-platform-util upload-quest-build -a $APP_ID -s $APP_SECRET --apk $APK_PATH -c alpha -n "message"
# install a specific android platform # install a specific android platform
sdkmanager "platforms;android-33" "build-tools;33.0.0" sdkmanager "platforms;android-33" "build-tools;33.0.0"
``` ```

View file

@ -39,14 +39,14 @@ static class Arts
Mesh.Sphere.Draw( Mesh.Sphere.Draw(
mat_unlit, mat_unlit,
Matrix.TS( Matrix.TS(
Mono.r_con_stick.position, Rig.r_con_stick.position,
5 * U.mm 5 * U.mm
), ),
Color.White Color.White
); );
Lines.Add( Lines.Add(
Mono.r_con_stick.position + V.XYZ(0, 0, 0), Rig.r_con_stick.position + V.XYZ(0, 0, 0),
Mono.r_con_stick.position + Mono.fullstick * U.cm, Rig.r_con_stick.position + Rig.fullstick * U.cm,
Color.White, Color.White,
2 * U.mm 2 * U.mm
); );
@ -54,8 +54,26 @@ static class Arts
// box // box
Hierarchy.Push(Mono.box_pose.ToMatrix(Mono.box_scale)); Hierarchy.Push(Mono.box_pose.ToMatrix(Mono.box_scale));
meshes["InsideOut"].Draw(mat_unlit, Matrix.Identity); meshes["InsideOut"].Draw(mat_unlit, Matrix.Identity);
meshes["InsideOut"].Draw(mat_backbox, Matrix.Identity);
meshes["Corrugation"].Draw(mat_unlit, Matrix.Identity); meshes["Corrugation"].Draw(mat_unlit, Matrix.Identity);
meshes["Corrugation"].Draw(mat_backbox, Matrix.Identity); if (Mono.menu)
{
meshes["Tape"].Draw(mat_mono, Matrix.Identity);
meshes["uiPlay"].Draw(
mat_unlit,
Matrix.TR(
V.XYZ(0, 0, Mono.SD_Z + 0.5f + 0.1f),
Quat.FromAngles(90, 0, 0)
)
);
meshes["uiCursor"].Draw(
mat_unlit,
Matrix.TR(
V.XYZ(0, 0, Mono.SD_Z + 0.5f + 0.2f),
Quat.FromAngles(90, 0, 0)
)
);
}
// snake // snake
float fall_t = 0.0f; float fall_t = 0.0f;
@ -72,7 +90,7 @@ static class Arts
mat_mono, mat_mono,
Matrix.TRS( Matrix.TRS(
Mono.snake[0].ToVec3 + fall, Mono.snake[0].ToVec3 + fall,
Quat.LookDir(Mono.fullstick), Quat.LookDir(Rig.fullstick),
V.XYZ(1, 1, 0.666f + Maths.smooth_stop((float)Mono.step_t) * 0.333f) V.XYZ(1, 1, 0.666f + Maths.smooth_stop((float)Mono.step_t) * 0.333f)
) )
); );

View file

@ -8,9 +8,11 @@ static class Mono
public static double step_step = 60.0 / 80; // 80|100|120 bpm public static double step_step = 60.0 / 80; // 80|100|120 bpm
public static double step_time = 0.0; public static double step_time = 0.0;
public static double step_t = 0.0; public static double step_t = 0.0;
public static bool menu = true;
public static Pose box_pose = new(0, 0, -10 * U.cm); public static Pose box_pose = new(0, 0, -10 * U.cm);
public static float box_scale = 1.333f * U.cm; public static float box_scale = 1.333f * U.cm;
public const int SD_X = 2, SD_Y = 1, SD_Z = 2; public const int SD_X = 2, SD_Y = 1, SD_Z = 2;
public static SpatialArray<int> public static SpatialArray<int>
s_array = new(SD_X, SD_Y, SD_Z, -1), s_array = new(SD_X, SD_Y, SD_Z, -1),
@ -22,9 +24,7 @@ static class Mono
]; ];
public static int snake_len = 4; public static int snake_len = 4;
public static int grow_buffer = 0; public static int grow_buffer = 0;
public static Vec3 fullstick = Vec3.Up; public static XYZi snake_dir = new(0, 0, 1);
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 in_box = new(true);
public static DeltaBool snake_fall = new(false); public static DeltaBool snake_fall = new(false);
public static Dictionary<XYZi, XYZi> holes = new(); public static Dictionary<XYZi, XYZi> holes = new();
@ -32,13 +32,6 @@ static class Mono
public static DeltaBool food_fall = new(false); public static DeltaBool food_fall = new(false);
public static double eat_timestamp = 0.0; 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() public static void Init()
{ {
for (int i = 0; i < snake.Length; i++) for (int i = 0; i < snake.Length; i++)
@ -49,60 +42,32 @@ static class Mono
public static void Frame() public static void Frame()
{ {
if (Rig.btn_trigger.delta == +1)
{
menu = !menu;
SFX.click.PlayBox(new XYZi(0, 0, Mono.SD_Z + 1));
}
// flatscreen dev controls // flatscreen dev controls
if (Device.Name == "Simulator") if (Device.Name == "Simulator")
{ {
if (Input.Key(Key.MouseLeft).IsActive()) if (Input.Key(Key.MouseCenter).IsActive())
{ {
float sx = Maths.s_scalar(Input.Mouse.pos.x / 640); float sx = Input.Mouse.posChange.x;
float ssx = Maths.smooth_start(sx); float ssx = Maths.smooth_start(sx);
box_pose.orientation *= Quat.FromAngles(0, sx * 180.0f * Time.Stepf, 0); 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 else
{ {
Pose head = Input.Head; box_pose.position = Rig.head.position + Rig.head.orientation * V.XYZ(0, -(SD_Y + 0.5f) * box_scale, -32 * U.cm);
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 // filter out neck breaking from snake_dir value set
if (snake[0] + snake_dir == snake[1]) if (snake[0] + Rig.new_dir != snake[1])
{ {
snake_dir = last_snake_dir; snake_dir = Rig.new_dir;
} }
last_snake_dir = snake_dir;
} }
public static void Step() public static void Step()
@ -141,11 +106,14 @@ static class Mono
} }
// slither // slither
for (int i = snake.Length - 1; i > 0; i--) if (!menu)
{ {
snake[i] = snake[i - 1]; for (int i = snake.Length - 1; i > 0; i--)
{
snake[i] = snake[i - 1];
}
snake[0] += snake_dir;
} }
snake[0] += snake_dir;
// gravity // gravity
bool grounded = false; bool grounded = false;
@ -180,7 +148,7 @@ static class Mono
if (in_box.delta != 0) // 1 just in -1 just out if (in_box.delta != 0) // 1 just in -1 just out
{ {
holes.Add(snake[0], snake_dir); holes.Add(snake[0], snake_dir);
sfx_punch_through.PlayBox(snake[0]); SFX.punch_through.PlayBox(snake[0]);
} }
if (holes.ContainsKey(snake[snake_len - 1])) if (holes.ContainsKey(snake[snake_len - 1]))
{ {
@ -200,7 +168,7 @@ static class Mono
grow_buffer += 3; grow_buffer += 3;
Feed(); Feed();
sfx_crisp_nom.PlayBox(snake[0]); SFX.crisp_nom.PlayBox(snake[0]);
} }
} }

View file

@ -31,12 +31,14 @@ class Program
// World.OcclusionEnabled = true; // World.OcclusionEnabled = true;
// Device.DisplayBlend = DisplayBlend.Blend; // Device.DisplayBlend = DisplayBlend.Blend;
Rig.Init();
Mono.Init(); Mono.Init();
Arts.Init(); Arts.Init();
// Core application loop // Core application loop
SK.Run(() => SK.Run(() =>
{ {
Rig.Frame();
Mono.Frame(); Mono.Frame();
// stepper // stepper

66
src/Rig.cs Normal file
View file

@ -0,0 +1,66 @@
using StereoKit;
namespace snake;
static class Rig
{
public static Pose head = Pose.Identity;
public static DeltaBool btn_trigger = new(false);
public static Vec3 fullstick = Vec3.Up;
public static Pose r_con_stick = Pose.Identity;
public static XYZi new_dir = new(0, 0, 1);
public static void Init()
{
}
public static void Frame()
{
head = Input.Head;
// flatscreen dev controls
if (Device.Name == "Simulator")
{
btn_trigger.Step(Input.Key(Key.MouseLeft).IsActive());
if (Input.Key(Key.A).IsJustActive()) new_dir = new(-1, 0, 0);
if (Input.Key(Key.S).IsJustActive()) new_dir = new(+1, 0, 0);
if (Input.Key(Key.W).IsJustActive()) new_dir = new(0, 0, -1);
if (Input.Key(Key.R).IsJustActive()) new_dir = new(0, 0, +1);
if (Input.Key(Key.Shift).IsJustActive()) new_dir = new(0, -1, 0);
if (Input.Key(Key.Space).IsJustActive()) new_dir = new(0, +1, 0);
fullstick = new_dir.ToVec3;
}
else
{
Hand r_hand = Input.Hand(Handed.Right);
Controller r_con = Input.Controller(Handed.Right);
btn_trigger.Step(r_con.trigger > 0.5f);
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) new_dir = new(Maths.sign(fullstick.x), 0, 0);
if (ay > ax && ay > az) new_dir = new(0, Maths.sign(fullstick.y), 0);
if (az > ax && az > ay) new_dir = new(0, 0, Maths.sign(fullstick.z));
}
}
}
}

15
src/SFX.cs Normal file
View file

@ -0,0 +1,15 @@
using StereoKit;
namespace snake;
static class SFX
{
public static void PlayBox(this Sound sound, XYZi pos)
{
sound.Play(Mono.box_pose.ToMatrix(Mono.box_scale) * pos.ToVec3);
}
public static Sound click = Sound.FromFile("sfx/click.mp3");
public static Sound crisp_nom = Sound.FromFile("sfx/crisp_nom.mp3");
public static Sound punch_through = Sound.FromFile("sfx/punch_through.mp3");
}