├── models
└── hands
│ ├── HandRight.fbx
│ ├── handleft.vmdl
│ └── handright.vmdl
├── .gitignore
├── code
├── UI
│ ├── ExampleHud.html
│ ├── ExampleHud.scss
│ ├── ExampleHudEntity.cs
│ └── HudEntity.cs
├── Game.cs
├── Player
│ ├── Hands
│ │ ├── LeftHand.cs
│ │ ├── RightHand.cs
│ │ └── HandEntity.cs
│ ├── PlayerAnimator.cs
│ ├── Player.cs
│ └── WalkController.cs
└── Utils
│ └── TransformExtensions.cs
├── .gitattributes
├── .addon
├── README.md
├── LICENSE
└── animgraphs
└── hands
└── handright.vanmgrph
/models/hands/HandRight.fbx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xezno/sbox-vr-minimal/HEAD/models/hands/HandRight.fbx
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | code/*.csproj
2 | code/obj
3 | code/Properties/launchSettings.json
4 | .intermediate
5 | tools_asset_info.bin
6 | _bakeresourcecache
7 | tools_thumbnail_cache.bin
8 | .vs
9 | *.*_c
10 |
--------------------------------------------------------------------------------
/code/UI/ExampleHud.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | My VR Gamemode
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Allow .vfx & .fxc files to have hlsl syntax highlighting
5 | *.vfx linguist-language=HLSL
6 | *.fxc linguist-language=HLSL
7 |
--------------------------------------------------------------------------------
/code/Game.cs:
--------------------------------------------------------------------------------
1 | using Sandbox;
2 |
3 | namespace VrExample;
4 |
5 | public partial class Game : Sandbox.Game
6 | {
7 | public Game()
8 | {
9 | if ( IsServer )
10 | {
11 | _ = new ExampleHudEntity();
12 | }
13 | }
14 |
15 | public override void ClientJoined( Client client )
16 | {
17 | base.ClientJoined( client );
18 |
19 | var player = new Player();
20 | client.Pawn = player;
21 |
22 | player.Respawn();
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/code/Player/Hands/LeftHand.cs:
--------------------------------------------------------------------------------
1 | using Sandbox;
2 |
3 | namespace VrExample;
4 |
5 | public class LeftHand : HandEntity
6 | {
7 | protected override string ModelPath => "models/hands/handleft.vmdl";
8 | public override Input.VrHand InputHand => Input.VR.LeftHand;
9 |
10 | public override void Spawn()
11 | {
12 | base.Spawn();
13 | Log.Info( "VR Controller Left Spawned" );
14 | SetInteractsAs( CollisionLayer.LEFT_HAND );
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/code/Player/Hands/RightHand.cs:
--------------------------------------------------------------------------------
1 | using Sandbox;
2 |
3 | namespace VrExample;
4 |
5 | public class RightHand : HandEntity
6 | {
7 | protected override string ModelPath => "models/hands/handright.vmdl";
8 | public override Input.VrHand InputHand => Input.VR.RightHand;
9 |
10 | public override void Spawn()
11 | {
12 | base.Spawn();
13 | Log.Info( "VR Controller Right Spawned" );
14 | SetInteractsAs( CollisionLayer.RIGHT_HAND );
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/code/Utils/TransformExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace VrExample;
2 |
3 | public static class TransformExtensions
4 | {
5 | public static Transform RotateAround( this Transform transform, Vector3 pivotPoint, Rotation rotation )
6 | {
7 | var resultTransform = transform;
8 |
9 | resultTransform.Position = rotation * (transform.Position - pivotPoint) + pivotPoint;
10 | resultTransform.Rotation = rotation * transform.Rotation;
11 |
12 | return resultTransform;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/code/UI/ExampleHud.scss:
--------------------------------------------------------------------------------
1 | .main-panel {
2 | position: absolute;
3 | top: 0;
4 | left: 0;
5 | right: 0;
6 | bottom: 0;
7 |
8 | &.is-vr {
9 | border-radius: 100px;
10 | background-color: rgba( black, 0.1 );
11 | backdrop-filter: blur( 50px );
12 | }
13 | }
14 |
15 | text {
16 | font-family: Poppins;
17 | font-weight: 400;
18 | font-size: 64px;
19 | padding: 50px;
20 | color: white;
21 | text-shadow: 0 0 10px rgba( black, 0.25 );
22 | }
23 |
--------------------------------------------------------------------------------
/code/UI/ExampleHudEntity.cs:
--------------------------------------------------------------------------------
1 | using Sandbox;
2 | using Sandbox.UI;
3 |
4 | namespace VrExample;
5 |
6 | public class ExampleHudEntity : HudEntity
7 | {
8 | public ExampleHudEntity()
9 | {
10 | if ( IsClient )
11 | {
12 | if ( Global.IsRunningInVR )
13 | {
14 | // Use a world panel - we're in VR
15 | _ = new HudEntity();
16 | }
17 | else
18 | {
19 | // Just display the HUD on-screen
20 | RootPanel.SetTemplate( "/Code/UI/ExampleHud.html" );
21 | }
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/.addon:
--------------------------------------------------------------------------------
1 | {
2 | "Title": "VR Minimal",
3 | "Type": "game",
4 | "Org": "alex",
5 | "Ident": "vrminimal",
6 | "Schema": 1,
7 | "HasAssets": true,
8 | "AssetsPath": "",
9 | "ResourcePaths": [
10 | "/code/UI/*"
11 | ],
12 | "HasCode": true,
13 | "CodePath": "code",
14 | "RootNamespace": "VrExample",
15 | "Metadata": {
16 | "ProjectTemplate": {
17 | "Icon": "panorama_photosphere"
18 | },
19 | "MapList": [
20 | "facepunch.construct"
21 | ],
22 | "PerMapRanking": false,
23 | "LeaderboardType": 0,
24 | "RankType": 0,
25 | "MapSelect": 0,
26 | "GameNetworkType": 0,
27 | "MaxPlayers": 16,
28 | "MinPlayers": 0
29 | }
30 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # sbox-vr-minimal
2 |
3 | Basic minimal setup for VR in s&box.
4 |
5 | ## How-to
6 |
7 | ### Using the template system
8 |
9 | 1. `git clone https://github.com/xezno/sbox-vr-minimal` in `sbox/templates`
10 | 2. Launch s&box with the `-vr` launch argument
11 | 3. Create your addon using the template shown in the addon manager.
12 |
13 | 
14 |
15 | ### Manually cloning & adding
16 |
17 | 1. `git clone https://github.com/xezno/sbox-vr-minimal` in your s&box addons folder
18 | 2. Launch s&box with the `-vr` launch argument
19 | 3. Add the .addon file using the s&box addon manager
20 |
21 | ## Credits
22 |
23 | Hand models by [ShadowBrain](https://github.com/ShadowBrian)
24 |
--------------------------------------------------------------------------------
/code/UI/HudEntity.cs:
--------------------------------------------------------------------------------
1 | using Sandbox;
2 | using Sandbox.UI;
3 |
4 | namespace VrExample;
5 |
6 | ///
7 | /// This will project the example HUD onto your left wrist
8 | ///
9 | public class HudEntity : WorldPanel
10 | {
11 | public HudEntity()
12 | {
13 | SetTemplate( "/Code/UI/ExampleHud.html" );
14 | SetClass( "is-vr", true );
15 | }
16 |
17 | public override void Tick()
18 | {
19 | base.Tick();
20 |
21 | if ( Local.Pawn is Player player )
22 | {
23 | Transform = player.LeftHand.Transform;
24 |
25 | //
26 | // Offsets
27 | //
28 | Rotation *= new Angles( -180, -90, 45 ).ToRotation();
29 | Position += Rotation.Forward * 5 + Rotation.Up * 6 - Rotation.Left * 12;
30 | WorldScale = 0.1f;
31 | Scale = 2.0f;
32 |
33 | PanelBounds = new Rect( 0, 0, 1920, 1080 );
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Alex Guthrie
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/code/Player/Hands/HandEntity.cs:
--------------------------------------------------------------------------------
1 | using Sandbox;
2 |
3 | namespace VrExample;
4 |
5 | public partial class HandEntity : AnimatedEntity
6 | {
7 | protected virtual string ModelPath => "";
8 |
9 | [Net] public HandEntity Other { get; set; }
10 |
11 | public bool GripPressed => InputHand.Grip > 0.5f;
12 | public bool TriggerPressed => InputHand.Trigger > 0.5f;
13 |
14 | public virtual Input.VrHand InputHand { get; }
15 |
16 | public override void Spawn()
17 | {
18 | SetModel( ModelPath );
19 |
20 | Transmit = TransmitType.Always;
21 | }
22 |
23 | public override void FrameSimulate( Client cl )
24 | {
25 | base.FrameSimulate( cl );
26 |
27 | Transform = InputHand.Transform;
28 | }
29 |
30 | public override void Simulate( Client cl )
31 | {
32 | base.Simulate( cl );
33 |
34 | Transform = InputHand.Transform;
35 | Animate();
36 | }
37 |
38 | private void Animate()
39 | {
40 | SetAnimParameter( "Index", InputHand.GetFingerCurl( 1 ) );
41 | SetAnimParameter( "Middle", InputHand.GetFingerCurl( 2 ) );
42 | SetAnimParameter( "Ring", InputHand.GetFingerCurl( 3 ) );
43 | SetAnimParameter( "Thumb", InputHand.GetFingerCurl( 0 ) );
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/code/Player/PlayerAnimator.cs:
--------------------------------------------------------------------------------
1 | using Sandbox;
2 | using System;
3 |
4 | namespace VrExample;
5 |
6 | public class PlayerAnimator : StandardPlayerAnimator
7 | {
8 | float duck;
9 |
10 | public override void Simulate()
11 | {
12 | DoWalk();
13 |
14 | //
15 | // Let the animation graph know some shit
16 | //
17 | bool sitting = HasTag( "sitting" );
18 | bool noclip = HasTag( "noclip" ) && !sitting;
19 |
20 | SetAnimParameter( "b_grounded", GroundEntity != null || noclip || sitting );
21 | SetAnimParameter( "b_noclip", noclip );
22 | SetAnimParameter( "b_sit", sitting );
23 | SetAnimParameter( "b_swim", Pawn.WaterLevel > 0.5f && !sitting );
24 |
25 | Vector3 aimPos = Pawn.EyePosition + Rotation.Forward * 256;
26 | Vector3 lookPos = Input.VR.Head.Position + Input.VR.Head.Rotation.Forward * 256;
27 |
28 | //
29 | // Look in the direction what the player's input is facing
30 | //
31 | SetLookAt( "lookat_pos", lookPos ); // old
32 | SetLookAt( "aimat_pos", aimPos ); // old
33 |
34 | SetLookAt( "aim_eyes", lookPos );
35 | SetLookAt( "aim_head", lookPos );
36 | SetLookAt( "aim_body", aimPos );
37 |
38 | SetAnimParameter( "b_ducked", HasTag( "ducked" ) ); // old
39 |
40 | if ( HasTag( "ducked" ) ) duck = duck.LerpTo( 1.0f, Time.Delta * 10.0f );
41 | else duck = duck.LerpTo( 0.0f, Time.Delta * 5.0f );
42 |
43 | if ( (Pawn as Player)?.ActiveChild is BaseCarriable carry )
44 | {
45 | carry.SimulateAnimator( this );
46 | }
47 | else
48 | {
49 | SetAnimParameter( "holdtype", 0 );
50 | SetAnimParameter( "aimat_weight", 0.5f ); // old
51 | SetAnimParameter( "aim_body_weight", 0.5f );
52 | }
53 | }
54 |
55 | void DoWalk()
56 | {
57 | // Move Speed
58 | {
59 | var dir = Velocity;
60 | var forward = Rotation.Forward.Dot( dir );
61 | var sideward = Rotation.Right.Dot( dir );
62 |
63 | var angle = MathF.Atan2( sideward, forward ).RadianToDegree().NormalizeDegrees();
64 |
65 | SetAnimParameter( "move_direction", angle );
66 | SetAnimParameter( "move_speed", Velocity.Length );
67 | SetAnimParameter( "move_groundspeed", Velocity.WithZ( 0 ).Length );
68 | SetAnimParameter( "move_y", sideward );
69 | SetAnimParameter( "move_x", forward );
70 | SetAnimParameter( "move_z", Velocity.z );
71 | }
72 |
73 | // Wish Speed
74 | {
75 | var dir = WishVelocity;
76 | var forward = Rotation.Forward.Dot( dir );
77 | var sideward = Rotation.Right.Dot( dir );
78 |
79 | var angle = MathF.Atan2( sideward, forward ).RadianToDegree().NormalizeDegrees();
80 |
81 | SetAnimParameter( "wish_direction", angle );
82 | SetAnimParameter( "wish_speed", WishVelocity.Length );
83 | SetAnimParameter( "wish_groundspeed", WishVelocity.WithZ( 0 ).Length );
84 | SetAnimParameter( "wish_y", sideward );
85 | SetAnimParameter( "wish_x", forward );
86 | SetAnimParameter( "wish_z", WishVelocity.z );
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/code/Player/Player.cs:
--------------------------------------------------------------------------------
1 | using Sandbox;
2 |
3 | namespace VrExample;
4 |
5 | partial class Player : Sandbox.Player
6 | {
7 | [Net, Local] public LeftHand LeftHand { get; set; }
8 | [Net, Local] public RightHand RightHand { get; set; }
9 |
10 | private void CreateHands()
11 | {
12 | DeleteHands();
13 |
14 | LeftHand = new() { Owner = this };
15 | RightHand = new() { Owner = this };
16 |
17 | LeftHand.Other = RightHand;
18 | RightHand.Other = LeftHand;
19 | }
20 |
21 | private void DeleteHands()
22 | {
23 | LeftHand?.Delete();
24 | RightHand?.Delete();
25 | }
26 |
27 | public override void Respawn()
28 | {
29 | SetModel( "models/citizen/citizen.vmdl" );
30 |
31 | if ( Client.IsUsingVr )
32 | {
33 | Controller = new WalkController();
34 | Animator = new PlayerAnimator();
35 | CameraMode = new FirstPersonCamera();
36 | }
37 | else
38 | {
39 | Controller = new Sandbox.WalkController();
40 | Animator = new StandardPlayerAnimator();
41 | CameraMode = new FirstPersonCamera();
42 | }
43 |
44 | EnableAllCollisions = true;
45 | EnableDrawing = true;
46 | EnableHideInFirstPerson = true;
47 | EnableShadowInFirstPerson = true;
48 |
49 | CreateHands();
50 |
51 | if ( Client.IsUsingVr )
52 | SetBodyGroup( "Hands", 1 ); // Hide hands
53 |
54 | base.Respawn();
55 | }
56 |
57 | public override void ClientSpawn()
58 | {
59 | base.ClientSpawn();
60 | }
61 |
62 | public override void Simulate( Client cl )
63 | {
64 | base.Simulate( cl );
65 | SimulateActiveChild( cl, ActiveChild );
66 |
67 | CheckRotate();
68 | SetVrAnimProperties();
69 |
70 | LeftHand?.Simulate( cl );
71 | RightHand?.Simulate( cl );
72 | }
73 |
74 | public override void FrameSimulate( Client cl )
75 | {
76 | base.FrameSimulate( cl );
77 |
78 | LeftHand?.FrameSimulate( cl );
79 | RightHand?.FrameSimulate( cl );
80 | }
81 |
82 | public void SetVrAnimProperties()
83 | {
84 | if ( LifeState != LifeState.Alive )
85 | return;
86 |
87 | if ( !Input.VR.IsActive )
88 | return;
89 |
90 | SetAnimParameter( "b_vr", true );
91 | var leftHandLocal = Transform.ToLocal( LeftHand.GetBoneTransform( 0 ) );
92 | var rightHandLocal = Transform.ToLocal( RightHand.GetBoneTransform( 0 ) );
93 |
94 | var handOffset = Vector3.Zero;
95 | SetAnimParameter( "left_hand_ik.position", leftHandLocal.Position + (handOffset * leftHandLocal.Rotation) );
96 | SetAnimParameter( "right_hand_ik.position", rightHandLocal.Position + (handOffset * rightHandLocal.Rotation) );
97 |
98 | SetAnimParameter( "left_hand_ik.rotation", leftHandLocal.Rotation * Rotation.From( 0, 0, 180 ) );
99 | SetAnimParameter( "right_hand_ik.rotation", rightHandLocal.Rotation );
100 |
101 | float height = Input.VR.Head.Position.z - Position.z;
102 | SetAnimParameter( "duck", 1.0f - ((height - 32f) / 32f) ); // This will probably need tweaking depending on height
103 | }
104 |
105 | private TimeSince timeSinceLastRotation;
106 | private void CheckRotate()
107 | {
108 | if ( !IsServer )
109 | return;
110 |
111 | const float deadzone = 0.2f;
112 | const float angle = 45f;
113 | const float delay = 0.25f;
114 |
115 | float rotate = Input.VR.RightHand.Joystick.Value.x;
116 |
117 | if ( timeSinceLastRotation > delay )
118 | {
119 | if ( rotate > deadzone )
120 | {
121 | Transform = Transform.RotateAround(
122 | Input.VR.Head.Position.WithZ( Position.z ),
123 | Rotation.FromAxis( Vector3.Up, -angle )
124 | );
125 |
126 | timeSinceLastRotation = 0;
127 | }
128 | else if ( rotate < -deadzone )
129 | {
130 | Transform = Transform.RotateAround(
131 | Input.VR.Head.Position.WithZ( Position.z ),
132 | Rotation.FromAxis( Vector3.Up, angle )
133 | );
134 |
135 | timeSinceLastRotation = 0;
136 | }
137 | }
138 |
139 | if ( rotate > -deadzone && rotate < deadzone )
140 | {
141 | timeSinceLastRotation = 10;
142 | }
143 | }
144 |
145 | public override void OnKilled()
146 | {
147 | base.OnKilled();
148 | EnableDrawing = false;
149 | DeleteHands();
150 | }
151 |
152 | public override void PostCameraSetup( ref CameraSetup setup )
153 | {
154 | // You will probably need to tweak these depending on your use case
155 | setup.ZNear = 1;
156 | setup.ZFar = 25000;
157 |
158 | base.PostCameraSetup( ref setup );
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/models/hands/handleft.vmdl:
--------------------------------------------------------------------------------
1 |
2 | {
3 | rootNode =
4 | {
5 | _class = "RootNode"
6 | children =
7 | [
8 | {
9 | _class = "MaterialGroupList"
10 | children =
11 | [
12 | {
13 | _class = "DefaultMaterialGroup"
14 | remaps = [ ]
15 | use_global_default = true
16 | global_default_material = "models/citizen/skin/citizen_skin.vmat"
17 | },
18 | ]
19 | },
20 | {
21 | _class = "RenderMeshList"
22 | children =
23 | [
24 | {
25 | _class = "RenderMeshFile"
26 | filename = "models/hands/HandRight.fbx"
27 | import_translation = [ 0.0, 0.0, 0.0 ]
28 | import_rotation = [ 0.0, 0.0, 0.0 ]
29 | import_scale = 1.0
30 | align_origin_x_type = "None"
31 | align_origin_y_type = "None"
32 | align_origin_z_type = "None"
33 | parent_bone = ""
34 | import_filter =
35 | {
36 | exclude_by_default = false
37 | exception_list = [ ]
38 | }
39 | },
40 | ]
41 | },
42 | {
43 | _class = "PhysicsShapeList"
44 | children =
45 | [
46 | {
47 | _class = "PhysicsHullFromRender"
48 | parent_bone = ""
49 | surface_prop = "default"
50 | collision_group = ""
51 | collision_interact_as = ""
52 | collision_interact_with = ""
53 | collision_interact_exclude = ""
54 | faceMergeAngle = 5.0
55 | maxHullVertices = 53
56 | },
57 | ]
58 | },
59 | {
60 | _class = "AnimationList"
61 | children =
62 | [
63 | {
64 | _class = "AnimFile"
65 | name = "HandRight"
66 | activity_name = ""
67 | activity_weight = 1
68 | weight_list_name = ""
69 | fade_in_time = 0.2
70 | fade_out_time = 0.2
71 | looping = false
72 | delta = false
73 | worldSpace = false
74 | hidden = false
75 | anim_markup_ordered = false
76 | disable_compression = false
77 | source_filename = "models/hands/HandRight.fbx"
78 | start_frame = -1
79 | end_frame = -1
80 | framerate = -1.0
81 | take = 0
82 | reverse = false
83 | },
84 | ]
85 | default_root_bone_name = ""
86 | },
87 | {
88 | _class = "ModelModifierList"
89 | children =
90 | [
91 | {
92 | _class = "ModelModifier_ScaleAndMirror"
93 | scale = 0.7
94 | mirror_x = false
95 | mirror_y = true
96 | mirror_z = false
97 | flip_bone_forward = false
98 | swap_left_and_right_bones = false
99 | },
100 | ]
101 | },
102 | {
103 | _class = "WeightListList"
104 | children =
105 | [
106 | {
107 | _class = "WeightList"
108 | name = "Index"
109 | default_weight = 0.0
110 | weights =
111 | [
112 | {
113 | bone = "finger_index_meta_R"
114 | weight = 1.0
115 | },
116 | ]
117 | master_morph_weight = -1.0
118 | morph_weights = [ ]
119 | },
120 | {
121 | _class = "WeightList"
122 | name = "Middle"
123 | default_weight = 0.0
124 | weights =
125 | [
126 | {
127 | bone = "finger_index_meta_R"
128 | weight = 0.0
129 | },
130 | {
131 | bone = "finger_middle_meta_R"
132 | weight = 1.0
133 | },
134 | ]
135 | master_morph_weight = -1.0
136 | morph_weights = [ ]
137 | },
138 | {
139 | _class = "WeightList"
140 | name = "Ring"
141 | default_weight = 0.0
142 | weights =
143 | [
144 | {
145 | bone = "finger_index_meta_R"
146 | weight = 0.0
147 | },
148 | {
149 | bone = "finger_ring_meta_R"
150 | weight = 1.0
151 | },
152 | ]
153 | master_morph_weight = -1.0
154 | morph_weights = [ ]
155 | },
156 | {
157 | _class = "WeightList"
158 | name = "Thumb"
159 | default_weight = 0.0
160 | weights =
161 | [
162 | {
163 | bone = "finger_index_meta_R"
164 | weight = 0.0
165 | },
166 | {
167 | bone = "finger_thumb_0_R"
168 | weight = 1.0
169 | },
170 | ]
171 | master_morph_weight = -1.0
172 | morph_weights = [ ]
173 | },
174 | {
175 | _class = "WeightList"
176 | name = "Base"
177 | default_weight = 0.0
178 | weights =
179 | [
180 | {
181 | bone = "finger_index_meta_R"
182 | weight = 0.0
183 | },
184 | {
185 | bone = "finger_middle_meta_R"
186 | weight = 0.0
187 | },
188 | {
189 | bone = "finger_ring_meta_R"
190 | weight = 0.0
191 | },
192 | {
193 | bone = "finger_thumb_0_R"
194 | weight = 0.0
195 | },
196 | {
197 | bone = "hand_R"
198 | weight = 1.0
199 | },
200 | ]
201 | master_morph_weight = -1.0
202 | morph_weights = [ ]
203 | },
204 | ]
205 | },
206 | {
207 | _class = "ModelDataList"
208 | children =
209 | [
210 | {
211 | _class = "Bounds View"
212 | mins = [ -9.14642, -3.673171, -6.486435 ]
213 | maxs = [ 1.0, 3.672426, 4.49463 ]
214 | },
215 | ]
216 | },
217 | ]
218 | model_archetype = ""
219 | primary_associated_entity = ""
220 | anim_graph_name = "animgraphs/hands/handright.vanmgrph"
221 | base_model_name = ""
222 | }
223 | }
--------------------------------------------------------------------------------
/models/hands/handright.vmdl:
--------------------------------------------------------------------------------
1 |
2 | {
3 | rootNode =
4 | {
5 | _class = "RootNode"
6 | children =
7 | [
8 | {
9 | _class = "MaterialGroupList"
10 | children =
11 | [
12 | {
13 | _class = "DefaultMaterialGroup"
14 | remaps = [ ]
15 | use_global_default = true
16 | global_default_material = "models/citizen/skin/citizen_skin.vmat"
17 | },
18 | ]
19 | },
20 | {
21 | _class = "RenderMeshList"
22 | children =
23 | [
24 | {
25 | _class = "RenderMeshFile"
26 | filename = "models/hands/HandRight.fbx"
27 | import_translation = [ 0.0, 0.0, 0.0 ]
28 | import_rotation = [ 0.0, 0.0, 0.0 ]
29 | import_scale = 1.0
30 | align_origin_x_type = "BoundsMax"
31 | align_origin_y_type = "BoundsCenter"
32 | align_origin_z_type = "BoundsCenter"
33 | parent_bone = ""
34 | import_filter =
35 | {
36 | exclude_by_default = false
37 | exception_list = [ ]
38 | }
39 | },
40 | ]
41 | },
42 | {
43 | _class = "PhysicsShapeList"
44 | children =
45 | [
46 | {
47 | _class = "PhysicsHullFromRender"
48 | parent_bone = ""
49 | surface_prop = "default"
50 | collision_group = ""
51 | collision_interact_as = ""
52 | collision_interact_with = ""
53 | collision_interact_exclude = ""
54 | faceMergeAngle = 5.0
55 | maxHullVertices = 53
56 | },
57 | ]
58 | },
59 | {
60 | _class = "AnimationList"
61 | children =
62 | [
63 | {
64 | _class = "AnimFile"
65 | name = "HandRight"
66 | activity_name = ""
67 | activity_weight = 1
68 | weight_list_name = ""
69 | fade_in_time = 0.2
70 | fade_out_time = 0.2
71 | looping = false
72 | delta = false
73 | worldSpace = false
74 | hidden = false
75 | anim_markup_ordered = false
76 | disable_compression = false
77 | source_filename = "models/hands/HandRight.fbx"
78 | start_frame = -1
79 | end_frame = -1
80 | framerate = -1.0
81 | take = 0
82 | reverse = false
83 | },
84 | ]
85 | default_root_bone_name = ""
86 | },
87 | {
88 | _class = "ModelDataList"
89 | children =
90 | [
91 | {
92 | _class = "Bounds Hull"
93 | mins = [ -10.0, -4.0, -7.0 ]
94 | maxs = [ 2.0, 4.0, 5.0 ]
95 | },
96 | {
97 | _class = "Bounds View"
98 | mins = [ -7.432799, -2.736166, -6.17193 ]
99 | maxs = [ 1.0, 3.972052, 5.079096 ]
100 | },
101 | ]
102 | },
103 | {
104 | _class = "WeightListList"
105 | children =
106 | [
107 | {
108 | _class = "WeightList"
109 | name = "Index"
110 | default_weight = 0.0
111 | weights =
112 | [
113 | {
114 | bone = "finger_index_meta_R"
115 | weight = 1.0
116 | },
117 | ]
118 | master_morph_weight = -1.0
119 | morph_weights = [ ]
120 | },
121 | {
122 | _class = "WeightList"
123 | name = "Middle"
124 | default_weight = 0.0
125 | weights =
126 | [
127 | {
128 | bone = "finger_index_meta_R"
129 | weight = 0.0
130 | },
131 | {
132 | bone = "finger_middle_meta_R"
133 | weight = 1.0
134 | },
135 | ]
136 | master_morph_weight = -1.0
137 | morph_weights = [ ]
138 | },
139 | {
140 | _class = "WeightList"
141 | name = "Ring"
142 | default_weight = 0.0
143 | weights =
144 | [
145 | {
146 | bone = "finger_index_meta_R"
147 | weight = 0.0
148 | },
149 | {
150 | bone = "finger_ring_meta_R"
151 | weight = 1.0
152 | },
153 | ]
154 | master_morph_weight = -1.0
155 | morph_weights = [ ]
156 | },
157 | {
158 | _class = "WeightList"
159 | name = "Thumb"
160 | default_weight = 0.0
161 | weights =
162 | [
163 | {
164 | bone = "finger_index_meta_R"
165 | weight = 0.0
166 | },
167 | {
168 | bone = "finger_thumb_0_R"
169 | weight = 1.0
170 | },
171 | {
172 | bone = "hand_R"
173 | weight = 0.0
174 | },
175 | ]
176 | master_morph_weight = -1.0
177 | morph_weights = [ ]
178 | },
179 | {
180 | _class = "WeightList"
181 | name = "Base"
182 | default_weight = 0.0
183 | weights =
184 | [
185 | {
186 | bone = "finger_index_meta_R"
187 | weight = 0.0
188 | },
189 | {
190 | bone = "finger_middle_meta_R"
191 | weight = 0.0
192 | },
193 | {
194 | bone = "finger_ring_meta_R"
195 | weight = 0.0
196 | },
197 | {
198 | bone = "finger_thumb_0_R"
199 | weight = 0.0
200 | },
201 | {
202 | bone = "hand_R"
203 | weight = 1.0
204 | },
205 | ]
206 | master_morph_weight = -1.0
207 | morph_weights = [ ]
208 | },
209 | ]
210 | },
211 | {
212 | _class = "ModelModifierList"
213 | children =
214 | [
215 | {
216 | _class = "ModelModifier_ScaleAndMirror"
217 | scale = 0.7
218 | mirror_x = false
219 | mirror_y = false
220 | mirror_z = false
221 | flip_bone_forward = false
222 | swap_left_and_right_bones = false
223 | },
224 | ]
225 | },
226 | ]
227 | model_archetype = ""
228 | primary_associated_entity = ""
229 | anim_graph_name = "animgraphs/hands/handright.vanmgrph"
230 | base_model_name = ""
231 | }
232 | }
--------------------------------------------------------------------------------
/animgraphs/hands/handright.vanmgrph:
--------------------------------------------------------------------------------
1 |
2 | {
3 | _class = "CAnimationGraph"
4 | m_pParameterList =
5 | {
6 | _class = "CAnimParameterList"
7 | m_Parameters =
8 | [
9 | {
10 | _class = "CBoolAnimParameter"
11 | m_name = "Grabbing"
12 | m_id =
13 | {
14 | m_id = 1548504135
15 | }
16 | m_previewButton = "ANIMPARAM_BUTTON_NONE"
17 | m_bNetwork = false
18 | m_bUseMostRecentValue = false
19 | m_bAutoReset = false
20 | m_bPredicted = false
21 | m_bDefaultValue = false
22 | },
23 | {
24 | _class = "CFloatAnimParameter"
25 | m_name = "Index"
26 | m_id =
27 | {
28 | m_id = 640280030
29 | }
30 | m_previewButton = "ANIMPARAM_BUTTON_NONE"
31 | m_bNetwork = true
32 | m_bUseMostRecentValue = false
33 | m_bAutoReset = false
34 | m_bPredicted = false
35 | m_fDefaultValue = 0.0
36 | m_fMinValue = 0.0
37 | m_fMaxValue = 1.0
38 | m_bInterpolate = false
39 | },
40 | {
41 | _class = "CFloatAnimParameter"
42 | m_name = "Middle"
43 | m_id =
44 | {
45 | m_id = 1812020517
46 | }
47 | m_previewButton = "ANIMPARAM_BUTTON_NONE"
48 | m_bNetwork = true
49 | m_bUseMostRecentValue = false
50 | m_bAutoReset = false
51 | m_bPredicted = false
52 | m_fDefaultValue = 0.0
53 | m_fMinValue = 0.0
54 | m_fMaxValue = 1.0
55 | m_bInterpolate = false
56 | },
57 | {
58 | _class = "CFloatAnimParameter"
59 | m_name = "Ring"
60 | m_id =
61 | {
62 | m_id = 1168202039
63 | }
64 | m_previewButton = "ANIMPARAM_BUTTON_NONE"
65 | m_bNetwork = true
66 | m_bUseMostRecentValue = false
67 | m_bAutoReset = false
68 | m_bPredicted = false
69 | m_fDefaultValue = 0.0
70 | m_fMinValue = 0.0
71 | m_fMaxValue = 1.0
72 | m_bInterpolate = false
73 | },
74 | {
75 | _class = "CFloatAnimParameter"
76 | m_name = "Thumb"
77 | m_id =
78 | {
79 | m_id = 1575258000
80 | }
81 | m_previewButton = "ANIMPARAM_BUTTON_NONE"
82 | m_bNetwork = true
83 | m_bUseMostRecentValue = false
84 | m_bAutoReset = false
85 | m_bPredicted = false
86 | m_fDefaultValue = 0.0
87 | m_fMinValue = 0.0
88 | m_fMaxValue = 1.0
89 | m_bInterpolate = false
90 | },
91 | ]
92 | }
93 | m_pTagManager =
94 | {
95 | _class = "CAnimTagManager"
96 | m_tags = [ ]
97 | }
98 | m_pMovementManager =
99 | {
100 | _class = "CAnimMovementManager"
101 | m_MotorList =
102 | {
103 | _class = "CAnimMotorList"
104 | m_motors = [ ]
105 | }
106 | m_MovementSettings =
107 | {
108 | _class = "CAnimMovementSettings"
109 | m_bShouldCalculateSlope = false
110 | }
111 | }
112 | m_pSettingsManager =
113 | {
114 | _class = "CAnimGraphSettingsManager"
115 | m_settingsGroups =
116 | [
117 | {
118 | _class = "CAnimGraphGeneralSettings"
119 | m_iGridSnap = 16
120 | },
121 | {
122 | _class = "CAnimGraphNetworkSettings"
123 | m_bNetworkingEnabled = true
124 | },
125 | ]
126 | }
127 | m_pActivityValuesList =
128 | {
129 | _class = "CActivityValueList"
130 | m_activities = [ ]
131 | }
132 | m_rootNodeID =
133 | {
134 | m_id = 333965117
135 | }
136 | m_previewModels =
137 | [
138 | "models/hands/handleft.vmdl",
139 | ]
140 | m_nodes =
141 | [
142 | {
143 | key =
144 | {
145 | m_id = 79048108
146 | }
147 | value =
148 | {
149 | _class = "CBoneMaskAnimNode"
150 | m_sName = "MaskMiddleIn"
151 | m_vecPosition = [ -493.0, -150.0 ]
152 | m_nNodeID =
153 | {
154 | m_id = 79048108
155 | }
156 | m_networkMode = "ClientPredicted"
157 | m_sNote = ""
158 | m_weightListName = "Middle"
159 | m_child1ID =
160 | {
161 | m_id = 272882607
162 | }
163 | m_child2ID =
164 | {
165 | m_id = 276102712
166 | }
167 | m_blendSpace = "BlendSpace_Parent"
168 | m_bUseBlendScale = false
169 | m_blendValueSource = "Parameter"
170 | m_blendParameter =
171 | {
172 | m_id = 4294967295
173 | }
174 | m_timingBehavior = "UseChild2"
175 | m_flTimingBlend = 0.5
176 | m_flRootMotionBlend = 1.0
177 | m_footMotionTiming = "Child1"
178 | m_bResetChild1 = true
179 | m_bResetChild2 = true
180 | }
181 | },
182 | {
183 | key =
184 | {
185 | m_id = 194746901
186 | }
187 | value =
188 | {
189 | _class = "CBoneMaskAnimNode"
190 | m_sName = "MaskRingIn"
191 | m_vecPosition = [ -282.0, -29.0 ]
192 | m_nNodeID =
193 | {
194 | m_id = 194746901
195 | }
196 | m_networkMode = "ClientPredicted"
197 | m_sNote = ""
198 | m_weightListName = "Ring"
199 | m_child1ID =
200 | {
201 | m_id = 79048108
202 | }
203 | m_child2ID =
204 | {
205 | m_id = 1735913125
206 | }
207 | m_blendSpace = "BlendSpace_Parent"
208 | m_bUseBlendScale = false
209 | m_blendValueSource = "Parameter"
210 | m_blendParameter =
211 | {
212 | m_id = 4294967295
213 | }
214 | m_timingBehavior = "UseChild2"
215 | m_flTimingBlend = 0.5
216 | m_flRootMotionBlend = 1.0
217 | m_footMotionTiming = "Child1"
218 | m_bResetChild1 = true
219 | m_bResetChild2 = true
220 | }
221 | },
222 | {
223 | key =
224 | {
225 | m_id = 272882607
226 | }
227 | value =
228 | {
229 | _class = "CBoneMaskAnimNode"
230 | m_sName = "MaskIndexIn"
231 | m_vecPosition = [ -711.0, -242.0 ]
232 | m_nNodeID =
233 | {
234 | m_id = 272882607
235 | }
236 | m_networkMode = "ClientPredicted"
237 | m_sNote = ""
238 | m_weightListName = "Index"
239 | m_child1ID =
240 | {
241 | m_id = 2020438561
242 | }
243 | m_child2ID =
244 | {
245 | m_id = 1682182037
246 | }
247 | m_blendSpace = "BlendSpace_Parent"
248 | m_bUseBlendScale = false
249 | m_blendValueSource = "Parameter"
250 | m_blendParameter =
251 | {
252 | m_id = 4294967295
253 | }
254 | m_timingBehavior = "UseChild2"
255 | m_flTimingBlend = 0.5
256 | m_flRootMotionBlend = 1.0
257 | m_footMotionTiming = "Child1"
258 | m_bResetChild1 = true
259 | m_bResetChild2 = true
260 | }
261 | },
262 | {
263 | key =
264 | {
265 | m_id = 276102712
266 | }
267 | value =
268 | {
269 | _class = "CBlendAnimNode"
270 | m_sName = "BlendMiddle"
271 | m_vecPosition = [ -717.0, -133.0 ]
272 | m_nNodeID =
273 | {
274 | m_id = 276102712
275 | }
276 | m_networkMode = "ClientPredicted"
277 | m_sNote = ""
278 | m_children =
279 | [
280 | {
281 | m_nodeID =
282 | {
283 | m_id = 640991500
284 | }
285 | m_name = ""
286 | m_blendValue = 0.0
287 | },
288 | {
289 | m_nodeID =
290 | {
291 | m_id = 763054373
292 | }
293 | m_name = ""
294 | m_blendValue = 1.0
295 | },
296 | ]
297 | m_blendValueSource = "Parameter"
298 | m_param =
299 | {
300 | m_id = 1812020517
301 | }
302 | m_blendKeyType = "BlendKey_UserValue"
303 | m_bLockBlendOnReset = false
304 | m_bSyncCycles = true
305 | m_bLoop = true
306 | m_bLockWhenWaning = true
307 | m_damping =
308 | {
309 | _class = "CAnimInputDamping"
310 | m_speedFunction = "NoDamping"
311 | m_fSpeedScale = 1.0
312 | m_fMinSpeed = 10.0
313 | m_fMaxTension = 1000.0
314 | }
315 | }
316 | },
317 | {
318 | key =
319 | {
320 | m_id = 333965117
321 | }
322 | value =
323 | {
324 | _class = "CRootAnimNode"
325 | m_sName = "Unnamed"
326 | m_vecPosition = [ 155.0, 104.0 ]
327 | m_nNodeID =
328 | {
329 | m_id = 333965117
330 | }
331 | m_networkMode = "ClientPredicted"
332 | m_sNote = ""
333 | m_childID =
334 | {
335 | m_id = 571264672
336 | }
337 | }
338 | },
339 | {
340 | key =
341 | {
342 | m_id = 571264672
343 | }
344 | value =
345 | {
346 | _class = "CBoneMaskAnimNode"
347 | m_sName = "MaskThumbIn"
348 | m_vecPosition = [ -78.0, 102.0 ]
349 | m_nNodeID =
350 | {
351 | m_id = 571264672
352 | }
353 | m_networkMode = "ClientPredicted"
354 | m_sNote = ""
355 | m_weightListName = "Thumb"
356 | m_child1ID =
357 | {
358 | m_id = 194746901
359 | }
360 | m_child2ID =
361 | {
362 | m_id = 876805543
363 | }
364 | m_blendSpace = "BlendSpace_Parent"
365 | m_bUseBlendScale = false
366 | m_blendValueSource = "Parameter"
367 | m_blendParameter =
368 | {
369 | m_id = 4294967295
370 | }
371 | m_timingBehavior = "UseChild2"
372 | m_flTimingBlend = 0.5
373 | m_flRootMotionBlend = 1.0
374 | m_footMotionTiming = "Child1"
375 | m_bResetChild1 = true
376 | m_bResetChild2 = true
377 | }
378 | },
379 | {
380 | key =
381 | {
382 | m_id = 640991500
383 | }
384 | value =
385 | {
386 | _class = "CSingleFrameAnimNode"
387 | m_sName = "Unnamed"
388 | m_vecPosition = [ -916.0, -136.0 ]
389 | m_nNodeID =
390 | {
391 | m_id = 640991500
392 | }
393 | m_networkMode = "ClientPredicted"
394 | m_sNote = ""
395 | m_sequenceName = "HandRight"
396 | m_nFrameIndex = 0
397 | }
398 | },
399 | {
400 | key =
401 | {
402 | m_id = 763054373
403 | }
404 | value =
405 | {
406 | _class = "CSingleFrameAnimNode"
407 | m_sName = "Unnamed"
408 | m_vecPosition = [ -916.0, -74.0 ]
409 | m_nNodeID =
410 | {
411 | m_id = 763054373
412 | }
413 | m_networkMode = "ClientPredicted"
414 | m_sNote = ""
415 | m_sequenceName = "HandRight"
416 | m_nFrameIndex = 20
417 | }
418 | },
419 | {
420 | key =
421 | {
422 | m_id = 817800449
423 | }
424 | value =
425 | {
426 | _class = "CSingleFrameAnimNode"
427 | m_sName = "Unnamed"
428 | m_vecPosition = [ -491.0, 177.0 ]
429 | m_nNodeID =
430 | {
431 | m_id = 817800449
432 | }
433 | m_networkMode = "ClientPredicted"
434 | m_sNote = ""
435 | m_sequenceName = "HandRight"
436 | m_nFrameIndex = 20
437 | }
438 | },
439 | {
440 | key =
441 | {
442 | m_id = 824680803
443 | }
444 | value =
445 | {
446 | _class = "CSingleFrameAnimNode"
447 | m_sName = "Unnamed"
448 | m_vecPosition = [ -717.0, -13.0 ]
449 | m_nNodeID =
450 | {
451 | m_id = 824680803
452 | }
453 | m_networkMode = "ClientPredicted"
454 | m_sNote = ""
455 | m_sequenceName = "HandRight"
456 | m_nFrameIndex = 0
457 | }
458 | },
459 | {
460 | key =
461 | {
462 | m_id = 876805543
463 | }
464 | value =
465 | {
466 | _class = "CBlendAnimNode"
467 | m_sName = "BlendThumb"
468 | m_vecPosition = [ -279.0, 116.0 ]
469 | m_nNodeID =
470 | {
471 | m_id = 876805543
472 | }
473 | m_networkMode = "ClientPredicted"
474 | m_sNote = ""
475 | m_children =
476 | [
477 | {
478 | m_nodeID =
479 | {
480 | m_id = 1627551740
481 | }
482 | m_name = ""
483 | m_blendValue = 0.0
484 | },
485 | {
486 | m_nodeID =
487 | {
488 | m_id = 817800449
489 | }
490 | m_name = ""
491 | m_blendValue = 1.0
492 | },
493 | ]
494 | m_blendValueSource = "Parameter"
495 | m_param =
496 | {
497 | m_id = 1575258000
498 | }
499 | m_blendKeyType = "BlendKey_UserValue"
500 | m_bLockBlendOnReset = false
501 | m_bSyncCycles = true
502 | m_bLoop = true
503 | m_bLockWhenWaning = true
504 | m_damping =
505 | {
506 | _class = "CAnimInputDamping"
507 | m_speedFunction = "NoDamping"
508 | m_fSpeedScale = 1.0
509 | m_fMinSpeed = 10.0
510 | m_fMaxTension = 1000.0
511 | }
512 | }
513 | },
514 | {
515 | key =
516 | {
517 | m_id = 1373944808
518 | }
519 | value =
520 | {
521 | _class = "CSingleFrameAnimNode"
522 | m_sName = "Unnamed"
523 | m_vecPosition = [ -715.0, 49.0 ]
524 | m_nNodeID =
525 | {
526 | m_id = 1373944808
527 | }
528 | m_networkMode = "ClientPredicted"
529 | m_sNote = ""
530 | m_sequenceName = "HandRight"
531 | m_nFrameIndex = 20
532 | }
533 | },
534 | {
535 | key =
536 | {
537 | m_id = 1499922691
538 | }
539 | value =
540 | {
541 | _class = "CSingleFrameAnimNode"
542 | m_sName = "Unnamed"
543 | m_vecPosition = [ -1113.0, -167.0 ]
544 | m_nNodeID =
545 | {
546 | m_id = 1499922691
547 | }
548 | m_networkMode = "ClientPredicted"
549 | m_sNote = ""
550 | m_sequenceName = "HandRight"
551 | m_nFrameIndex = 20
552 | }
553 | },
554 | {
555 | key =
556 | {
557 | m_id = 1627551740
558 | }
559 | value =
560 | {
561 | _class = "CSingleFrameAnimNode"
562 | m_sName = "Unnamed"
563 | m_vecPosition = [ -493.0, 115.0 ]
564 | m_nNodeID =
565 | {
566 | m_id = 1627551740
567 | }
568 | m_networkMode = "ClientPredicted"
569 | m_sNote = ""
570 | m_sequenceName = "HandRight"
571 | m_nFrameIndex = 0
572 | }
573 | },
574 | {
575 | key =
576 | {
577 | m_id = 1682182037
578 | }
579 | value =
580 | {
581 | _class = "CBlendAnimNode"
582 | m_sName = "BlendIndex"
583 | m_vecPosition = [ -916.0, -228.0 ]
584 | m_nNodeID =
585 | {
586 | m_id = 1682182037
587 | }
588 | m_networkMode = "ClientPredicted"
589 | m_sNote = ""
590 | m_children =
591 | [
592 | {
593 | m_nodeID =
594 | {
595 | m_id = 1972884863
596 | }
597 | m_name = ""
598 | m_blendValue = 0.0
599 | },
600 | {
601 | m_nodeID =
602 | {
603 | m_id = 1499922691
604 | }
605 | m_name = ""
606 | m_blendValue = 1.0
607 | },
608 | ]
609 | m_blendValueSource = "Parameter"
610 | m_param =
611 | {
612 | m_id = 640280030
613 | }
614 | m_blendKeyType = "BlendKey_UserValue"
615 | m_bLockBlendOnReset = false
616 | m_bSyncCycles = true
617 | m_bLoop = true
618 | m_bLockWhenWaning = true
619 | m_damping =
620 | {
621 | _class = "CAnimInputDamping"
622 | m_speedFunction = "NoDamping"
623 | m_fSpeedScale = 1.0
624 | m_fMinSpeed = 10.0
625 | m_fMaxTension = 1000.0
626 | }
627 | }
628 | },
629 | {
630 | key =
631 | {
632 | m_id = 1735913125
633 | }
634 | value =
635 | {
636 | _class = "CBlendAnimNode"
637 | m_sName = "BlendRing"
638 | m_vecPosition = [ -491.0, -12.0 ]
639 | m_nNodeID =
640 | {
641 | m_id = 1735913125
642 | }
643 | m_networkMode = "ClientPredicted"
644 | m_sNote = ""
645 | m_children =
646 | [
647 | {
648 | m_nodeID =
649 | {
650 | m_id = 824680803
651 | }
652 | m_name = ""
653 | m_blendValue = 0.0
654 | },
655 | {
656 | m_nodeID =
657 | {
658 | m_id = 1373944808
659 | }
660 | m_name = ""
661 | m_blendValue = 1.0
662 | },
663 | ]
664 | m_blendValueSource = "Parameter"
665 | m_param =
666 | {
667 | m_id = 1168202039
668 | }
669 | m_blendKeyType = "BlendKey_UserValue"
670 | m_bLockBlendOnReset = false
671 | m_bSyncCycles = true
672 | m_bLoop = true
673 | m_bLockWhenWaning = true
674 | m_damping =
675 | {
676 | _class = "CAnimInputDamping"
677 | m_speedFunction = "NoDamping"
678 | m_fSpeedScale = 1.0
679 | m_fMinSpeed = 10.0
680 | m_fMaxTension = 1000.0
681 | }
682 | }
683 | },
684 | {
685 | key =
686 | {
687 | m_id = 1972884863
688 | }
689 | value =
690 | {
691 | _class = "CSingleFrameAnimNode"
692 | m_sName = "Unnamed"
693 | m_vecPosition = [ -1111.0, -229.0 ]
694 | m_nNodeID =
695 | {
696 | m_id = 1972884863
697 | }
698 | m_networkMode = "ClientPredicted"
699 | m_sNote = ""
700 | m_sequenceName = "HandRight"
701 | m_nFrameIndex = 0
702 | }
703 | },
704 | {
705 | key =
706 | {
707 | m_id = 2020438561
708 | }
709 | value =
710 | {
711 | _class = "CSingleFrameAnimNode"
712 | m_sName = "OpenHand"
713 | m_vecPosition = [ -914.0, -350.0 ]
714 | m_nNodeID =
715 | {
716 | m_id = 2020438561
717 | }
718 | m_networkMode = "ClientPredicted"
719 | m_sNote = ""
720 | m_sequenceName = "HandRight"
721 | m_nFrameIndex = 0
722 | }
723 | },
724 | ]
725 | }
--------------------------------------------------------------------------------
/code/Player/WalkController.cs:
--------------------------------------------------------------------------------
1 | using Sandbox;
2 |
3 | namespace VrExample;
4 |
5 | [Library]
6 | public class WalkController : BasePlayerController
7 | {
8 | public float Speed => 100.0f;
9 | public float Acceleration => 10.0f;
10 | public float AirAcceleration => 50.0f;
11 | public float FallSoundZ => -30.0f;
12 | public float GroundFriction => 4.0f;
13 | public float StopSpeed => 100.0f;
14 | public float Size => 20.0f;
15 | public float DistEpsilon => 0.03125f;
16 | public float GroundAngle => 46.0f;
17 | public float Bounce => 0.0f;
18 | public float MoveFriction => 1.0f;
19 | public float StepSize => 18.0f;
20 | public float MaxNonJumpVelocity => 140.0f;
21 | public float BodyGirth => 6.0f;
22 | public float BodyHeight => 72.0f;
23 | public float EyeHeight => 64.0f;
24 | public float Gravity => 800.0f;
25 | public float AirControl => 30.0f;
26 |
27 | public bool Swimming { get; set; } = false;
28 | public bool AutoJump { get; set; } = false;
29 |
30 | public Duck Duck;
31 | public Unstuck Unstuck;
32 |
33 |
34 | public WalkController()
35 | {
36 | Duck = new Duck( this );
37 | Unstuck = new Unstuck( this );
38 | }
39 |
40 | ///
41 | /// This is temporary, get the hull size for the player's collision
42 | ///
43 | public override BBox GetHull()
44 | {
45 | var girth = BodyGirth * 0.5f;
46 | var mins = new Vector3( -girth, -girth, 0 );
47 | var maxs = new Vector3( +girth, +girth, BodyHeight );
48 |
49 | return new BBox( mins, maxs );
50 | }
51 |
52 |
53 | // Duck body height 32
54 | // Eye Height 64
55 | // Duck Eye Height 28
56 |
57 | protected Vector3 mins;
58 | protected Vector3 maxs;
59 |
60 | public virtual void SetBBox( Vector3 mins, Vector3 maxs )
61 | {
62 | if ( this.mins == mins && this.maxs == maxs )
63 | return;
64 |
65 | this.mins = mins;
66 | this.maxs = maxs;
67 | }
68 |
69 | ///
70 | /// Update the size of the bbox. We should really trigger some shit if this changes.
71 | ///
72 | public virtual void UpdateBBox()
73 | {
74 | Transform headLocal = Pawn.Transform.ToLocal( Input.VR.Head );
75 | var girth = BodyGirth * 0.5f;
76 |
77 | var mins = (new Vector3( -girth, -girth, 0 ) + headLocal.Position.WithZ( 0 ) * Rotation) * Pawn.Scale;
78 | var maxs = (new Vector3( +girth, +girth, BodyHeight ) + headLocal.Position.WithZ( 0 ) * Rotation) *
79 | Pawn.Scale;
80 |
81 | Duck.UpdateBBox( ref mins, ref maxs, Pawn.Scale );
82 | SetBBox( mins, maxs );
83 | }
84 |
85 | protected float SurfaceFriction;
86 |
87 | public override void FrameSimulate()
88 | {
89 | base.FrameSimulate();
90 |
91 | EyeRotation = Input.VR.Head.Rotation;
92 | }
93 |
94 | public override void Simulate()
95 | {
96 | EyeLocalPosition = Vector3.Up * (EyeHeight * Pawn.Scale);
97 | UpdateBBox();
98 |
99 | EyeLocalPosition += TraceOffset;
100 | EyeRotation = Input.VR.Head.Rotation;
101 |
102 | RestoreGroundPos();
103 |
104 | if ( Unstuck.TestAndFix() )
105 | return;
106 |
107 | // RunLadderMode
108 |
109 | CheckLadder();
110 | Swimming = Pawn.WaterLevel > 0.6f;
111 |
112 | //
113 | // Start Gravity
114 | //
115 | if ( !Swimming && !IsTouchingLadder )
116 | {
117 | Velocity -= new Vector3( 0, 0, Gravity * 0.5f ) * Time.Delta;
118 | Velocity += new Vector3( 0, 0, BaseVelocity.z ) * Time.Delta;
119 |
120 | BaseVelocity = BaseVelocity.WithZ( 0 );
121 | }
122 |
123 | if ( Input.VR.RightHand.JoystickPress.IsPressed )
124 | {
125 | CheckJumpButton();
126 | }
127 |
128 | // Fricion is handled before we add in any base velocity. That way, if we are on a conveyor,
129 | // we don't slow when standing still, relative to the conveyor.
130 | bool bStartOnGround = GroundEntity != null;
131 |
132 | if ( bStartOnGround )
133 | {
134 | Velocity = Velocity.WithZ( 0 );
135 |
136 | if ( GroundEntity != null )
137 | {
138 | ApplyFriction( GroundFriction * SurfaceFriction );
139 | }
140 | }
141 |
142 | //
143 | // Work out wish velocity.. just take input, rotate it to view, clamp to -1, 1
144 | //
145 | WishVelocity = Vector3.Zero;
146 | WishVelocity += Input.VR.LeftHand.Joystick.Value.y * Input.VR.Head.Rotation.Forward;
147 | WishVelocity += Input.VR.LeftHand.Joystick.Value.x * Input.VR.Head.Rotation.Right;
148 |
149 | var inSpeed = WishVelocity.Length.Clamp( 0, 1 );
150 |
151 | if ( !Swimming && !IsTouchingLadder )
152 | {
153 | WishVelocity = WishVelocity.WithZ( 0 );
154 | }
155 |
156 | WishVelocity = WishVelocity.Normal * inSpeed;
157 | WishVelocity *= GetWishSpeed();
158 |
159 | Duck.PreTick();
160 |
161 | bool bStayOnGround = false;
162 | if ( Swimming )
163 | {
164 | ApplyFriction( 1 );
165 | WaterMove();
166 | }
167 | else if ( IsTouchingLadder )
168 | {
169 | LadderMove();
170 | }
171 | else if ( GroundEntity != null )
172 | {
173 | bStayOnGround = true;
174 | WalkMove();
175 | }
176 | else
177 | {
178 | AirMove();
179 | }
180 |
181 | CategorizePosition( bStayOnGround );
182 |
183 | if ( !Swimming && !IsTouchingLadder )
184 | Velocity -= new Vector3( 0, 0, Gravity * 0.5f ) * Time.Delta;
185 |
186 | if ( GroundEntity != null )
187 | Velocity = Velocity.WithZ( 0 );
188 |
189 | SaveGroundPos();
190 |
191 | if ( Debug )
192 | {
193 | DebugOverlay.Box( Position + TraceOffset, mins, maxs, Color.Red );
194 | DebugOverlay.Box( Position, mins, maxs, Color.Blue );
195 |
196 | var lineOffset = 0;
197 | if ( Host.IsServer ) lineOffset = 10;
198 |
199 | DebugOverlay.ScreenText( $" Position: {Position}", lineOffset + 0 );
200 | DebugOverlay.ScreenText( $" Velocity: {Velocity}", lineOffset + 1 );
201 | DebugOverlay.ScreenText( $" BaseVelocity: {BaseVelocity}", lineOffset + 2 );
202 | DebugOverlay.ScreenText( $" GroundEntity: {GroundEntity} [{GroundEntity?.Velocity}]", lineOffset + 3 );
203 | DebugOverlay.ScreenText( $" SurfaceFriction: {SurfaceFriction}", lineOffset + 4 );
204 | DebugOverlay.ScreenText( $" WishVelocity: {WishVelocity}", lineOffset + 5 );
205 | }
206 | }
207 |
208 | public virtual float GetWishSpeed()
209 | {
210 | var ws = Duck.GetWishSpeed();
211 | if ( ws >= 0 ) return ws;
212 |
213 | return Speed;
214 | }
215 |
216 | public virtual void WalkMove()
217 | {
218 | var wishdir = WishVelocity.Normal;
219 | var wishspeed = WishVelocity.Length;
220 |
221 | WishVelocity = WishVelocity.WithZ( 0 );
222 | WishVelocity = WishVelocity.Normal * wishspeed;
223 |
224 | Velocity = Velocity.WithZ( 0 );
225 | Accelerate( wishdir, wishspeed, 0, Acceleration );
226 | Velocity = Velocity.WithZ( 0 );
227 |
228 | // Add in any base velocity to the current velocity.
229 | Velocity += BaseVelocity;
230 |
231 | try
232 | {
233 | if ( Velocity.Length < 1.0f )
234 | {
235 | Velocity = Vector3.Zero;
236 | return;
237 | }
238 |
239 | // first try just moving to the destination
240 | var dest = (Position + Velocity * Time.Delta).WithZ( Position.z );
241 |
242 | var pm = TraceBBox( Position, dest );
243 |
244 | if ( pm.Fraction == 1 )
245 | {
246 | Position = pm.EndPosition;
247 | StayOnGround();
248 | return;
249 | }
250 |
251 | StepMove();
252 | }
253 | finally
254 | {
255 | // Now pull the base velocity back out. Base velocity is set if you are on a moving object, like a conveyor (or maybe another monster?)
256 | Velocity -= BaseVelocity;
257 | }
258 |
259 | StayOnGround();
260 | }
261 |
262 | public virtual void StepMove()
263 | {
264 | MoveHelper mover = new MoveHelper( Position, Velocity );
265 | mover.Trace = mover.Trace.Size( mins, maxs ).Ignore( Pawn );
266 | mover.MaxStandableAngle = GroundAngle;
267 |
268 | mover.TryMoveWithStep( Time.Delta, StepSize );
269 |
270 | Position = mover.Position;
271 | Velocity = mover.Velocity;
272 | }
273 |
274 | public virtual void Move()
275 | {
276 | MoveHelper mover = new MoveHelper( Position, Velocity );
277 | mover.Trace = mover.Trace.Size( mins, maxs ).Ignore( Pawn );
278 | mover.MaxStandableAngle = GroundAngle;
279 |
280 | mover.TryMove( Time.Delta );
281 |
282 | Position = mover.Position;
283 | Velocity = mover.Velocity;
284 | }
285 |
286 | ///
287 | /// Add our wish direction and speed onto our velocity
288 | ///
289 | public virtual void Accelerate( Vector3 wishdir, float wishspeed, float speedLimit, float acceleration )
290 | {
291 | // This gets overridden because some games (CSPort) want to allow dead (observer) players
292 | // to be able to move around.
293 |
294 | if ( speedLimit > 0 && wishspeed > speedLimit )
295 | wishspeed = speedLimit;
296 |
297 | // See if we are changing direction a bit
298 | var currentspeed = Velocity.Dot( wishdir );
299 |
300 | // Reduce wishspeed by the amount of veer.
301 | var addspeed = wishspeed - currentspeed;
302 |
303 | // If not going to add any speed, done.
304 | if ( addspeed <= 0 )
305 | return;
306 |
307 | // Determine amount of acceleration.
308 | var accelspeed = acceleration * Time.Delta * wishspeed * SurfaceFriction;
309 |
310 | // Cap at addspeed
311 | if ( accelspeed > addspeed )
312 | accelspeed = addspeed;
313 |
314 | Velocity += wishdir * accelspeed;
315 | }
316 |
317 | ///
318 | /// Remove ground friction from velocity
319 | ///
320 | public virtual void ApplyFriction( float frictionAmount = 1.0f )
321 | {
322 | // Calculate speed
323 | var speed = Velocity.Length;
324 | if ( speed < 0.1f ) return;
325 |
326 | // Bleed off some speed, but if we have less than the bleed
327 | // threshold, bleed the threshold amount.
328 | float control = (speed < StopSpeed) ? StopSpeed : speed;
329 |
330 | // Add the amount to the drop amount.
331 | var drop = control * Time.Delta * frictionAmount;
332 |
333 | // scale the velocity
334 | float newspeed = speed - drop;
335 | if ( newspeed < 0 ) newspeed = 0;
336 |
337 | if ( newspeed != speed )
338 | {
339 | newspeed /= speed;
340 | Velocity *= newspeed;
341 | }
342 | }
343 |
344 | public virtual void CheckJumpButton()
345 | {
346 | // If we are in the water most of the way...
347 | if ( Swimming )
348 | {
349 | ClearGroundEntity();
350 |
351 | Velocity = Velocity.WithZ( 100 );
352 | return;
353 | }
354 |
355 | if ( GroundEntity == null )
356 | return;
357 |
358 | ClearGroundEntity();
359 |
360 | float flGroundFactor = 1.0f;
361 | float flMul = 268.3281572999747f * 1.2f;
362 | float startz = Velocity.z;
363 |
364 | if ( Duck.IsActive )
365 | flMul *= 0.8f;
366 |
367 | Velocity = Velocity.WithZ( startz + flMul * flGroundFactor );
368 |
369 | Velocity -= new Vector3( 0, 0, Gravity * 0.5f ) * Time.Delta;
370 |
371 | AddEvent( "jump" );
372 | }
373 |
374 | public virtual void AirMove()
375 | {
376 | var wishdir = WishVelocity.Normal;
377 | var wishspeed = WishVelocity.Length;
378 |
379 | Accelerate( wishdir, wishspeed, AirControl, AirAcceleration );
380 |
381 | Velocity += BaseVelocity;
382 |
383 | Move();
384 |
385 | Velocity -= BaseVelocity;
386 | }
387 |
388 | public virtual void WaterMove()
389 | {
390 | var wishdir = WishVelocity.Normal;
391 | var wishspeed = WishVelocity.Length;
392 |
393 | wishspeed *= 0.8f;
394 |
395 | Accelerate( wishdir, wishspeed, 100, Acceleration );
396 |
397 | Velocity += BaseVelocity;
398 |
399 | Move();
400 |
401 | Velocity -= BaseVelocity;
402 | }
403 |
404 | bool IsTouchingLadder = false;
405 | Vector3 LadderNormal;
406 |
407 | public virtual void CheckLadder()
408 | {
409 | if ( IsTouchingLadder && Input.VR.RightHand.JoystickPress.IsPressed )
410 | {
411 | Velocity = LadderNormal * 100.0f;
412 | IsTouchingLadder = false;
413 |
414 | return;
415 | }
416 |
417 | const float ladderDistance = 1.0f;
418 | var start = Position;
419 | Vector3 end = start + (IsTouchingLadder ? (LadderNormal * -1.0f) : WishVelocity.Normal) * ladderDistance;
420 |
421 | var pm = Trace.Ray( start, end )
422 | .Size( mins, maxs )
423 | .HitLayer( CollisionLayer.All, false )
424 | .HitLayer( CollisionLayer.LADDER, true )
425 | .Ignore( Pawn )
426 | .Run();
427 |
428 | IsTouchingLadder = false;
429 |
430 | if ( pm.Hit )
431 | {
432 | IsTouchingLadder = true;
433 | LadderNormal = pm.Normal;
434 | }
435 | }
436 |
437 | public virtual void LadderMove()
438 | {
439 | var velocity = WishVelocity;
440 | float normalDot = velocity.Dot( LadderNormal );
441 | var cross = LadderNormal * normalDot;
442 | Velocity = (velocity - cross) +
443 | (-normalDot * LadderNormal.Cross( Vector3.Up.Cross( LadderNormal ).Normal ));
444 |
445 | Move();
446 | }
447 |
448 | public virtual void CategorizePosition( bool bStayOnGround )
449 | {
450 | SurfaceFriction = 1.0f;
451 |
452 | // Doing this before we move may introduce a potential latency in water detection, but
453 | // doing it after can get us stuck on the bottom in water if the amount we move up
454 | // is less than the 1 pixel 'threshold' we're about to snap to. Also, we'll call
455 | // this several times per frame, so we really need to avoid sticking to the bottom of
456 | // water on each call, and the converse case will correct itself if called twice.
457 | //CheckWater();
458 |
459 | var point = Position - Vector3.Up * 2;
460 | var vBumpOrigin = Position;
461 |
462 | //
463 | // Shooting up really fast. Definitely not on ground trimed until ladder shit
464 | //
465 | bool bMovingUpRapidly = Velocity.z > MaxNonJumpVelocity;
466 | bool bMovingUp = Velocity.z > 0;
467 |
468 | bool bMoveToEndPos = false;
469 |
470 | if ( GroundEntity != null ) // and not underwater
471 | {
472 | bMoveToEndPos = true;
473 | point.z -= StepSize;
474 | }
475 | else if ( bStayOnGround )
476 | {
477 | bMoveToEndPos = true;
478 | point.z -= StepSize;
479 | }
480 |
481 | if ( bMovingUpRapidly || Swimming ) // or ladder and moving up
482 | {
483 | ClearGroundEntity();
484 | return;
485 | }
486 |
487 | var pm = TraceBBox( vBumpOrigin, point, 4.0f );
488 |
489 | if ( pm.Entity == null || Vector3.GetAngle( Vector3.Up, pm.Normal ) > GroundAngle )
490 | {
491 | ClearGroundEntity();
492 | bMoveToEndPos = false;
493 |
494 | if ( Velocity.z > 0 )
495 | SurfaceFriction = 0.25f;
496 | }
497 | else
498 | {
499 | UpdateGroundEntity( pm );
500 | }
501 |
502 | if ( bMoveToEndPos && !pm.StartedSolid && pm.Fraction > 0.0f && pm.Fraction < 1.0f )
503 | {
504 | Position = pm.EndPosition;
505 | }
506 | }
507 |
508 | ///
509 | /// We have a new ground entity
510 | ///
511 | public virtual void UpdateGroundEntity( TraceResult tr )
512 | {
513 | GroundNormal = tr.Normal;
514 |
515 | // VALVE HACKHACK: Scale this to fudge the relationship between vphysics friction values and player friction values.
516 | // A value of 0.8f feels pretty normal for vphysics, whereas 1.0f is normal for players.
517 | // This scaling trivially makes them equivalent. REVISIT if this affects low friction surfaces too much.
518 | SurfaceFriction = tr.Surface.Friction * 1.25f;
519 | if ( SurfaceFriction > 1 ) SurfaceFriction = 1;
520 |
521 | GroundEntity = tr.Entity;
522 |
523 | if ( GroundEntity != null )
524 | {
525 | BaseVelocity = GroundEntity.Velocity;
526 | }
527 | }
528 |
529 | ///
530 | /// We're no longer on the ground, remove it
531 | ///
532 | public virtual void ClearGroundEntity()
533 | {
534 | if ( GroundEntity == null ) return;
535 |
536 | Pawn.GroundEntity = null;
537 | GroundEntity = null;
538 | GroundNormal = Vector3.Up;
539 | SurfaceFriction = 1.0f;
540 | }
541 |
542 | ///
543 | /// Traces the current bbox and returns the result.
544 | /// liftFeet will move the start position up by this amount, while keeping the top of the bbox at the same
545 | /// position. This is good when tracing down because you won't be tracing through the ceiling above.
546 | ///
547 | public override TraceResult TraceBBox( Vector3 start, Vector3 end, float liftFeet = 0.0f )
548 | {
549 | return TraceBBox( start, end, mins, maxs, liftFeet );
550 | }
551 |
552 | ///
553 | /// Try to keep a walking player on the ground when running down slopes etc
554 | ///
555 | public virtual void StayOnGround()
556 | {
557 | var start = Position + Vector3.Up * 2;
558 | var end = Position + Vector3.Down * StepSize;
559 |
560 | // See how far up we can go without getting stuck
561 | var trace = TraceBBox( Position, start );
562 | start = trace.EndPosition;
563 |
564 | // Now trace down from a known safe position
565 | trace = TraceBBox( start, end );
566 |
567 | if ( trace.Fraction <= 0 ) return;
568 | if ( trace.Fraction >= 1 ) return;
569 | if ( trace.StartedSolid ) return;
570 | if ( Vector3.GetAngle( Vector3.Up, trace.Normal ) > GroundAngle ) return;
571 |
572 | Position = trace.EndPosition;
573 | }
574 |
575 | void RestoreGroundPos()
576 | {
577 | if ( GroundEntity == null || GroundEntity.IsWorld )
578 | return;
579 | }
580 |
581 | void SaveGroundPos()
582 | {
583 | if ( GroundEntity == null || GroundEntity.IsWorld )
584 | return;
585 | }
586 | }
587 |
--------------------------------------------------------------------------------