Compare commits

..

No commits in common. "1b7b915548da503993ceebdadd8d5c2784c7fcbf" and "62c36b96600ea90eb32d9618844306a835fc4240" have entirely different histories.

21 changed files with 53 additions and 186 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,5 +1,4 @@
#include "camera_controller.h" #include "camera_controller.h"
#include "core/camera_node.h"
#include "utils/mirror.h" #include "utils/mirror.h"
START_REFLECT(CameraController); START_REFLECT(CameraController);
@ -19,11 +18,10 @@ SceneNode *CreateCameraController(Transformable target) {
CameraController *self = new(CameraController); CameraController *self = new(CameraController);
*self = (CameraController){ *self = (CameraController){
.transform = tc_null(Transformable), .transform = tc_null(Transformable),
.rotation_speed = 2.f, .rotation_speed = 5.f,
.max_speed_time = 4.f, .max_speed_time = 4.f,
.target = target, .target = target,
.time_rotated = 0.f, .time_rotated = 0.f
.camera = NULL,
}; };
SceneNode *node = CreateSceneNode(CameraController_as_SceneNodeEntity(self)); SceneNode *node = CreateSceneNode(CameraController_as_SceneNodeEntity(self));
return node; return node;
@ -36,9 +34,6 @@ void DestroyCameraController(CameraController *self) {
void CameraControllerEnterTree(CameraController *self) { void CameraControllerEnterTree(CameraController *self) {
self->transform = TC_CAST(self->node->parent->entity, Transformable); self->transform = TC_CAST(self->node->parent->entity, Transformable);
self->global = self->transform.tc->get_global_transform(self->transform.data); self->global = self->transform.tc->get_global_transform(self->transform.data);
SceneNode *camera_node = SceneNodeGetChildByTypeid(self->node->parent, GET_TYPEID(CameraNode), true);
self->camera = camera_node->entity.data;
self->camera->fov = 100;
} }
void CameraControllerExitTree(CameraController *self) {} void CameraControllerExitTree(CameraController *self) {}
@ -54,10 +49,11 @@ void CameraControllerTick(CameraController *self, double delta) {
// ... and maximum frame step size // ... and maximum frame step size
float const step = self->rotation_speed * time_mul * delta; float const step = self->rotation_speed * time_mul * delta;
if(angle > 0.f) if(angle > 0.f)
target.rotation = QuaternionSlerp(current.rotation, target.rotation, step); target.rotation = QuaternionSlerp(current.rotation, target.rotation, fminf(fminf(step, angle) / angle, 0.99f));
if(step > angle && self->time_rotated > delta) if(step > angle && self->time_rotated > delta)
self->time_rotated -= delta; self->time_rotated -= 3.f * delta;
else else if(step > angle)
self->time_rotated += delta; self->time_rotated = 0.f;
self->time_rotated += delta;
self->transform.tc->set_global_transform(self->transform.data, target); self->transform.tc->set_global_transform(self->transform.data, target);
} }

View file

@ -14,7 +14,6 @@ typedef struct CameraController {
float max_speed_time; float max_speed_time;
Transformable target; Transformable target;
float time_rotated; float time_rotated;
CameraNode *camera;
} CameraController; } CameraController;
extern SceneNode *CreateCameraController(Transformable target); extern SceneNode *CreateCameraController(Transformable target);

View file

@ -1,45 +0,0 @@
#include "city_generator.h"
#include "core/mesh_render_entity.h"
#include "core/transform_node.h"
static struct {
size_t x, y;
} const CITY_SIZE = {20, 20};
#define MODELS_LENGTH 4
static ModelResource models[MODELS_LENGTH] = {
ResourceEmpty(Model),
ResourceEmpty(Model),
ResourceEmpty(Model),
ResourceEmpty(Model)
};
static void Internal_LoadModelsIfRequired() {
for(size_t i = 0; i < MODELS_LENGTH; ++i)
if(models[i].handle == NULL && GetModelResource(TextFormat("building_%c", 'a' + i), &models[i]))
LoadResource(models[i].handle);
}
static SceneNode *Internal_CreateBuilding(size_t x, size_t y) {
size_t building_index = (3 * (x + y + GetRandomValue(0, 50))) % MODELS_LENGTH; // semi-random number based on the x,y coordinate
SceneNode *mesh_render_node = CreateMeshRenderEntity(TextFormat("building_%c", 'a' + building_index));
SceneNode *transform_node = CreateTransformNode();
SceneNodeAddChild(transform_node, mesh_render_node);
Transformable transform = TC_CAST(transform_node->entity, Transformable);
Transform local = transform.tc->get_transform(transform.data);
local.translation.x = (float)(x * 40);
local.translation.z = (float)(y * 40);
transform.tc->set_transform(transform.data, local);
return transform_node;
}
SceneNode *GenerateCity() {
Internal_LoadModelsIfRequired();
SceneNode *root = CreateTransformNode();
for(size_t x = 0; x < CITY_SIZE.x; ++x) {
for(size_t y = 0; y < CITY_SIZE.y; ++y) {
SceneNodeAddChild(root, Internal_CreateBuilding(x, y));
}
}
return root;
}

View file

@ -1,8 +0,0 @@
#ifndef CITY_GENERATOR_H
#define CITY_GENERATOR_H
#include "core/scene.h"
extern SceneNode *GenerateCity();
#endif // !CITY_GENERATOR_H

View file

@ -20,11 +20,6 @@ impl_SceneNodeEntity_for(CameraNode,
SceneNode *CreateCameraNode() { SceneNode *CreateCameraNode() {
CameraNode *self = new(CameraNode); CameraNode *self = new(CameraNode);
*self = (CameraNode){
.fov = 60,
.node = NULL,
.transform = tc_null(Transformable)
};
return CreateSceneNode(CameraNode_as_SceneNodeEntity(self)); return CreateSceneNode(CameraNode_as_SceneNodeEntity(self));
} }
@ -56,7 +51,7 @@ Camera3D CameraNodeGetCamera(CameraNode *self) {
Vector3 forward = MATRIX_FORWARD(mat); Vector3 forward = MATRIX_FORWARD(mat);
// construct a new camera at the global transform location and facing the forward vector // construct a new camera at the global transform location and facing the forward vector
return (Camera3D){ return (Camera3D){
.fovy = self->fov, .fovy = 90,
.position = global_transform.translation, .position = global_transform.translation,
.projection = CAMERA_PERSPECTIVE, .projection = CAMERA_PERSPECTIVE,
.target = Vector3Add(global_transform.translation, forward), .target = Vector3Add(global_transform.translation, forward),

View file

@ -10,7 +10,6 @@
typedef struct CameraNode { typedef struct CameraNode {
SceneNode *node; SceneNode *node;
Transformable transform; Transformable transform;
float fov;
} CameraNode; } CameraNode;
//! Instantiate new camera node //! Instantiate new camera node

View file

@ -54,6 +54,7 @@ void RenderNextFrame() {
BeginMode3D(CameraNodeGetCamera(camera)); BeginMode3D(CameraNodeGetCamera(camera));
list_foreach(Renderable *,object, &g_render_objects) list_foreach(Renderable *,object, &g_render_objects)
object->tc->draw(object->data); object->tc->draw(object->data);
DrawGrid(500, 1.f); // TODO Remove this (or make it a scene node entity)
EndMode3D(); EndMode3D();
} }
DrawFPS(20, 20); DrawFPS(20, 20);

View file

@ -132,9 +132,9 @@ bool GetModelResource(char const *path, ModelResource *out) {
ResourceContainer *container = hash_map_get_as(ResourceContainer, &g_resource_map, &path); ResourceContainer *container = hash_map_get_as(ResourceContainer, &g_resource_map, &path);
*out = ResourceEmpty(Model); *out = ResourceEmpty(Model);
// assert some assumptions about the found resource // assert some assumptions about the found resource
ASSERT_RETURN(container != NULL, false, "GetModelResource: Resource %s not in index.", path); ASSERT_RETURN(container != NULL, false, "GetTextureResource: Resource %s not in index.", path);
ASSERT_RETURN(container->type == RESOURCE_MODEL, false, "GetModelResource: Resource %s is not a Texture.", path); ASSERT_RETURN(container->type == RESOURCE_MODEL, false, "GetTextureResource: Resource %s is not a Texture.", path);
ASSERT_RETURN(strcmp(container->name, path) == 0, false, "GetModelResource: Resource %s was loaded for path %s", container->name, path); ++container->use_counter;
*out = (ModelResource) { *out = (ModelResource) {
.handle = container, .handle = container,
.resource = &container->model .resource = &container->model
@ -174,20 +174,16 @@ bool IsResourceLoaded(ResourceHandle handle) {
void LoadResource(ResourceHandle handle) { void LoadResource(ResourceHandle handle) {
ASSERT_RETURN(handle != NULL,, "LoadResource: Resource handle invalid"); ASSERT_RETURN(handle != NULL,, "LoadResource: Resource handle invalid");
if(!handle->is_loaded) { g_load_functions[handle->type](handle);
g_load_functions[handle->type](handle); handle->is_loaded = true;
handle->is_loaded = true;
}
++handle->use_counter; ++handle->use_counter;
} }
void ReleaseResource(ResourceHandle handle) { void ReleaseResource(ResourceHandle handle) {
ASSERT_RETURN(handle != NULL,, "ReleaseResource: Resource handle invalid"); ASSERT_RETURN(handle != NULL,, "ReleaseResource: Resource handle invalid");
ASSERT_RETURN_WARN(handle->is_loaded,, "ReleaseResource: Resource %s is not loaded.", handle->path); ASSERT_RETURN_WARN(handle->is_loaded,, "ReleaseResource: Resource %s is not loaded.", handle->path);
if(handle->use_counter == 1) { g_unload_functions[handle->type](handle);
g_unload_functions[handle->type](handle); handle->is_loaded = false;
handle->is_loaded = false;
}
--handle->use_counter; --handle->use_counter;
} }

View file

@ -170,23 +170,8 @@ SceneNode *SceneNodeGetChildByTypeclass(SceneNode *self, char const *typeclass,
SceneNodeEntity entity = (*child)->entity; SceneNodeEntity entity = (*child)->entity;
if(mirror_get_function(entity.data, entity.mirror, typeclass)) if(mirror_get_function(entity.data, entity.mirror, typeclass))
return *child; return *child;
if(recurse) { if(recurse)
SceneNode *recursed = SceneNodeGetChildByTypeclass(*child, typeclass, recurse); SceneNodeGetChildByTypeclass(*child, typeclass, recurse);
if(recursed != NULL) return recursed;
}
}
return NULL;
}
SceneNode *SceneNodeGetChildByTypeid(SceneNode *self, typeid id, bool recurse) {
list_foreach(SceneNode **,child, &self->children) {
SceneNodeEntity entity = (*child)->entity;
if(entity.mirror->get_typeid() == id)
return *child;
if(recurse) {
SceneNode *recursed = SceneNodeGetChildByTypeid(*child, id, recurse);
if(recursed != NULL) return recursed;
}
} }
return NULL; return NULL;
} }

View file

@ -41,12 +41,10 @@ extern void SceneNodeRemoveChild(SceneNode *self, SceneNode *child);
extern void SceneNodeAttachEntity(SceneNode *self, SceneNodeEntity entity); extern void SceneNodeAttachEntity(SceneNode *self, SceneNodeEntity entity);
//! Detach an entity from a scene node //! Detach an entity from a scene node
extern SceneNodeEntity SceneNodeDetachEntity(SceneNode *self); extern SceneNodeEntity SceneNodeDetachEntity(SceneNode *self);
//! Returns the first child node with an entity that implements a specific typeclass. //! Returns the first child node that implements a specific typeclass.
//! Optionally recurses through the entire branch //! Optionally recurses through the entire branch
extern SceneNode *SceneNodeGetChildByTypeclass(SceneNode *self, char const *typeclass, bool recurse); extern SceneNode *SceneNodeGetChildByTypeclass(SceneNode *self, char const *typeclass, bool recurse);
//! Returns the first child node with an entity that matches the given typeid.
//! Optionally recurses through the entire branch.
extern SceneNode *SceneNodeGetChildByTypeid(SceneNode *self, typeid id, bool recurse);
//! Instantiate a new scene with a root node. //! Instantiate a new scene with a root node.
extern Scene *CreateScene(SceneNode *root); extern Scene *CreateScene(SceneNode *root);
//! Destroy a node and it's scene tree. //! Destroy a node and it's scene tree.

View file

@ -1,7 +1,6 @@
#include "raylib.h" #include "raylib.h"
#include "player_controller.h" #include "player_controller.h"
#include "camera_controller.h" #include "camera_controller.h"
#include "city_generator.h"
#include "core/input.h" #include "core/input.h"
#include "core/camera_node.h" #include "core/camera_node.h"
#include "core/engine_global.h" #include "core/engine_global.h"
@ -49,7 +48,6 @@ Scene *CreateInitialScene() {
SceneNodeAddChild(root, model); SceneNodeAddChild(root, model);
SceneNodeAddChild(root, camera_scene); SceneNodeAddChild(root, camera_scene);
SceneNodeAddChild(camera_scene, CreateCameraController(TC_CAST(model->entity, Transformable))); SceneNodeAddChild(camera_scene, CreateCameraController(TC_CAST(model->entity, Transformable)));
SceneNodeAddChild(root, GenerateCity());
return CreateScene(root); return CreateScene(root);
} }
@ -58,10 +56,7 @@ void ConfigureInput() {
AddAction("pitch_down", INPUT_LISTENER_KEY, 0, KEY_W); AddAction("pitch_down", INPUT_LISTENER_KEY, 0, KEY_W);
AddAction("roll_left", INPUT_LISTENER_KEY, 0, KEY_A); AddAction("roll_left", INPUT_LISTENER_KEY, 0, KEY_A);
AddAction("roll_right", INPUT_LISTENER_KEY, 0, KEY_D); AddAction("roll_right", INPUT_LISTENER_KEY, 0, KEY_D);
AddAction("yaw_right", INPUT_LISTENER_KEY, 0, KEY_E); AddAction("stop", INPUT_LISTENER_KEY, 0, KEY_SPACE);
AddAction("yaw_left", INPUT_LISTENER_KEY, 0, KEY_Q);
AddAction("brake", INPUT_LISTENER_KEY, 0, KEY_SPACE);
AddAction("boost", INPUT_LISTENER_KEY, 0, KEY_LEFT_SHIFT);
} }
int main() { int main() {

View file

@ -1,8 +1,7 @@
#include "player_controller.h" #include "player_controller.h"
#include "core/render.h" #include "core/render.h"
#include "core/input.h"
#include "utils/typeclass_helpers.h"
#include "utils/debug.h" #include "utils/debug.h"
#include "core/input.h"
#include "raylib.h" #include "raylib.h"
START_REFLECT(PlayerController); START_REFLECT(PlayerController);
@ -20,25 +19,10 @@ impl_SceneNodeEntity_for(PlayerController,
PlayerControllerTick PlayerControllerTick
) )
static float const PLAYER_BASE_SPEED = 10.f;
static float const PLAYER_BASE_ACCELERATION = 10.f;
static float const PLAYER_BOOST_SPEED = 30.f;
static float const PLAYER_BOOST_ACCELERATION = 60.f;
static float const PLAYER_BRAKE_SPEED = 0.5f;
static float const PLAYER_BRAKE_DECELERATION = 10.f;
static float const PLAYER_FALL_SPEED = 10.f;
static float const PLAYER_FALL_ACCELERATION = 10.f;
SceneNode *CreatePlayerController() { SceneNode *CreatePlayerController() {
PlayerController *self = new(PlayerController); PlayerController *self = new(PlayerController);
*self = (PlayerController) { self->rotation = self->fly_input = Vector2Zero();
.node = NULL, self->stopped = false;
.transform = tc_null(Transformable),
.rotation = Vector3Zero(),
.fly_input = Vector3Zero(),
.speed = 10.f,
.brake = false
};
return CreateSceneNode(PlayerController_as_SceneNodeEntity(self)); return CreateSceneNode(PlayerController_as_SceneNodeEntity(self));
} }
@ -49,42 +33,32 @@ void DestroyPlayerController(PlayerController *self) {
void PlayerControllerEnterTree(PlayerController *self) { void PlayerControllerEnterTree(PlayerController *self) {
self->transform = TC_CAST(self->node->parent->entity, Transformable); self->transform = TC_CAST(self->node->parent->entity, Transformable);
DisableCursor(); DisableCursor();
AddListener("pitch_up", ButtonInputListener(self, PlayerControllerInputPitchUp)); AddListener("pitch_up", ButtonInputListener(self, PlayerControllerUpInput));
AddListener("pitch_down", ButtonInputListener(self, PlayerControllerInputPitchDown)); AddListener("pitch_down", ButtonInputListener(self, PlayerControllerDownInput));
AddListener("roll_left", ButtonInputListener(self, PlayerControllerInputRollLeft)); AddListener("roll_left", ButtonInputListener(self, PlayerControllerLeftInput));
AddListener("roll_right", ButtonInputListener(self, PlayerControllerInputRollRight)); AddListener("roll_right", ButtonInputListener(self, PlayerControllerRightInput));
AddListener("yaw_left", ButtonInputListener(self, PlayerControllerInputYawLeft)); AddListener("stop", ButtonInputListener(self, PlayerControllerStopInput));
AddListener("yaw_right", ButtonInputListener(self, PlayerControllerInputYawRight));
AddListener("brake", ButtonInputListener(self, PlayerControllerInputBrake));
AddListener("boost", ButtonInputListener(self, PlayerControllerInputBoost));
} }
void PlayerControllerExitTree(PlayerController *self) { void PlayerControllerExitTree(PlayerController *self) {
RemoveAllListeners(self); RemoveAllListeners(self);
} }
static
float MoveTowards(float from, float to, float delta) {
if(from == to) return to;
float const diff = to - from;
float const sign = signbit(diff) ? -1.f : 1.f;
return from + sign * fminf(delta, fabsf(diff));
}
//! angular acceleration limited to local X and Z axes //! angular acceleration limited to local X and Z axes
static static
void PlayerControllerTickAngularAcceleration(PlayerController *self, double delta) { void PlayerControllerTickAngularAcceleration(PlayerController *self, double delta) {
self->rotation.x = MoveTowards(self->rotation.x, self->fly_input.x, delta * 4.f); Vector2 diff = Vector2Subtract(self->fly_input, self->rotation);
self->rotation.y = MoveTowards(self->rotation.y, self->fly_input.y, delta * 4.f); float const length = Vector2Length(diff);
self->rotation.z = MoveTowards(self->rotation.z, self->fly_input.z, delta * 4.f); if(length != 0.f)
self->rotation = Vector2Add(self->rotation, Vector2Scale(diff, 1.0f/length * fminf(4.f * delta, length)));
} }
//! linear acceleration limited to the local Z axis //! linear acceleration limited to the local Z axis
static static
void PlayerControllerTickLinearAcceleration(PlayerController *self, double delta) { void PlayerControllerTickLinearAcceleration(PlayerController *self, double delta) {
float const target = self->brake ? PLAYER_BRAKE_SPEED : (self->boost ? PLAYER_BOOST_SPEED : PLAYER_BASE_SPEED); float const target = self->stopped ? 10.f : (self->rotation.y == 0.f ? 30.f : 20.f);
float const acceleration = target < self->speed ? PLAYER_BRAKE_DECELERATION : (self->boost ? PLAYER_BOOST_ACCELERATION : PLAYER_BASE_ACCELERATION); float const speed_diff = target - self->speed;
self->speed = MoveTowards(self->speed, target, delta * acceleration); self->speed = self->speed + copysignf(fminf(fabsf(speed_diff), 10.f * delta), speed_diff);
} }
//! Update linear transform based on velocities //! Update linear transform based on velocities
@ -92,11 +66,9 @@ static
void PlayerControllerTickTransform(PlayerController *self, double delta) { void PlayerControllerTickTransform(PlayerController *self, double delta) {
Transform global_transform = self->transform.tc->get_global_transform(self->transform.data); Transform global_transform = self->transform.tc->get_global_transform(self->transform.data);
Matrix global_matrix = TransformGetMatrix(global_transform); Matrix global_matrix = TransformGetMatrix(global_transform);
float const rotate_speed = 1.f - 0.9f * self->speed / PLAYER_BOOST_SPEED;
global_transform.translation = Vector3Add(global_transform.translation, Vector3Scale(MATRIX_FORWARD(global_matrix), self->speed * delta)); global_transform.translation = Vector3Add(global_transform.translation, Vector3Scale(MATRIX_FORWARD(global_matrix), self->speed * delta));
global_transform.rotation = QuaternionMultiply(QuaternionFromAxisAngle(MATRIX_FORWARD(global_matrix), self->rotation.x * 1.25f * delta), global_transform.rotation); global_transform.rotation = QuaternionMultiply(QuaternionFromAxisAngle(MATRIX_FORWARD(global_matrix), self->rotation.x * 2.f * delta), global_transform.rotation);
global_transform.rotation = QuaternionMultiply(QuaternionFromAxisAngle(MATRIX_RIGHT(global_matrix), self->rotation.y * rotate_speed * 3.75f * delta), global_transform.rotation); global_transform.rotation = QuaternionMultiply(QuaternionFromAxisAngle(MATRIX_RIGHT(global_matrix), self->rotation.y * 2.5f * delta), global_transform.rotation);
global_transform.rotation = QuaternionMultiply(QuaternionFromAxisAngle(MATRIX_UP(global_matrix), self->rotation.z * rotate_speed * 1.f * delta), global_transform.rotation);
self->transform.tc->set_global_transform(self->transform.data, global_transform); self->transform.tc->set_global_transform(self->transform.data, global_transform);
} }
@ -106,34 +78,22 @@ void PlayerControllerTick(PlayerController *self, double delta) {
PlayerControllerTickTransform(self, delta); PlayerControllerTickTransform(self, delta);
} }
void PlayerControllerInputRollLeft(PlayerController *self, bool value) { void PlayerControllerLeftInput(PlayerController *self, bool value) {
self->fly_input.x += value ? -1 : +1; self->fly_input.x += value ? -1 : +1;
} }
void PlayerControllerInputRollRight(PlayerController *self, bool value) { void PlayerControllerRightInput(PlayerController *self, bool value) {
self->fly_input.x += value ? +1 : -1; self->fly_input.x += value ? +1 : -1;
} }
void PlayerControllerInputPitchUp(PlayerController *self, bool value) { void PlayerControllerUpInput(PlayerController *self, bool value) {
self->fly_input.y += value ? -1 : +1; self->fly_input.y += value ? -1 : +1;
} }
void PlayerControllerInputPitchDown(PlayerController *self, bool value) { void PlayerControllerDownInput(PlayerController *self, bool value) {
self->fly_input.y += value ? +1 : -1; self->fly_input.y += value ? +1 : -1;
} }
void PlayerControllerInputYawRight(PlayerController *self, bool value) { void PlayerControllerStopInput(PlayerController *self, bool value) {
self->fly_input.z += value ? -1 : +1; self->stopped = value;
}
void PlayerControllerInputYawLeft(PlayerController *self, bool value) {
self->fly_input.z += value ? +1 : -1;
}
void PlayerControllerInputBrake(PlayerController *self, bool value) {
self->brake = value;
}
void PlayerControllerInputBoost(PlayerController *self, bool value) {
self->boost = value;
} }

View file

@ -12,11 +12,10 @@
typedef struct PlayerController { typedef struct PlayerController {
SceneNode *node; SceneNode *node;
Transformable transform; Transformable transform;
Vector3 fly_input; Vector2 fly_input;
Vector3 rotation; Vector2 rotation;
float speed; float speed;
bool brake; bool stopped;
bool boost;
} PlayerController; } PlayerController;
SceneNode *CreatePlayerController(); SceneNode *CreatePlayerController();
@ -26,14 +25,11 @@ extern void PlayerControllerEnterTree(PlayerController *self);
extern void PlayerControllerExitTree(PlayerController *self); extern void PlayerControllerExitTree(PlayerController *self);
extern void PlayerControllerTick(PlayerController *self, double delta); extern void PlayerControllerTick(PlayerController *self, double delta);
extern void PlayerControllerInputRollLeft(PlayerController *self, bool value); extern void PlayerControllerLeftInput(PlayerController *self, bool value);
extern void PlayerControllerInputRollRight(PlayerController *self, bool value); extern void PlayerControllerRightInput(PlayerController *self, bool value);
extern void PlayerControllerInputPitchUp(PlayerController *self, bool value); extern void PlayerControllerUpInput(PlayerController *self, bool value);
extern void PlayerControllerInputPitchDown(PlayerController *self, bool value); extern void PlayerControllerDownInput(PlayerController *self, bool value);
extern void PlayerControllerInputYawRight(PlayerController *self, bool value); extern void PlayerControllerStopInput(PlayerController *self, bool value);
extern void PlayerControllerInputYawLeft(PlayerController *self, bool value);
extern void PlayerControllerInputBrake(PlayerController *self, bool value);
extern void PlayerControllerInputBoost(PlayerController *self, bool value);
DECL_REFLECT(PlayerController); DECL_REFLECT(PlayerController);
decl_typeclass_impl(SceneNodeEntity, PlayerController); decl_typeclass_impl(SceneNodeEntity, PlayerController);

@ -1 +1 @@
Subproject commit bb201d5085cfb2e54ebad9a0bf22347a5251f7a2 Subproject commit faf0463e3791ee97dfc370282ecad4cd3b4ca475