@woosh/meep-engine 2.138.17 → 2.138.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/build/bundle-worker-image-decoder.js +1 -1
- package/editor/actions/concrete/ActionUpdateTexture.d.ts +12 -0
- package/editor/actions/concrete/ActionUpdateTexture.d.ts.map +1 -0
- package/editor/actions/concrete/ArrayCopyAction.d.ts +20 -0
- package/editor/actions/concrete/ArrayCopyAction.d.ts.map +1 -0
- package/editor/actions/concrete/ComponentRemoveAction.d.ts +11 -0
- package/editor/actions/concrete/ComponentRemoveAction.d.ts.map +1 -0
- package/editor/actions/concrete/ModifyPatchSampler2DAction.d.ts +47 -0
- package/editor/actions/concrete/ModifyPatchSampler2DAction.d.ts.map +1 -0
- package/editor/actions/concrete/ModifyPatchTextureArray2DAction.d.ts +38 -0
- package/editor/actions/concrete/ModifyPatchTextureArray2DAction.d.ts.map +1 -0
- package/editor/actions/concrete/PaintTerrainOverlayAction.d.ts +23 -0
- package/editor/actions/concrete/PaintTerrainOverlayAction.d.ts.map +1 -0
- package/editor/actions/concrete/PatchTerrainHeightAction.d.ts +19 -0
- package/editor/actions/concrete/PatchTerrainHeightAction.d.ts.map +1 -0
- package/editor/actions/concrete/SelectionRemoveAction.d.ts +10 -0
- package/editor/actions/concrete/SelectionRemoveAction.d.ts.map +1 -0
- package/editor/actions/concrete/WriteGridValueAction.d.ts +15 -0
- package/editor/actions/concrete/WriteGridValueAction.d.ts.map +1 -0
- package/editor/ecs/component/FieldDescriptor.d.ts +27 -0
- package/editor/ecs/component/FieldDescriptor.d.ts.map +1 -0
- package/editor/ecs/component/FieldValueAdapter.d.ts +7 -0
- package/editor/ecs/component/FieldValueAdapter.d.ts.map +1 -0
- package/editor/ecs/component/createFieldEditor.d.ts +9 -0
- package/editor/ecs/component/createFieldEditor.d.ts.map +1 -0
- package/editor/ecs/component/createObjectEditor.d.ts +14 -0
- package/editor/ecs/component/createObjectEditor.d.ts.map +1 -0
- package/editor/ecs/component/editors/geom/QuaternionEditor.d.ts.map +1 -1
- package/editor/ecs/component/findNearestRegisteredType.d.ts +8 -0
- package/editor/ecs/component/findNearestRegisteredType.d.ts.map +1 -0
- package/editor/process/ObstacleGridDisplayProcess.d.ts.map +1 -1
- package/editor/process/symbolic/makeGridPositionSymbolDisplay.d.ts.map +1 -1
- package/editor/tools/GridPaintTool.d.ts +17 -0
- package/editor/tools/GridPaintTool.d.ts.map +1 -0
- package/editor/tools/SelectionTool.d.ts +27 -0
- package/editor/tools/SelectionTool.d.ts.map +1 -0
- package/editor/tools/TopDownCameraControlTool.d.ts +13 -0
- package/editor/tools/TopDownCameraControlTool.d.ts.map +1 -0
- package/editor/view/ecs/ComponentControlFactory.d.ts.map +1 -0
- package/editor/view/ecs/ComponentControlView.d.ts.map +1 -1
- package/editor/view/ecs/EntityEditor.d.ts.map +1 -0
- package/editor/view/ecs/EntityList.d.ts.map +1 -0
- package/editor/view/ecs/HierarchicalEntityListView.d.ts.map +1 -0
- package/editor/view/node-graph/NodeGraphEditorView.d.ts.map +1 -1
- package/editor/view/node-graph/NodeGraphView.d.ts.map +1 -1
- package/editor/view/node-graph/NodeView.d.ts.map +1 -1
- package/editor/view/node-graph/PortView.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core/binary/BinaryBuffer.d.ts +1 -1
- package/src/core/binary/BinaryBuffer.js +1 -1
- package/src/core/binary/BitSet.d.ts +1 -1
- package/src/core/binary/BitSet.js +1 -1
- package/src/core/cache/Cache.js +1 -1
- package/src/core/cache/LoadingCache.js +1 -1
- package/src/core/collection/list/List.js +1 -1
- package/src/core/collection/map/HashMap.js +1 -1
- package/src/core/collection/table/RowFirstTable.d.ts +1 -1
- package/src/core/collection/table/RowFirstTable.js +1 -1
- package/src/core/collection/table/RowFirstTableSpec.d.ts +1 -1
- package/src/core/collection/table/RowFirstTableSpec.js +1 -1
- package/src/core/color/oklab/compute_max_saturation.d.ts +1 -1
- package/src/core/color/oklab/compute_max_saturation.js +1 -1
- package/src/core/color/oklab/find_cusp.d.ts +1 -1
- package/src/core/color/oklab/find_cusp.js +1 -1
- package/src/core/color/oklab/find_gamut_intersection.d.ts +1 -1
- package/src/core/color/oklab/find_gamut_intersection.js +1 -1
- package/src/core/color/oklab/okhsv_to_linear_srgb.d.ts +1 -1
- package/src/core/color/oklab/okhsv_to_linear_srgb.js +1 -1
- package/src/core/color/oklab/oklab_to_linear_srgb.d.ts +1 -1
- package/src/core/color/oklab/oklab_to_linear_srgb.js +1 -1
- package/src/core/color/oklab/oklab_to_xyz.d.ts +1 -1
- package/src/core/color/oklab/oklab_to_xyz.js +1 -1
- package/src/core/color/oklab/xyz_to_oklab.d.ts +1 -1
- package/src/core/color/oklab/xyz_to_oklab.js +1 -1
- package/src/core/geom/2d/convex-hull/fixed_convex_hull_relaxation.d.ts +1 -1
- package/src/core/geom/2d/convex-hull/fixed_convex_hull_relaxation.js +1 -1
- package/src/core/geom/3d/aabb/aabb3_nearest_point_on_surface.d.ts.map +1 -1
- package/src/core/geom/3d/aabb/aabb3_nearest_point_on_surface.js +57 -65
- package/src/core/geom/3d/octahedra/octahedral_uv_wrap.d.ts +1 -1
- package/src/core/geom/3d/octahedra/octahedral_uv_wrap.js +1 -1
- package/src/core/geom/3d/quaternion/quat_decode_from_uint32.d.ts +1 -1
- package/src/core/geom/3d/quaternion/quat_decode_from_uint32.js +1 -1
- package/src/core/geom/3d/quaternion/quat_encode_to_uint32.d.ts +1 -1
- package/src/core/geom/3d/quaternion/quat_encode_to_uint32.js +1 -1
- package/src/core/geom/3d/shape/AbstractShape3D.d.ts +74 -3
- package/src/core/geom/3d/shape/CapsuleShape3D.d.ts +37 -0
- package/src/core/geom/3d/shape/CapsuleShape3D.d.ts.map +1 -0
- package/src/core/geom/3d/shape/CapsuleShape3D.js +210 -0
- package/src/core/geom/3d/shape/PointShape3D.d.ts +5 -0
- package/src/core/geom/3d/shape/PointShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/PointShape3D.js +52 -14
- package/src/core/geom/3d/shape/TransformedShape3D.d.ts +75 -12
- package/src/core/geom/3d/shape/TransformedShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/TransformedShape3D.js +12 -3
- package/src/core/geom/3d/shape/UnionShape3D.d.ts +47 -5
- package/src/core/geom/3d/shape/UnionShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/UnitCubeShape3D.d.ts +12 -5
- package/src/core/geom/3d/shape/UnitCubeShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/UnitSphereShape3D.d.ts +17 -5
- package/src/core/geom/3d/shape/UnitSphereShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/json/shape_to_type.d.ts.map +1 -1
- package/src/core/geom/3d/shape/json/shape_to_type.js +4 -1
- package/src/core/geom/3d/shape/json/type_adapters.d.ts +17 -2
- package/src/core/geom/3d/shape/json/type_adapters.d.ts.map +1 -1
- package/src/core/geom/3d/shape/json/type_adapters.js +18 -2
- package/src/core/geom/3d/shape/util/compute_signed_distance_gradient_by_sampling.d.ts.map +1 -1
- package/src/core/geom/3d/shape/util/compute_signed_distance_gradient_by_sampling.js +51 -48
- package/src/core/geom/3d/tetrahedra/compute_bounding_simplex_3d.d.ts +1 -1
- package/src/core/geom/3d/tetrahedra/compute_bounding_simplex_3d.js +1 -1
- package/src/core/geom/ConicRay.d.ts +1 -1
- package/src/core/geom/ConicRay.js +1 -1
- package/src/core/geom/Quaternion.d.ts +2 -2
- package/src/core/geom/Quaternion.js +2 -2
- package/src/core/geom/Vector1.d.ts +1 -1
- package/src/core/geom/Vector1.js +1 -1
- package/src/core/geom/Vector2.d.ts +1 -1
- package/src/core/geom/Vector2.js +1 -1
- package/src/core/geom/Vector3.d.ts +1 -1
- package/src/core/geom/Vector3.js +1 -1
- package/src/core/geom/packing/miniball/Miniball.d.ts +1 -1
- package/src/core/geom/packing/miniball/Miniball.js +1 -1
- package/src/core/geom/vec3/serialization/v3_binary_equality_decode.d.ts +1 -1
- package/src/core/geom/vec3/serialization/v3_binary_equality_decode.js +1 -1
- package/src/core/geom/vec3/serialization/v3_binary_equality_encode.d.ts +1 -1
- package/src/core/geom/vec3/serialization/v3_binary_equality_encode.js +1 -1
- package/src/core/math/spline/spline3_hermite.d.ts +1 -1
- package/src/core/math/spline/spline3_hermite.js +1 -1
- package/src/core/math/spline/spline3_hermite_bounds.d.ts +1 -1
- package/src/core/math/spline/spline3_hermite_bounds.js +1 -1
- package/src/core/math/spline/spline3_hermite_bounds_t.d.ts +1 -1
- package/src/core/math/spline/spline3_hermite_bounds_t.js +1 -1
- package/src/core/math/spline/spline3_hermite_to_monomial.d.ts +1 -1
- package/src/core/math/spline/spline3_hermite_to_monomial.js +1 -1
- package/src/core/model/ObservedBoolean.d.ts +1 -1
- package/src/core/model/ObservedBoolean.js +1 -1
- package/src/core/model/ObservedString.d.ts +1 -1
- package/src/core/model/ObservedString.js +1 -1
- package/src/core/process/undo/Action.js +1 -1
- package/src/core/process/undo/ActionProcessor.js +1 -1
- package/src/engine/animation/curve/AnimationCurve.d.ts +1 -1
- package/src/engine/animation/curve/AnimationCurve.js +1 -1
- package/src/engine/animation/curve/Keyframe.d.ts +1 -1
- package/src/engine/animation/curve/Keyframe.js +1 -1
- package/src/engine/animation/curve/animation_curve_compute_aabb.d.ts +1 -1
- package/src/engine/animation/curve/animation_curve_compute_aabb.js +1 -1
- package/src/engine/animation/curve/animation_curve_optimize.d.ts +1 -1
- package/src/engine/animation/curve/animation_curve_optimize.js +2 -2
- package/src/engine/asset/loaders/image/jpeg/JpegImage.js +1 -1
- package/src/engine/asset/loaders/image/png/PNGReader.d.ts.map +1 -1
- package/src/engine/asset/loaders/image/png/PNGReader.js +27 -7
- package/src/engine/asset/loaders/image/png/crc32.d.ts +1 -1
- package/src/engine/asset/loaders/image/png/crc32.js +1 -1
- package/src/engine/control/first-person/DESIGN.md +616 -0
- package/src/engine/control/first-person/FirstPersonPlayerController.d.ts +229 -0
- package/src/engine/control/first-person/FirstPersonPlayerController.d.ts.map +1 -0
- package/src/engine/control/first-person/FirstPersonPlayerController.js +176 -0
- package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.d.ts +251 -0
- package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.d.ts.map +1 -0
- package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.js +205 -0
- package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.d.ts +151 -0
- package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.d.ts.map +1 -0
- package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.js +799 -0
- package/src/engine/control/first-person/math/computeJumpFromApex.d.ts +23 -0
- package/src/engine/control/first-person/math/computeJumpFromApex.d.ts.map +1 -0
- package/src/engine/control/first-person/math/computeJumpFromApex.js +23 -0
- package/src/engine/control/first-person/math/criticallyDampedSpring.d.ts +23 -0
- package/src/engine/control/first-person/math/criticallyDampedSpring.d.ts.map +1 -0
- package/src/engine/control/first-person/math/criticallyDampedSpring.js +34 -0
- package/src/engine/control/first-person/math/dampedSpringStep.d.ts +23 -0
- package/src/engine/control/first-person/math/dampedSpringStep.d.ts.map +1 -0
- package/src/engine/control/first-person/math/dampedSpringStep.js +72 -0
- package/src/engine/control/first-person/math/stepTowards.d.ts +12 -0
- package/src/engine/control/first-person/math/stepTowards.d.ts.map +1 -0
- package/src/engine/control/first-person/math/stepTowards.js +20 -0
- package/src/engine/control/first-person/pose/FirstPersonPose.d.ts +69 -0
- package/src/engine/control/first-person/pose/FirstPersonPose.d.ts.map +1 -0
- package/src/engine/control/first-person/pose/FirstPersonPose.js +91 -0
- package/src/engine/control/first-person/prototype_first_person_controller.d.ts +2 -0
- package/src/engine/control/first-person/prototype_first_person_controller.d.ts.map +1 -0
- package/src/engine/control/first-person/prototype_first_person_controller.js +314 -0
- package/src/engine/ecs/Entity.js +1 -1
- package/src/engine/ecs/EntityComponentDataset.js +1 -1
- package/src/engine/ecs/EntityManager.d.ts +1 -1
- package/src/engine/ecs/EntityManager.js +1 -1
- package/src/engine/ecs/EntityObserver.js +1 -1
- package/src/engine/ecs/EntityReference.d.ts +1 -1
- package/src/engine/ecs/EntityReference.js +1 -1
- package/src/engine/ecs/System.js +1 -1
- package/src/engine/ecs/attachment/AttachmentSystem.d.ts +2 -2
- package/src/engine/ecs/attachment/AttachmentSystem.d.ts.map +1 -1
- package/src/engine/ecs/grid/HeightMap2AOMap.d.ts.map +1 -1
- package/src/engine/ecs/grid/HeightMap2AOMap.js +3 -2
- package/src/engine/ecs/guid/UUID.d.ts +1 -1
- package/src/engine/ecs/guid/UUID.js +1 -1
- package/src/engine/ecs/name/Name.d.ts +1 -1
- package/src/engine/ecs/name/Name.js +1 -1
- package/src/engine/ecs/parent/ParentEntity.js +1 -1
- package/src/engine/ecs/storage/BinaryBufferDeSerializer.d.ts +1 -1
- package/src/engine/ecs/storage/BinaryBufferDeSerializer.js +1 -1
- package/src/engine/ecs/storage/BinaryBufferSerializer.d.ts +1 -1
- package/src/engine/ecs/storage/BinaryBufferSerializer.js +1 -1
- package/src/engine/ecs/storage/binary/BinaryClassSerializationAdapter.js +1 -1
- package/src/engine/ecs/storage/binary/BinarySerializationRegistry.d.ts +1 -1
- package/src/engine/ecs/storage/binary/BinarySerializationRegistry.js +1 -1
- package/src/engine/ecs/storage/binary/collection/BinaryCollectionDeSerializer.d.ts +1 -1
- package/src/engine/ecs/storage/binary/collection/BinaryCollectionDeSerializer.js +1 -1
- package/src/engine/ecs/transform/Transform.d.ts +1 -1
- package/src/engine/ecs/transform/Transform.js +1 -1
- package/src/engine/graphics/ecs/path/PathDisplaySystem.d.ts.map +1 -1
- package/src/engine/graphics/impostors/octahedral/ImpostorBaker.d.ts.map +1 -1
- package/src/engine/graphics/impostors/octahedral/ImpostorBaker.js +10 -1
- package/src/engine/graphics/impostors/voxel/README.md +149 -0
- package/src/engine/graphics/impostors/voxel/VoxelImpostorBaker.d.ts +91 -0
- package/src/engine/graphics/impostors/voxel/VoxelImpostorBaker.d.ts.map +1 -0
- package/src/engine/graphics/impostors/voxel/VoxelImpostorBaker.js +376 -0
- package/src/engine/graphics/impostors/voxel/VoxelImpostorDescription.d.ts +128 -0
- package/src/engine/graphics/impostors/voxel/VoxelImpostorDescription.d.ts.map +1 -0
- package/src/engine/graphics/impostors/voxel/VoxelImpostorDescription.js +141 -0
- package/src/engine/graphics/impostors/voxel/bake/read_atlas_to_samples.d.ts +38 -0
- package/src/engine/graphics/impostors/voxel/bake/read_atlas_to_samples.d.ts.map +1 -0
- package/src/engine/graphics/impostors/voxel/bake/read_atlas_to_samples.js +338 -0
- package/src/engine/graphics/impostors/voxel/bake/voxelize_samples.d.ts +43 -0
- package/src/engine/graphics/impostors/voxel/bake/voxelize_samples.d.ts.map +1 -0
- package/src/engine/graphics/impostors/voxel/bake/voxelize_samples.js +192 -0
- package/src/engine/graphics/impostors/voxel/prototype_voxel_impostors.d.ts +2 -0
- package/src/engine/graphics/impostors/voxel/prototype_voxel_impostors.d.ts.map +1 -0
- package/src/engine/graphics/impostors/voxel/prototype_voxel_impostors.js +343 -0
- package/src/engine/graphics/impostors/voxel/shader/VoxelImpostorShaderDepthV0.d.ts +13 -0
- package/src/engine/graphics/impostors/voxel/shader/VoxelImpostorShaderDepthV0.d.ts.map +1 -0
- package/src/engine/graphics/impostors/voxel/shader/VoxelImpostorShaderDepthV0.js +99 -0
- package/src/engine/graphics/impostors/voxel/shader/VoxelImpostorShaderLitV0.d.ts +19 -0
- package/src/engine/graphics/impostors/voxel/shader/VoxelImpostorShaderLitV0.d.ts.map +1 -0
- package/src/engine/graphics/impostors/voxel/shader/VoxelImpostorShaderLitV0.js +231 -0
- package/src/engine/graphics/impostors/voxel/shader/VoxelImpostorShaderNormalsV0.d.ts +5 -0
- package/src/engine/graphics/impostors/voxel/shader/VoxelImpostorShaderNormalsV0.d.ts.map +1 -0
- package/src/engine/graphics/impostors/voxel/shader/VoxelImpostorShaderNormalsV0.js +70 -0
- package/src/engine/graphics/impostors/voxel/shader/VoxelImpostorShaderV0.d.ts +13 -0
- package/src/engine/graphics/impostors/voxel/shader/VoxelImpostorShaderV0.d.ts.map +1 -0
- package/src/engine/graphics/impostors/voxel/shader/VoxelImpostorShaderV0.js +127 -0
- package/src/engine/graphics/impostors/voxel/shader/VoxelImpostorShaderViewportDepthV0.d.ts +5 -0
- package/src/engine/graphics/impostors/voxel/shader/VoxelImpostorShaderViewportDepthV0.d.ts.map +1 -0
- package/src/engine/graphics/impostors/voxel/shader/VoxelImpostorShaderViewportDepthV0.js +68 -0
- package/src/engine/graphics/impostors/voxel/util/make_voxel_impostor_geometry.d.ts +27 -0
- package/src/engine/graphics/impostors/voxel/util/make_voxel_impostor_geometry.d.ts.map +1 -0
- package/src/engine/graphics/impostors/voxel/util/make_voxel_impostor_geometry.js +96 -0
- package/src/engine/graphics/particles/particular/engine/utils/volume/ParticleVolume.d.ts +113 -29
- package/src/engine/graphics/render/forward_plus/SPECIFICATION.md +1 -1
- package/src/engine/graphics/shaders/AmbientOcclusionShader.d.ts.map +1 -1
- package/src/engine/graphics/shaders/AmbientOcclusionShader.js +20 -5
- package/src/engine/graphics/texture/sampler/Sampler2D.d.ts +1 -1
- package/src/engine/graphics/texture/sampler/Sampler2D.js +1 -1
- package/src/engine/graphics/texture/sampler/distance/computeSignedDistanceField_NaiveFlood.d.ts +1 -1
- package/src/engine/graphics/texture/sampler/distance/computeSignedDistanceField_NaiveFlood.js +1 -1
- package/src/engine/graphics/util/build_max_height_pyramid.d.ts +21 -14
- package/src/engine/graphics/util/build_max_height_pyramid.d.ts.map +1 -1
- package/src/engine/graphics/util/build_max_height_pyramid.js +107 -70
- package/src/engine/input/devices/InputDeviceSwitch.js +1 -1
- package/src/engine/input/devices/KeyboardDevice.js +1 -1
- package/src/engine/input/devices/PointerDevice.js +1 -1
- package/src/engine/intelligence/behavior/Behavior.js +1 -1
- package/src/engine/intelligence/behavior/SelectorBehavior.d.ts +1 -1
- package/src/engine/intelligence/behavior/SelectorBehavior.js +1 -1
- package/src/engine/intelligence/behavior/behavior_to_dot.d.ts +1 -1
- package/src/engine/intelligence/behavior/behavior_to_dot.js +1 -1
- package/src/engine/intelligence/behavior/composite/CompositeBehavior.d.ts +1 -1
- package/src/engine/intelligence/behavior/composite/CompositeBehavior.js +1 -1
- package/src/engine/intelligence/behavior/composite/ParallelBehavior.d.ts +1 -1
- package/src/engine/intelligence/behavior/composite/ParallelBehavior.js +1 -1
- package/src/engine/intelligence/behavior/composite/SequenceBehavior.d.ts +1 -1
- package/src/engine/intelligence/behavior/composite/SequenceBehavior.js +1 -1
- package/src/engine/intelligence/behavior/decorator/AbstractDecoratorBehavior.d.ts +1 -1
- package/src/engine/intelligence/behavior/decorator/AbstractDecoratorBehavior.js +1 -1
- package/src/engine/intelligence/behavior/decorator/IgnoreFailureBehavior.d.ts +1 -1
- package/src/engine/intelligence/behavior/decorator/IgnoreFailureBehavior.js +1 -1
- package/src/engine/intelligence/behavior/decorator/InvertStatusBehavior.d.ts +1 -1
- package/src/engine/intelligence/behavior/decorator/InvertStatusBehavior.js +1 -1
- package/src/engine/intelligence/behavior/decorator/RepeatBehavior.d.ts +1 -1
- package/src/engine/intelligence/behavior/decorator/RepeatBehavior.js +1 -1
- package/src/engine/intelligence/behavior/decorator/RepeatUntilSuccessBehavior.d.ts +1 -1
- package/src/engine/intelligence/behavior/decorator/RepeatUntilSuccessBehavior.js +1 -1
- package/src/engine/intelligence/behavior/ecs/BehaviorComponent.d.ts +1 -1
- package/src/engine/intelligence/behavior/ecs/BehaviorComponent.js +1 -1
- package/src/engine/intelligence/behavior/ecs/BehaviorSystem.d.ts +1 -1
- package/src/engine/intelligence/behavior/ecs/BehaviorSystem.js +1 -1
- package/src/engine/intelligence/behavior/ecs/DieBehavior.d.ts +1 -1
- package/src/engine/intelligence/behavior/ecs/DieBehavior.js +1 -1
- package/src/engine/intelligence/behavior/ecs/EntityBehavior.d.ts +1 -1
- package/src/engine/intelligence/behavior/ecs/EntityBehavior.js +1 -1
- package/src/engine/intelligence/behavior/ecs/KillBehavior.d.ts +1 -1
- package/src/engine/intelligence/behavior/ecs/KillBehavior.js +1 -1
- package/src/engine/intelligence/behavior/ecs/SendEventBehavior.d.ts +1 -1
- package/src/engine/intelligence/behavior/ecs/SendEventBehavior.js +1 -1
- package/src/engine/intelligence/behavior/ecs/WaitForEventBehavior.d.ts +1 -1
- package/src/engine/intelligence/behavior/ecs/WaitForEventBehavior.js +1 -1
- package/src/engine/intelligence/behavior/primitive/ActionBehavior.d.ts +1 -1
- package/src/engine/intelligence/behavior/primitive/ActionBehavior.js +1 -1
- package/src/engine/intelligence/behavior/primitive/FailingBehavior.d.ts +1 -1
- package/src/engine/intelligence/behavior/primitive/FailingBehavior.js +1 -1
- package/src/engine/intelligence/behavior/primitive/PromiseBehavior.d.ts +1 -1
- package/src/engine/intelligence/behavior/primitive/PromiseBehavior.js +1 -1
- package/src/engine/intelligence/behavior/primitive/SucceedingBehavior.d.ts +1 -1
- package/src/engine/intelligence/behavior/primitive/SucceedingBehavior.js +1 -1
- package/src/engine/intelligence/behavior/selector/WeightedElement.d.ts +1 -1
- package/src/engine/intelligence/behavior/selector/WeightedElement.js +1 -1
- package/src/engine/intelligence/behavior/selector/WeightedRandomBehavior.d.ts +1 -1
- package/src/engine/intelligence/behavior/selector/WeightedRandomBehavior.js +1 -1
- package/src/engine/intelligence/behavior/util/BranchBehavior.d.ts +1 -1
- package/src/engine/intelligence/behavior/util/BranchBehavior.js +1 -1
- package/src/engine/intelligence/behavior/util/DelayBehavior.d.ts +1 -1
- package/src/engine/intelligence/behavior/util/DelayBehavior.js +1 -1
- package/src/engine/intelligence/behavior/util/LogMessageBehavior.d.ts +1 -1
- package/src/engine/intelligence/behavior/util/LogMessageBehavior.js +1 -1
- package/src/engine/intelligence/behavior/util/RandomDelayBehavior.d.ts +1 -1
- package/src/engine/intelligence/behavior/util/RandomDelayBehavior.js +1 -1
- package/src/engine/intelligence/blackboard/Blackboard.js +1 -1
- package/src/engine/intelligence/mcts/MonteCarlo.d.ts +1 -1
- package/src/engine/intelligence/mcts/MonteCarlo.js +1 -1
- package/src/engine/intelligence/mcts/MoveEdge.d.ts +1 -1
- package/src/engine/intelligence/mcts/MoveEdge.js +1 -1
- package/src/engine/intelligence/mcts/StateNode.d.ts +1 -1
- package/src/engine/intelligence/mcts/StateNode.js +1 -1
- package/src/engine/intelligence/resource/ActionSequence.d.ts +1 -1
- package/src/engine/intelligence/resource/ActionSequence.js +1 -1
- package/src/engine/intelligence/resource/Resource.d.ts +1 -1
- package/src/engine/intelligence/resource/Resource.js +1 -1
- package/src/engine/intelligence/resource/ResourceAllocation.d.ts +1 -1
- package/src/engine/intelligence/resource/ResourceAllocation.js +1 -1
- package/src/engine/intelligence/resource/ResourceAllocationBid.d.ts +1 -1
- package/src/engine/intelligence/resource/ResourceAllocationBid.js +1 -1
- package/src/engine/intelligence/resource/ResourceAllocationSolver.d.ts +1 -1
- package/src/engine/intelligence/resource/ResourceAllocationSolver.js +1 -1
- package/src/engine/intelligence/resource/StrategicResourceAllocator.d.ts +1 -1
- package/src/engine/intelligence/resource/StrategicResourceAllocator.js +1 -1
- package/src/engine/intelligence/resource/TacticalModule.d.ts +1 -1
- package/src/engine/intelligence/resource/TacticalModule.js +1 -1
- package/src/engine/network/NetworkSession.d.ts +1 -1
- package/src/engine/network/NetworkSession.js +1 -1
- package/src/engine/network/adapters/QuaternionInterpolationAdapter.d.ts +1 -1
- package/src/engine/network/adapters/QuaternionInterpolationAdapter.js +1 -1
- package/src/engine/network/adapters/TransformInterpolationAdapter.d.ts +1 -1
- package/src/engine/network/adapters/TransformInterpolationAdapter.js +1 -1
- package/src/engine/network/adapters/TransformReplicationAdapter.d.ts +1 -1
- package/src/engine/network/adapters/TransformReplicationAdapter.js +1 -1
- package/src/engine/network/adapters/Vector3InterpolationAdapter.d.ts +1 -1
- package/src/engine/network/adapters/Vector3InterpolationAdapter.js +1 -1
- package/src/engine/network/core/quantize/quantize_float.d.ts +1 -1
- package/src/engine/network/core/quantize/quantize_float.js +1 -1
- package/src/engine/network/core/quantize/quantize_position.d.ts +1 -1
- package/src/engine/network/core/quantize/quantize_position.js +1 -1
- package/src/engine/network/core/sequence/ack_bitfield.d.ts +1 -1
- package/src/engine/network/core/sequence/ack_bitfield.js +1 -1
- package/src/engine/network/core/sequence/seq16.d.ts +1 -1
- package/src/engine/network/core/sequence/seq16.js +1 -1
- package/src/engine/network/core/sequence/seq32.d.ts +1 -1
- package/src/engine/network/core/sequence/seq32.js +1 -1
- package/src/engine/network/diagnostics/BandwidthMeter.d.ts +1 -1
- package/src/engine/network/diagnostics/BandwidthMeter.js +1 -1
- package/src/engine/network/diagnostics/ReplayLog.d.ts +1 -1
- package/src/engine/network/diagnostics/ReplayLog.js +1 -1
- package/src/engine/network/diagnostics/SyncTest.d.ts +1 -1
- package/src/engine/network/diagnostics/SyncTest.js +1 -1
- package/src/engine/network/ecs/NetworkSystem.d.ts +1 -1
- package/src/engine/network/ecs/NetworkSystem.js +1 -1
- package/src/engine/network/ecs/components/NetworkIdentity.d.ts +1 -1
- package/src/engine/network/ecs/components/NetworkIdentity.js +1 -1
- package/src/engine/network/ecs/serialization/NetworkIdentitySerializationAdapter.d.ts +1 -1
- package/src/engine/network/ecs/serialization/NetworkIdentitySerializationAdapter.js +1 -1
- package/src/engine/network/orchestrator/NetworkPeer.d.ts +1 -1
- package/src/engine/network/orchestrator/NetworkPeer.js +1 -1
- package/src/engine/network/orchestrator/ServerAuthoritativeClient.d.ts +1 -1
- package/src/engine/network/orchestrator/ServerAuthoritativeClient.js +1 -1
- package/src/engine/network/orchestrator/ServerAuthoritativeServer.d.ts +1 -1
- package/src/engine/network/orchestrator/ServerAuthoritativeServer.js +1 -1
- package/src/engine/network/replication/Replicator.d.ts +1 -1
- package/src/engine/network/replication/Replicator.js +1 -1
- package/src/engine/network/replication/ScopeFilter.d.ts +2 -2
- package/src/engine/network/replication/ScopeFilter.js +2 -2
- package/src/engine/network/sim/ActionLog.d.ts +1 -1
- package/src/engine/network/sim/ActionLog.js +1 -1
- package/src/engine/network/sim/BinaryInterpolationAdapter.d.ts +1 -1
- package/src/engine/network/sim/BinaryInterpolationAdapter.js +1 -1
- package/src/engine/network/sim/InterpolationLog.d.ts +1 -1
- package/src/engine/network/sim/InterpolationLog.js +1 -1
- package/src/engine/network/sim/ReplicatedComponentRegistry.d.ts +1 -1
- package/src/engine/network/sim/ReplicatedComponentRegistry.js +1 -1
- package/src/engine/network/sim/RewindEngine.d.ts +1 -1
- package/src/engine/network/sim/RewindEngine.js +1 -1
- package/src/engine/network/sim/SimAction.d.ts +1 -1
- package/src/engine/network/sim/SimAction.js +1 -1
- package/src/engine/network/sim/SimActionExecutor.d.ts +1 -1
- package/src/engine/network/sim/SimActionExecutor.js +1 -1
- package/src/engine/network/sim/SimActionRegistry.d.ts +1 -1
- package/src/engine/network/sim/SimActionRegistry.js +1 -1
- package/src/engine/network/sim/SmoothingState.js +1 -1
- package/src/engine/network/sim/Snapshotter.d.ts +1 -1
- package/src/engine/network/sim/Snapshotter.js +1 -1
- package/src/engine/network/sim/SpeculationLog.d.ts +1 -1
- package/src/engine/network/sim/SpeculationLog.js +1 -1
- package/src/engine/network/state/Baseline.d.ts +1 -1
- package/src/engine/network/state/Baseline.js +1 -1
- package/src/engine/network/state/ChangedEntitySet.d.ts +1 -1
- package/src/engine/network/state/ChangedEntitySet.js +1 -1
- package/src/engine/network/state/InputRing.d.ts +1 -1
- package/src/engine/network/state/InputRing.js +1 -1
- package/src/engine/network/state/MutationLedger.d.ts +1 -1
- package/src/engine/network/state/MutationLedger.js +1 -1
- package/src/engine/network/state/PriorityAccumulator.d.ts +1 -1
- package/src/engine/network/state/PriorityAccumulator.js +1 -1
- package/src/engine/network/state/ReplicationSlotTable.d.ts +1 -1
- package/src/engine/network/state/ReplicationSlotTable.js +1 -1
- package/src/engine/network/time/AdaptiveRenderDelay.d.ts +1 -1
- package/src/engine/network/time/AdaptiveRenderDelay.js +1 -1
- package/src/engine/network/time/JitterBuffer.d.ts +1 -1
- package/src/engine/network/time/JitterBuffer.js +1 -1
- package/src/engine/network/time/TimeDilation.d.ts +1 -1
- package/src/engine/network/time/TimeDilation.js +1 -1
- package/src/engine/network/time/TimeSync.d.ts +1 -1
- package/src/engine/network/time/TimeSync.js +1 -1
- package/src/engine/network/transport/Channel.d.ts.map +1 -1
- package/src/engine/network/transport/Channel.js +3 -6
- package/src/engine/network/transport/LoopbackTransport.d.ts +1 -1
- package/src/engine/network/transport/LoopbackTransport.js +1 -1
- package/src/engine/network/transport/ReliableCommandPipeline.d.ts +1 -1
- package/src/engine/network/transport/ReliableCommandPipeline.js +1 -1
- package/src/engine/network/transport/Transport.d.ts +1 -1
- package/src/engine/network/transport/Transport.js +1 -1
- package/src/engine/network/transport/adapters/NodeUDPTransport.d.ts +1 -1
- package/src/engine/network/transport/adapters/NodeUDPTransport.js +2 -2
- package/src/engine/network/transport/adapters/SimulatedTransport.d.ts +1 -1
- package/src/engine/network/transport/adapters/SimulatedTransport.js +1 -1
- package/src/engine/network/transport/adapters/WebRTCDataChannelTransport.d.ts +1 -1
- package/src/engine/network/transport/adapters/WebRTCDataChannelTransport.js +1 -1
- package/src/engine/network/transport/adapters/WebSocketTransport.d.ts +1 -1
- package/src/engine/network/transport/adapters/WebSocketTransport.js +1 -1
- package/src/engine/network/transport/adapters/WebTransportTransport.d.ts +1 -1
- package/src/engine/network/transport/adapters/WebTransportTransport.js +1 -1
- package/src/engine/network/transport/fragments/FragmentAssembler.d.ts +1 -1
- package/src/engine/network/transport/fragments/FragmentAssembler.js +1 -1
- package/src/engine/network/transport/fragments/FragmentRetention.d.ts +1 -1
- package/src/engine/network/transport/fragments/FragmentRetention.js +1 -1
- package/src/engine/network/transport/fragments/packet_size.d.ts +1 -1
- package/src/engine/network/transport/fragments/packet_size.js +1 -1
- package/src/engine/simulation/Ticker.d.ts +1 -1
- package/src/engine/simulation/Ticker.js +1 -1
- package/src/engine/sound/SoundEngine.js +1 -1
- package/src/engine/ui/DraggableAspect.d.ts +1 -1
- package/src/engine/ui/DraggableAspect.js +1 -1
- package/src/view/View.js +1 -1
|
@@ -0,0 +1,799 @@
|
|
|
1
|
+
import Quaternion from "../../../core/geom/Quaternion.js";
|
|
2
|
+
import Vector3 from "../../../core/geom/Vector3.js";
|
|
3
|
+
import { clamp } from "../../../core/math/clamp.js";
|
|
4
|
+
import { DEG_TO_RAD } from "../../../core/math/DEG_TO_RAD.js";
|
|
5
|
+
import { lerp } from "../../../core/math/lerp.js";
|
|
6
|
+
import { ResourceAccessKind } from "../../../core/model/ResourceAccessKind.js";
|
|
7
|
+
import { ResourceAccessSpecification } from "../../../core/model/ResourceAccessSpecification.js";
|
|
8
|
+
import { SerializationMetadata } from "../../ecs/components/SerializationMetadata.js";
|
|
9
|
+
import Entity from "../../ecs/Entity.js";
|
|
10
|
+
import { System } from "../../ecs/System.js";
|
|
11
|
+
import { Transform } from "../../ecs/transform/Transform.js";
|
|
12
|
+
import { Camera } from "../../graphics/ecs/camera/Camera.js";
|
|
13
|
+
import { FirstPersonPlayerController } from "./FirstPersonPlayerController.js";
|
|
14
|
+
import { computeJumpFromApex } from "./math/computeJumpFromApex.js";
|
|
15
|
+
import { criticallyDampedSpringStep } from "./math/criticallyDampedSpring.js";
|
|
16
|
+
import { dampedSpringStep } from "./math/dampedSpringStep.js";
|
|
17
|
+
import { stepTowards } from "./math/stepTowards.js";
|
|
18
|
+
import { FirstPersonActionState } from "./pose/FirstPersonPose.js";
|
|
19
|
+
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Scratch allocations — reused per frame to avoid GC pressure
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
const SCRATCH_V3_A = new Vector3();
|
|
24
|
+
const SCRATCH_V3_B = new Vector3();
|
|
25
|
+
const SCRATCH_V3_C = new Vector3();
|
|
26
|
+
const SCRATCH_Q_A = new Quaternion();
|
|
27
|
+
const SCRATCH_Q_B = new Quaternion();
|
|
28
|
+
const SCRATCH_Q_C = new Quaternion();
|
|
29
|
+
|
|
30
|
+
const TWO_PI = Math.PI * 2;
|
|
31
|
+
const LN2 = Math.log(2);
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Per-entity runtime state the system maintains internally — too transient
|
|
35
|
+
* even for {@link FirstPersonPlayerController}'s `state` member, because it
|
|
36
|
+
* encodes input-edge bookkeeping and timer values the public surface should
|
|
37
|
+
* never see directly.
|
|
38
|
+
*/
|
|
39
|
+
class PerEntityRuntime {
|
|
40
|
+
constructor() {
|
|
41
|
+
/** Eye pitch in radians, clamped to config.look limits. */
|
|
42
|
+
this.eyePitch = 0;
|
|
43
|
+
/** Body yaw in radians (around world up). */
|
|
44
|
+
this.bodyYaw = 0;
|
|
45
|
+
|
|
46
|
+
/** Horizontal+vertical velocity. We integrate these inside the system
|
|
47
|
+
* when no external physics layer is attached. */
|
|
48
|
+
this.velocityX = 0;
|
|
49
|
+
this.velocityY = 0;
|
|
50
|
+
this.velocityZ = 0;
|
|
51
|
+
|
|
52
|
+
/** Previous-tick jump intent — for rising/falling edge detection. */
|
|
53
|
+
this.prevJumpHeld = false;
|
|
54
|
+
/** Previous-tick crouch intent — for toggle-mode edge detection. */
|
|
55
|
+
this.prevCrouchHeld = false;
|
|
56
|
+
/** True while crouch toggle is latched on (used only in toggle mode). */
|
|
57
|
+
this.crouchLatched = false;
|
|
58
|
+
|
|
59
|
+
/** Remaining time in jump anticipation, or <= 0 if not anticipating. */
|
|
60
|
+
this.anticipationRemaining = 0;
|
|
61
|
+
/** Cached derived gravity (m/s^2) from peakHeight + timeToApex. */
|
|
62
|
+
this.gravity = 9.81;
|
|
63
|
+
/** Cached derived jump impulse (m/s upward). */
|
|
64
|
+
this.jumpInitialVy = 5.0;
|
|
65
|
+
|
|
66
|
+
/** Spring state for landing dip (mutated in place). */
|
|
67
|
+
this.landSpring = { value: 0, velocity: 0 };
|
|
68
|
+
/** Spring state for FOV. */
|
|
69
|
+
this.fovSpring = { value: 70, velocity: 0 };
|
|
70
|
+
/** Spring state for eye height (crouch). */
|
|
71
|
+
this.eyeHeightSpring = { value: 1.80, velocity: 0 };
|
|
72
|
+
/** Spring state for lean roll (radians). */
|
|
73
|
+
this.leanSpring = { value: 0, velocity: 0 };
|
|
74
|
+
|
|
75
|
+
/** Previous horizontal velocity — for lateral acceleration → lean. */
|
|
76
|
+
this.prevVelocityX = 0;
|
|
77
|
+
this.prevVelocityZ = 0;
|
|
78
|
+
|
|
79
|
+
/** Previous-tick grounded for edge detection. */
|
|
80
|
+
this.prevGrounded = true;
|
|
81
|
+
/** Vertical speed at moment of last "leave ground". */
|
|
82
|
+
this.takeoffVy = 0;
|
|
83
|
+
/** Max vertical position since last takeoff — for jump apex detection. */
|
|
84
|
+
this.peakAltitude = 0;
|
|
85
|
+
/** Set true once a jump has been launched; cleared on land. */
|
|
86
|
+
this.midJump = false;
|
|
87
|
+
/** Apex already fired for this airborne segment? */
|
|
88
|
+
this.apexFired = false;
|
|
89
|
+
|
|
90
|
+
/** Stride phase from previous fixed step — for footstep edge detection. */
|
|
91
|
+
this.prevStridePhase = 0;
|
|
92
|
+
/** Breath phase from previous fixed step — for inhale/exhale edge detection. */
|
|
93
|
+
this.prevBreathPhase = 0;
|
|
94
|
+
/** Which foot fires next — flipped on each footstep signal. */
|
|
95
|
+
this.nextFootSide = "R";
|
|
96
|
+
|
|
97
|
+
/** Cached eye entity ID. -1 until link assigns it. */
|
|
98
|
+
this.eyeEntity = -1;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Drives a first-person camera + body from intent fields. See sibling
|
|
104
|
+
* DESIGN.md for goals, architecture, and the five processing layers (L0..L4).
|
|
105
|
+
*
|
|
106
|
+
* - fixedUpdate runs L1 (locomotion), L2 (pose state), and L4 (events) so
|
|
107
|
+
* the simulation remains deterministic.
|
|
108
|
+
* - update runs L3 (camera composition) at render rate so the eye is never
|
|
109
|
+
* smoother than the screen.
|
|
110
|
+
*
|
|
111
|
+
* The system itself integrates a simple flat-floor at y = `config.gravity.magnitude > 0
|
|
112
|
+
* ? state.groundY : -Infinity` for the prototype. A real physics layer should
|
|
113
|
+
* write `state.grounded`/`state.groundNormal` from outside instead; the
|
|
114
|
+
* built-in resolver is just a convenience to keep the controller usable
|
|
115
|
+
* without dependencies.
|
|
116
|
+
*
|
|
117
|
+
* @author Alex Goldring
|
|
118
|
+
* @copyright Company Named Limited (c) 2026
|
|
119
|
+
*/
|
|
120
|
+
export class FirstPersonPlayerControllerSystem extends System {
|
|
121
|
+
constructor() {
|
|
122
|
+
super();
|
|
123
|
+
|
|
124
|
+
this.dependencies = [FirstPersonPlayerController, Transform];
|
|
125
|
+
|
|
126
|
+
this.components_used = [
|
|
127
|
+
ResourceAccessSpecification.from(Transform, ResourceAccessKind.Write),
|
|
128
|
+
ResourceAccessSpecification.from(Camera, ResourceAccessKind.Write),
|
|
129
|
+
];
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Per-entity runtime, keyed by entity id.
|
|
133
|
+
* @type {Map<number, PerEntityRuntime>}
|
|
134
|
+
*/
|
|
135
|
+
this.runtime = new Map();
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* If true, the system clamps body y >= groundY and writes
|
|
139
|
+
* state.grounded itself. Turn off when wiring a real physics layer.
|
|
140
|
+
* @type {boolean}
|
|
141
|
+
*/
|
|
142
|
+
this.useBuiltInFlatGround = true;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* The flat-ground y for the built-in resolver. Ignored when
|
|
146
|
+
* useBuiltInFlatGround is false.
|
|
147
|
+
* @type {number}
|
|
148
|
+
*/
|
|
149
|
+
this.groundY = 0;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* @param {FirstPersonPlayerController} controller
|
|
154
|
+
* @param {Transform} bodyTransform
|
|
155
|
+
* @param {number} entity
|
|
156
|
+
*/
|
|
157
|
+
link(controller, bodyTransform, entity) {
|
|
158
|
+
const ecd = this.entityManager.dataset;
|
|
159
|
+
|
|
160
|
+
const runtime = new PerEntityRuntime();
|
|
161
|
+
this.runtime.set(entity, runtime);
|
|
162
|
+
|
|
163
|
+
// Derive gravity + jump impulse from designer-friendly params
|
|
164
|
+
const derived = { gravity: 0, initialVelocity: 0 };
|
|
165
|
+
computeJumpFromApex(controller.config.jump.peakHeight, controller.config.jump.timeToApex, derived);
|
|
166
|
+
runtime.gravity = derived.gravity;
|
|
167
|
+
runtime.jumpInitialVy = derived.initialVelocity;
|
|
168
|
+
|
|
169
|
+
// Seed yaw from the starting body rotation. `toEulerAnglesYXZ`
|
|
170
|
+
// returns (pitch, yaw, roll) — we only care about y.
|
|
171
|
+
bodyTransform.rotation.toEulerAnglesYXZ(SCRATCH_V3_A);
|
|
172
|
+
runtime.bodyYaw = SCRATCH_V3_A.y;
|
|
173
|
+
runtime.eyePitch = 0;
|
|
174
|
+
|
|
175
|
+
// Initialize springs to standing-eye-height baseline
|
|
176
|
+
runtime.eyeHeightSpring.value = controller.config.body.height;
|
|
177
|
+
runtime.fovSpring.value = controller.config.fov.base;
|
|
178
|
+
controller.state.eyeHeight = controller.config.body.height;
|
|
179
|
+
|
|
180
|
+
// Create eye entity if one wasn't supplied
|
|
181
|
+
if (controller.eyeEntity === -1 || !ecd.entityExists(controller.eyeEntity)) {
|
|
182
|
+
const eye = new Entity();
|
|
183
|
+
|
|
184
|
+
const eyeTransform = new Transform();
|
|
185
|
+
const baseEyePos = SCRATCH_V3_A.copy(bodyTransform.position);
|
|
186
|
+
baseEyePos.y += controller.config.body.height;
|
|
187
|
+
eyeTransform.position.copy(baseEyePos);
|
|
188
|
+
|
|
189
|
+
const camera = new Camera();
|
|
190
|
+
camera.active.set(true);
|
|
191
|
+
camera.fov.set(controller.config.fov.base);
|
|
192
|
+
camera.clip_near = 0.05;
|
|
193
|
+
camera.clip_far = 1000;
|
|
194
|
+
camera.autoClip = false;
|
|
195
|
+
|
|
196
|
+
eye.add(eyeTransform);
|
|
197
|
+
eye.add(camera);
|
|
198
|
+
eye.add(SerializationMetadata.Transient);
|
|
199
|
+
|
|
200
|
+
eye.build(ecd);
|
|
201
|
+
|
|
202
|
+
controller.eyeEntity = eye.id;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
runtime.eyeEntity = controller.eyeEntity;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* @param {FirstPersonPlayerController} controller
|
|
210
|
+
* @param {Transform} bodyTransform
|
|
211
|
+
* @param {number} entity
|
|
212
|
+
*/
|
|
213
|
+
unlink(controller, bodyTransform, entity) {
|
|
214
|
+
const ecd = this.entityManager.dataset;
|
|
215
|
+
|
|
216
|
+
if (controller.eyeEntity !== -1 && ecd.entityExists(controller.eyeEntity)) {
|
|
217
|
+
ecd.removeEntity(controller.eyeEntity);
|
|
218
|
+
controller.eyeEntity = -1;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
this.runtime.delete(entity);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Deterministic simulation step — L1 + L2 + L4.
|
|
226
|
+
* @param {number} dt
|
|
227
|
+
*/
|
|
228
|
+
fixedUpdate(dt) {
|
|
229
|
+
const ecd = this.entityManager.dataset;
|
|
230
|
+
if (ecd === null) return;
|
|
231
|
+
|
|
232
|
+
this._currentDt = dt;
|
|
233
|
+
ecd.traverseComponents(FirstPersonPlayerController, this._tickEntity, this);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Variable-rate camera composition — L3.
|
|
238
|
+
* @param {number} dt
|
|
239
|
+
*/
|
|
240
|
+
update(dt) {
|
|
241
|
+
const ecd = this.entityManager.dataset;
|
|
242
|
+
if (ecd === null) return;
|
|
243
|
+
|
|
244
|
+
this._currentRenderDt = dt;
|
|
245
|
+
ecd.traverseComponents(FirstPersonPlayerController, this._composeEye, this);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* @private
|
|
250
|
+
* @param {FirstPersonPlayerController} controller
|
|
251
|
+
* @param {number} entity
|
|
252
|
+
*/
|
|
253
|
+
_tickEntity(controller, entity) {
|
|
254
|
+
const ecd = this.entityManager.dataset;
|
|
255
|
+
const runtime = this.runtime.get(entity);
|
|
256
|
+
if (runtime === undefined) return;
|
|
257
|
+
|
|
258
|
+
const dt = this._currentDt;
|
|
259
|
+
const cfg = controller.config;
|
|
260
|
+
const intent = controller.intent;
|
|
261
|
+
const state = controller.state;
|
|
262
|
+
const sig = controller.signals;
|
|
263
|
+
|
|
264
|
+
const bodyTransform = ecd.getComponent(entity, Transform);
|
|
265
|
+
if (bodyTransform === undefined) return;
|
|
266
|
+
|
|
267
|
+
// -- L1.a: Consume look delta -----------------------------------
|
|
268
|
+
// intent.look is zeroed after consume so accumulated input doesn't
|
|
269
|
+
// re-apply on the next fixed step.
|
|
270
|
+
//
|
|
271
|
+
// Conventions (with raw mouse delta as the source — movementX/Y both
|
|
272
|
+
// positive when moving right/down):
|
|
273
|
+
// look.x > 0 ("mouse right") → turn right
|
|
274
|
+
// look.y > 0 ("mouse down") → look down (flipped by invertY)
|
|
275
|
+
//
|
|
276
|
+
// The yaw sign is negated because the engine uses left-handed
|
|
277
|
+
// coordinates with +Z as forward; a positive Y-axis rotation takes
|
|
278
|
+
// +Z toward +X, which presents to the player as a LEFT turn through
|
|
279
|
+
// the Three.js camera (`quaternion_invert_orientation`). Negating
|
|
280
|
+
// here gives the player-intuitive "mouse right → turn right".
|
|
281
|
+
const yawDelta = -intent.look.x;
|
|
282
|
+
const pitchSign = cfg.look.invertY ? -1 : 1;
|
|
283
|
+
const pitchDelta = intent.look.y * pitchSign;
|
|
284
|
+
intent.look.set(0, 0);
|
|
285
|
+
|
|
286
|
+
runtime.bodyYaw += yawDelta;
|
|
287
|
+
// keep yaw bounded (purely cosmetic — sin/cos handle wraparound fine)
|
|
288
|
+
if (runtime.bodyYaw > Math.PI) runtime.bodyYaw -= TWO_PI;
|
|
289
|
+
else if (runtime.bodyYaw < -Math.PI) runtime.bodyYaw += TWO_PI;
|
|
290
|
+
|
|
291
|
+
runtime.eyePitch = clamp(
|
|
292
|
+
runtime.eyePitch + pitchDelta,
|
|
293
|
+
cfg.look.pitchMinDeg * DEG_TO_RAD,
|
|
294
|
+
cfg.look.pitchMaxDeg * DEG_TO_RAD,
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
// Write body yaw back to transform (pure yaw, no pitch on body)
|
|
298
|
+
bodyTransform.rotation.fromAxisAngle(Vector3.up, runtime.bodyYaw);
|
|
299
|
+
|
|
300
|
+
// -- L1.b: Speed selection --------------------------------------
|
|
301
|
+
const isSprintIntent = intent.sprint && intent.move.y > 0.5 && state.grounded;
|
|
302
|
+
const isCrouchActive = this._resolveCrouchHeld(controller, runtime);
|
|
303
|
+
|
|
304
|
+
let targetSpeed;
|
|
305
|
+
if (isCrouchActive) {
|
|
306
|
+
targetSpeed = cfg.motion.crouchSpeed;
|
|
307
|
+
} else if (isSprintIntent) {
|
|
308
|
+
targetSpeed = cfg.motion.sprintSpeed;
|
|
309
|
+
} else {
|
|
310
|
+
targetSpeed = cfg.motion.walkSpeed;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// -- L1.c: Move intent → desired horizontal velocity -----------
|
|
314
|
+
//
|
|
315
|
+
// Player-perceived axes at yaw=0 (looking +Z through the engine's
|
|
316
|
+
// inverted-camera presentation):
|
|
317
|
+
// forward (W) → +Z velocity
|
|
318
|
+
// right (D) → -X velocity (camera presents -X as screen-right)
|
|
319
|
+
//
|
|
320
|
+
// In closed form for arbitrary yaw, screen-forward and screen-right
|
|
321
|
+
// are 90° apart with screen-right being LEFT of screen-forward
|
|
322
|
+
// (the inversion is purely a consequence of camera-side coordinate
|
|
323
|
+
// handling — non-camera entities use the usual right = +X).
|
|
324
|
+
//
|
|
325
|
+
// screen_forward(θ) = ( sin θ, 0, cos θ )
|
|
326
|
+
// screen_right (θ) = (-cos θ, 0, sin θ )
|
|
327
|
+
const sinYaw = Math.sin(runtime.bodyYaw);
|
|
328
|
+
const cosYaw = Math.cos(runtime.bodyYaw);
|
|
329
|
+
|
|
330
|
+
// Normalize the move vector (so diagonal isn't √2× faster)
|
|
331
|
+
const mvX = intent.move.x; // strafe (right+)
|
|
332
|
+
const mvY = intent.move.y; // forward (forward+)
|
|
333
|
+
const mvMag = Math.hypot(mvX, mvY);
|
|
334
|
+
const nmvX = mvMag > 1 ? mvX / mvMag : mvX;
|
|
335
|
+
const nmvY = mvMag > 1 ? mvY / mvMag : mvY;
|
|
336
|
+
|
|
337
|
+
const desiredVx = sinYaw * nmvY + -cosYaw * nmvX;
|
|
338
|
+
const desiredVz = cosYaw * nmvY + sinYaw * nmvX;
|
|
339
|
+
|
|
340
|
+
const desiredHorizontalVx = desiredVx * targetSpeed;
|
|
341
|
+
const desiredHorizontalVz = desiredVz * targetSpeed;
|
|
342
|
+
|
|
343
|
+
// -- L1.d: Apply accel/decel to horizontal velocity -------------
|
|
344
|
+
// Velocity lives on the runtime — we don't depend on Motion existing,
|
|
345
|
+
// because the system runs its own integrator when no external
|
|
346
|
+
// physics layer is attached.
|
|
347
|
+
const intentLen = Math.hypot(nmvX, nmvY);
|
|
348
|
+
let horizAccel;
|
|
349
|
+
if (!state.grounded) {
|
|
350
|
+
horizAccel = cfg.motion.airAccel;
|
|
351
|
+
} else if (intentLen < 1e-4) {
|
|
352
|
+
horizAccel = cfg.motion.groundDecel;
|
|
353
|
+
} else {
|
|
354
|
+
horizAccel = cfg.motion.groundAccel;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const maxStep = horizAccel * dt;
|
|
358
|
+
runtime.velocityX = stepTowards(runtime.velocityX, desiredHorizontalVx, maxStep);
|
|
359
|
+
runtime.velocityZ = stepTowards(runtime.velocityZ, desiredHorizontalVz, maxStep);
|
|
360
|
+
|
|
361
|
+
// -- L1.e: Jump (edge-triggered, buffered, coyote-graced) -------
|
|
362
|
+
const jumpPressedEdge = intent.jump && !runtime.prevJumpHeld;
|
|
363
|
+
const jumpReleasedEdge = !intent.jump && runtime.prevJumpHeld;
|
|
364
|
+
runtime.prevJumpHeld = intent.jump;
|
|
365
|
+
|
|
366
|
+
if (jumpPressedEdge) {
|
|
367
|
+
state.jumpBufferRemaining = cfg.jump.bufferTime;
|
|
368
|
+
}
|
|
369
|
+
state.jumpBufferRemaining = Math.max(0, state.jumpBufferRemaining - dt);
|
|
370
|
+
|
|
371
|
+
const canJumpNow =
|
|
372
|
+
(state.grounded || state.timeSinceGrounded < cfg.jump.coyoteTime)
|
|
373
|
+
&& state.jumpBufferRemaining > 0
|
|
374
|
+
&& !state.inJumpAnticipation
|
|
375
|
+
&& !runtime.midJump;
|
|
376
|
+
|
|
377
|
+
if (canJumpNow) {
|
|
378
|
+
// Begin anticipation — squash; impulse fires after duration elapses
|
|
379
|
+
state.inJumpAnticipation = true;
|
|
380
|
+
runtime.anticipationRemaining = cfg.jump.anticipation.duration;
|
|
381
|
+
state.jumpBufferRemaining = 0; // claimed
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Variable-height cut: only valid during ascent and once jump has launched
|
|
385
|
+
if (jumpReleasedEdge && runtime.midJump && runtime.velocityY > 0) {
|
|
386
|
+
state.isVariableJumpCut = true;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Anticipation timer; impulse on completion
|
|
390
|
+
if (state.inJumpAnticipation) {
|
|
391
|
+
// If the entity goes airborne mid-anticipation (ground rug-pulled),
|
|
392
|
+
// abandon the queued impulse — fire onLeaveGround{fall} instead.
|
|
393
|
+
if (!state.grounded) {
|
|
394
|
+
state.inJumpAnticipation = false;
|
|
395
|
+
runtime.anticipationRemaining = 0;
|
|
396
|
+
} else {
|
|
397
|
+
runtime.anticipationRemaining -= dt;
|
|
398
|
+
if (runtime.anticipationRemaining <= 0) {
|
|
399
|
+
runtime.velocityY = runtime.jumpInitialVy;
|
|
400
|
+
runtime.midJump = true;
|
|
401
|
+
runtime.apexFired = false;
|
|
402
|
+
runtime.peakAltitude = bodyTransform.position.y;
|
|
403
|
+
state.inJumpAnticipation = false;
|
|
404
|
+
state.isVariableJumpCut = false;
|
|
405
|
+
state.isAscending = true;
|
|
406
|
+
controller.state.exertion = clamp(controller.state.exertion + cfg.exertion.jumpRise, 0, 1);
|
|
407
|
+
|
|
408
|
+
sig.onJumpStart.send1({ peakHeight: cfg.jump.peakHeight });
|
|
409
|
+
sig.onLeaveGround.send1({ reason: "jump" });
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// -- L1.f: Gravity ---------------------------------------------
|
|
415
|
+
let gMag = runtime.gravity;
|
|
416
|
+
if (runtime.velocityY <= 0) {
|
|
417
|
+
gMag *= cfg.jump.fallGravityMult;
|
|
418
|
+
state.isAscending = false;
|
|
419
|
+
} else if (state.isVariableJumpCut) {
|
|
420
|
+
gMag *= cfg.jump.cutGravityMult;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
runtime.velocityY -= gMag * dt;
|
|
424
|
+
|
|
425
|
+
// -- L1.g: Integrate position ----------------------------------
|
|
426
|
+
bodyTransform.position._add(
|
|
427
|
+
runtime.velocityX * dt,
|
|
428
|
+
runtime.velocityY * dt,
|
|
429
|
+
runtime.velocityZ * dt,
|
|
430
|
+
);
|
|
431
|
+
|
|
432
|
+
// -- L1.h: Ground resolution (built-in flat floor) -------------
|
|
433
|
+
if (this.useBuiltInFlatGround) {
|
|
434
|
+
if (bodyTransform.position.y <= this.groundY) {
|
|
435
|
+
bodyTransform.position.setY(this.groundY);
|
|
436
|
+
|
|
437
|
+
if (!state.grounded) {
|
|
438
|
+
// Land
|
|
439
|
+
const impactVy = -runtime.velocityY; // positive magnitude
|
|
440
|
+
const kind = impactVy >= cfg.landing.hardThreshold ? "hard"
|
|
441
|
+
: (impactVy >= cfg.landing.softThreshold ? "soft" : "soft");
|
|
442
|
+
sig.onLand.send1({ verticalSpeed: impactVy, kind });
|
|
443
|
+
|
|
444
|
+
// Land dip — drives the under-damped spring downward
|
|
445
|
+
const dip = clamp(impactVy * cfg.landing.recovery.dipPerVy, 0, cfg.landing.recovery.dipMax);
|
|
446
|
+
runtime.landSpring.value = -dip; // start displaced
|
|
447
|
+
runtime.landSpring.velocity = 0;
|
|
448
|
+
|
|
449
|
+
runtime.midJump = false;
|
|
450
|
+
state.isAscending = false;
|
|
451
|
+
state.isVariableJumpCut = false;
|
|
452
|
+
state.fallDistance = 0;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
state.grounded = true;
|
|
456
|
+
state.verticalSpeed = 0;
|
|
457
|
+
runtime.velocityY = 0;
|
|
458
|
+
state.airborneTime = 0;
|
|
459
|
+
state.timeSinceGrounded = 0;
|
|
460
|
+
} else {
|
|
461
|
+
if (state.grounded) {
|
|
462
|
+
sig.onLeaveGround.send1({ reason: runtime.midJump ? "jump" : "fall" });
|
|
463
|
+
runtime.takeoffVy = runtime.velocityY;
|
|
464
|
+
runtime.peakAltitude = bodyTransform.position.y;
|
|
465
|
+
}
|
|
466
|
+
state.grounded = false;
|
|
467
|
+
state.verticalSpeed = runtime.velocityY;
|
|
468
|
+
state.airborneTime += dt;
|
|
469
|
+
state.timeSinceGrounded += dt;
|
|
470
|
+
state.fallDistance += Math.max(0, -runtime.velocityY * dt);
|
|
471
|
+
}
|
|
472
|
+
} else {
|
|
473
|
+
// External physics is expected to maintain state.grounded /
|
|
474
|
+
// state.verticalSpeed; we still track airborne timer.
|
|
475
|
+
if (state.grounded) {
|
|
476
|
+
state.timeSinceGrounded = 0;
|
|
477
|
+
state.airborneTime = 0;
|
|
478
|
+
} else {
|
|
479
|
+
state.timeSinceGrounded += dt;
|
|
480
|
+
state.airborneTime += dt;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// Detect jump apex
|
|
485
|
+
if (runtime.midJump && !runtime.apexFired) {
|
|
486
|
+
if (bodyTransform.position.y > runtime.peakAltitude) {
|
|
487
|
+
runtime.peakAltitude = bodyTransform.position.y;
|
|
488
|
+
} else if (runtime.velocityY <= 0) {
|
|
489
|
+
sig.onJumpApex.send0();
|
|
490
|
+
runtime.apexFired = true;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// -- L2.a: speed / moveMode ------------------------------------
|
|
495
|
+
const horizSpeed = Math.hypot(runtime.velocityX, runtime.velocityZ);
|
|
496
|
+
state.speed = horizSpeed;
|
|
497
|
+
state.speedNormalized = clamp(horizSpeed / Math.max(cfg.motion.sprintSpeed, 1e-3), 0, 1);
|
|
498
|
+
|
|
499
|
+
const prevMoveMode = state.moveMode;
|
|
500
|
+
if (!state.grounded) {
|
|
501
|
+
state.moveMode = "Air";
|
|
502
|
+
} else if (isCrouchActive) {
|
|
503
|
+
state.moveMode = "Crouch";
|
|
504
|
+
} else if (isSprintIntent && horizSpeed > 0.1) {
|
|
505
|
+
state.moveMode = "Sprint";
|
|
506
|
+
} else if (horizSpeed > 0.1) {
|
|
507
|
+
state.moveMode = "Walk";
|
|
508
|
+
} else {
|
|
509
|
+
state.moveMode = "Idle";
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
if (state.moveMode === "Sprint" && prevMoveMode !== "Sprint") {
|
|
513
|
+
sig.onSprintStart.send0();
|
|
514
|
+
} else if (prevMoveMode === "Sprint" && state.moveMode !== "Sprint") {
|
|
515
|
+
sig.onSprintStop.send0();
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// -- L2.b: Exertion --------------------------------------------
|
|
519
|
+
const exertionRise = isSprintIntent ? cfg.exertion.sprintRiseRate : 0;
|
|
520
|
+
const exertionFall = exertionRise > 0 ? 0 : cfg.exertion.idleDecayRate;
|
|
521
|
+
state.exertion = clamp(state.exertion + (exertionRise - exertionFall) * dt, 0, 1);
|
|
522
|
+
|
|
523
|
+
// -- L2.c: Breath ----------------------------------------------
|
|
524
|
+
// breathRate and breathAmplitude lag exertion through separate
|
|
525
|
+
// exponential decays. Rate hangs around longer than amplitude.
|
|
526
|
+
const targetRate = lerp(cfg.breath.rateRestHz, cfg.breath.rateMaxHz, state.exertion);
|
|
527
|
+
const targetAmp = lerp(cfg.breath.amplitudeRestM, cfg.breath.amplitudeMaxM, state.exertion);
|
|
528
|
+
state.breathRateHz = exponentialApproach(state.breathRateHz, targetRate, cfg.exertion.rateDecayHalfLife, dt);
|
|
529
|
+
state.breathAmplitudeM = exponentialApproach(state.breathAmplitudeM, targetAmp, cfg.exertion.ampDecayHalfLife, dt);
|
|
530
|
+
|
|
531
|
+
runtime.prevBreathPhase = state.breathPhase;
|
|
532
|
+
state.breathPhase += state.breathRateHz * dt;
|
|
533
|
+
state.breathPhase -= Math.floor(state.breathPhase); // wrap [0,1)
|
|
534
|
+
|
|
535
|
+
// Breath edge detection — inhale at 0.25, exhale at 0.75
|
|
536
|
+
if (phaseCrossed(runtime.prevBreathPhase, state.breathPhase, 0.25)) {
|
|
537
|
+
sig.onBreathIn.send1({ amplitude: state.breathAmplitudeM, rateHz: state.breathRateHz });
|
|
538
|
+
}
|
|
539
|
+
if (phaseCrossed(runtime.prevBreathPhase, state.breathPhase, 0.75)) {
|
|
540
|
+
sig.onBreathOut.send1({ amplitude: state.breathAmplitudeM, rateHz: state.breathRateHz });
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// -- L2.d: Stride ----------------------------------------------
|
|
544
|
+
runtime.prevStridePhase = state.stridePhase;
|
|
545
|
+
if (state.grounded && horizSpeed > cfg.bob.minStepSpeed) {
|
|
546
|
+
const freq = cfg.bob.stepFreqAtWalk
|
|
547
|
+
* Math.pow(Math.max(horizSpeed, 1e-3) / Math.max(cfg.motion.walkSpeed, 1e-3), cfg.bob.stepFreqExp);
|
|
548
|
+
// 1 full stride cycle = 2 footfalls; phase advances at freq/2 of cycle
|
|
549
|
+
state.stridePhase += (freq * 0.5) * dt;
|
|
550
|
+
state.stridePhase -= Math.floor(state.stridePhase);
|
|
551
|
+
}
|
|
552
|
+
// Footstep on phase wraparound past 0 (R) or past 0.5 (L)
|
|
553
|
+
if (state.grounded && horizSpeed > cfg.bob.minStepSpeed) {
|
|
554
|
+
if (phaseCrossed(runtime.prevStridePhase, state.stridePhase, 0)) {
|
|
555
|
+
state.stepCount++;
|
|
556
|
+
const side = runtime.nextFootSide === "L" ? "L" : "R";
|
|
557
|
+
runtime.nextFootSide = side === "R" ? "L" : "R";
|
|
558
|
+
sig.onFootStep.send1({ side, speed: horizSpeed, surfaceTag: state.surfaceTag });
|
|
559
|
+
}
|
|
560
|
+
if (phaseCrossed(runtime.prevStridePhase, state.stridePhase, 0.5)) {
|
|
561
|
+
state.stepCount++;
|
|
562
|
+
const side = runtime.nextFootSide === "L" ? "L" : "R";
|
|
563
|
+
runtime.nextFootSide = side === "R" ? "L" : "R";
|
|
564
|
+
sig.onFootStep.send1({ side, speed: horizSpeed, surfaceTag: state.surfaceTag });
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// -- L2.e: Crouch eye height -----------------------------------
|
|
569
|
+
const targetEyeH = isCrouchActive ? cfg.body.crouchHeight : cfg.body.height;
|
|
570
|
+
const crouchHalfLife = cfg.crouch.transitionTime / 4; // halfLife is ~quarter of full transition
|
|
571
|
+
criticallyDampedSpringStep(runtime.eyeHeightSpring, targetEyeH, crouchHalfLife, dt);
|
|
572
|
+
state.eyeHeight = runtime.eyeHeightSpring.value;
|
|
573
|
+
|
|
574
|
+
if (isCrouchActive !== state.crouchActive) {
|
|
575
|
+
state.crouchActive = isCrouchActive;
|
|
576
|
+
if (isCrouchActive) sig.onCrouchEnter.send0();
|
|
577
|
+
else sig.onCrouchExit.send0();
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// -- L2.f: Lean (lateral acceleration → roll) ------------------
|
|
581
|
+
let leanTargetRad = 0;
|
|
582
|
+
if (cfg.lean.enabled) {
|
|
583
|
+
// Lateral acceleration projected onto screen-right.
|
|
584
|
+
// accel_world = (vel - prevVel) / dt; screen_right = (-cos θ, 0, sin θ).
|
|
585
|
+
const accWorldX = (runtime.velocityX - runtime.prevVelocityX) / Math.max(dt, 1e-4);
|
|
586
|
+
const accWorldZ = (runtime.velocityZ - runtime.prevVelocityZ) / Math.max(dt, 1e-4);
|
|
587
|
+
const latAccel = accWorldX * (-cosYaw) + accWorldZ * sinYaw;
|
|
588
|
+
const normalized = clamp(latAccel / 9.81, -2, 2);
|
|
589
|
+
// Negative roll on rightward accel = leaning into the turn
|
|
590
|
+
leanTargetRad = -normalized * cfg.lean.maxRollDeg * DEG_TO_RAD;
|
|
591
|
+
}
|
|
592
|
+
runtime.prevVelocityX = runtime.velocityX;
|
|
593
|
+
runtime.prevVelocityZ = runtime.velocityZ;
|
|
594
|
+
criticallyDampedSpringStep(runtime.leanSpring, leanTargetRad, cfg.lean.spring.halfLife, dt);
|
|
595
|
+
state.leanRollRad = runtime.leanSpring.value;
|
|
596
|
+
|
|
597
|
+
// -- L2.g: Land spring decay (drives the landing recovery dip) -
|
|
598
|
+
// Target is 0; under-damped so it rings.
|
|
599
|
+
dampedSpringStep(
|
|
600
|
+
runtime.landSpring, 0,
|
|
601
|
+
cfg.landing.recovery.spring.halfLife,
|
|
602
|
+
cfg.landing.recovery.spring.zeta,
|
|
603
|
+
dt,
|
|
604
|
+
);
|
|
605
|
+
|
|
606
|
+
// -- L2.h: Publish pose channels --------------------------------
|
|
607
|
+
const pose = controller.pose;
|
|
608
|
+
pose.rootPosition.copy(bodyTransform.position);
|
|
609
|
+
pose.rootYawRad = runtime.bodyYaw;
|
|
610
|
+
pose.headYawRad = runtime.bodyYaw;
|
|
611
|
+
pose.headPitchRad = runtime.eyePitch;
|
|
612
|
+
pose.headRollRad = state.leanRollRad;
|
|
613
|
+
pose.locomotionPhase = state.stridePhase;
|
|
614
|
+
pose.locomotionSpeed = horizSpeed;
|
|
615
|
+
// Strafe component: project velocity onto screen-right (-cos θ, 0, sin θ).
|
|
616
|
+
// Positive = moving to the player's right (animation blend-space convention).
|
|
617
|
+
pose.locomotionStrafe = (runtime.velocityX * (-cosYaw) + runtime.velocityZ * sinYaw)
|
|
618
|
+
/ Math.max(cfg.motion.sprintSpeed, 1e-3);
|
|
619
|
+
pose.actionState =
|
|
620
|
+
state.inJumpAnticipation ? FirstPersonActionState.Anticipating
|
|
621
|
+
: !state.grounded ? FirstPersonActionState.Airborne
|
|
622
|
+
: (Math.abs(runtime.landSpring.value) > 0.01 ? FirstPersonActionState.Landing
|
|
623
|
+
: FirstPersonActionState.Grounded);
|
|
624
|
+
const crouchSpan = Math.max(cfg.body.height - cfg.body.crouchHeight, 1e-3);
|
|
625
|
+
pose.crouchAmount = clamp((cfg.body.height - state.eyeHeight) / crouchSpan, 0, 1);
|
|
626
|
+
pose.aimPitch = runtime.eyePitch;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* @private
|
|
631
|
+
* @param {FirstPersonPlayerController} controller
|
|
632
|
+
* @param {PerEntityRuntime} runtime
|
|
633
|
+
* @returns {boolean}
|
|
634
|
+
*/
|
|
635
|
+
_resolveCrouchHeld(controller, runtime) {
|
|
636
|
+
const cfg = controller.config;
|
|
637
|
+
const intent = controller.intent;
|
|
638
|
+
|
|
639
|
+
if (cfg.crouch.mode === "toggle") {
|
|
640
|
+
// Edge: rising press flips the latch
|
|
641
|
+
if (intent.crouch && !runtime.prevCrouchHeld) {
|
|
642
|
+
runtime.crouchLatched = !runtime.crouchLatched;
|
|
643
|
+
}
|
|
644
|
+
runtime.prevCrouchHeld = intent.crouch;
|
|
645
|
+
return runtime.crouchLatched;
|
|
646
|
+
}
|
|
647
|
+
// "hold" mode
|
|
648
|
+
runtime.prevCrouchHeld = intent.crouch;
|
|
649
|
+
return intent.crouch;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Compose the eye transform from body + state-driven offsets.
|
|
654
|
+
* @private
|
|
655
|
+
* @param {FirstPersonPlayerController} controller
|
|
656
|
+
* @param {number} entity
|
|
657
|
+
*/
|
|
658
|
+
_composeEye(controller, entity) {
|
|
659
|
+
const ecd = this.entityManager.dataset;
|
|
660
|
+
const runtime = this.runtime.get(entity);
|
|
661
|
+
if (runtime === undefined) return;
|
|
662
|
+
|
|
663
|
+
const dt = this._currentRenderDt;
|
|
664
|
+
const cfg = controller.config;
|
|
665
|
+
const state = controller.state;
|
|
666
|
+
|
|
667
|
+
const bodyTransform = ecd.getComponent(entity, Transform);
|
|
668
|
+
if (bodyTransform === undefined) return;
|
|
669
|
+
|
|
670
|
+
if (controller.eyeEntity === -1) return;
|
|
671
|
+
const eyeTransform = ecd.getComponent(controller.eyeEntity, Transform);
|
|
672
|
+
const camera = ecd.getComponent(controller.eyeEntity, Camera);
|
|
673
|
+
if (eyeTransform === undefined || camera === undefined) return;
|
|
674
|
+
|
|
675
|
+
// -- Body-local eye offset --------------------------------------
|
|
676
|
+
const eyeLocal = SCRATCH_V3_A.set(0, state.eyeHeight, 0);
|
|
677
|
+
|
|
678
|
+
// Bob — only when grounded & moving
|
|
679
|
+
if (state.grounded && state.speed > cfg.bob.minStepSpeed) {
|
|
680
|
+
const phase = state.stridePhase * TWO_PI; // one full cycle = 2 steps
|
|
681
|
+
const massBoost = (cfg.body.mass - 80) * cfg.bob.ampMassScale;
|
|
682
|
+
const ampV = (cfg.bob.verticalAmpAtWalk + massBoost) * state.speedNormalized;
|
|
683
|
+
const ampL = (cfg.bob.lateralAmpAtWalk + massBoost) * state.speedNormalized;
|
|
684
|
+
|
|
685
|
+
eyeLocal.y += -ampV * Math.abs(Math.sin(phase));
|
|
686
|
+
eyeLocal.x += ampL * Math.sin(phase * 0.5);
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// Breath — sine + tiny noise
|
|
690
|
+
const breathOffset = -state.breathAmplitudeM
|
|
691
|
+
* Math.sin(state.breathPhase * TWO_PI)
|
|
692
|
+
* (1 + cfg.breath.noiseAmount * (Math.sin(state.breathPhase * 13.7) * 0.5));
|
|
693
|
+
eyeLocal.y += breathOffset;
|
|
694
|
+
|
|
695
|
+
// Landing spring dip
|
|
696
|
+
eyeLocal.y += runtime.landSpring.value;
|
|
697
|
+
|
|
698
|
+
// Jump anticipation dip (linear ramp during anticipation)
|
|
699
|
+
if (state.inJumpAnticipation) {
|
|
700
|
+
const t = 1 - clamp(runtime.anticipationRemaining / Math.max(cfg.jump.anticipation.duration, 1e-3), 0, 1);
|
|
701
|
+
// Ease-out: t * (2 - t)
|
|
702
|
+
const eased = t * (2 - t);
|
|
703
|
+
eyeLocal.y -= cfg.jump.anticipation.dipAmount * eased;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// Transform body-local offset into world space (body has yaw only,
|
|
707
|
+
// so rotate by bodyTransform.rotation around Y)
|
|
708
|
+
const worldOffset = SCRATCH_V3_B.copy(eyeLocal);
|
|
709
|
+
worldOffset.applyQuaternion(bodyTransform.rotation);
|
|
710
|
+
|
|
711
|
+
eyeTransform.position.copy(bodyTransform.position);
|
|
712
|
+
eyeTransform.position._add(worldOffset.x, worldOffset.y, worldOffset.z);
|
|
713
|
+
|
|
714
|
+
// -- Eye rotation: body yaw × eye pitch × roll -------------------
|
|
715
|
+
// Bob roll mixes in for a subtle head sway (in phase with lateral bob).
|
|
716
|
+
// Breath pitch is a small extra nod 90° out of phase with vertical
|
|
717
|
+
// breath; merged into the main pitch so we don't pay an extra quat
|
|
718
|
+
// multiply and the composition stays trivially correct.
|
|
719
|
+
let rollTotal = state.leanRollRad;
|
|
720
|
+
if (state.grounded && state.speed > cfg.bob.minStepSpeed) {
|
|
721
|
+
const phase = state.stridePhase * TWO_PI;
|
|
722
|
+
const ampRoll = cfg.bob.rollAtWalkDeg * DEG_TO_RAD * state.speedNormalized;
|
|
723
|
+
rollTotal += ampRoll * Math.sin(phase * 0.5);
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
const breathPitch = lerp(cfg.breath.pitchAmpRestDeg, cfg.breath.pitchAmpMaxDeg, state.exertion)
|
|
727
|
+
* DEG_TO_RAD
|
|
728
|
+
* Math.cos(state.breathPhase * TWO_PI);
|
|
729
|
+
const pitchTotal = runtime.eyePitch + breathPitch;
|
|
730
|
+
|
|
731
|
+
// composition: yaw * pitch * roll
|
|
732
|
+
// pitch around world X — yaw applied after, so effective axis is camera-local right
|
|
733
|
+
// roll around world Z — yaw and pitch applied after, so effective axis is camera-local forward
|
|
734
|
+
const qYaw = SCRATCH_Q_A.fromAxisAngle(Vector3.up, runtime.bodyYaw);
|
|
735
|
+
const qPitch = SCRATCH_Q_B.fromAxisAngle(Vector3.right, pitchTotal);
|
|
736
|
+
const qRoll = SCRATCH_Q_C.fromAxisAngle(Vector3.forward, rollTotal);
|
|
737
|
+
|
|
738
|
+
eyeTransform.rotation.multiplyQuaternions(qYaw, qPitch);
|
|
739
|
+
eyeTransform.rotation.multiply(qRoll);
|
|
740
|
+
|
|
741
|
+
// -- FOV ---------------------------------------------------------
|
|
742
|
+
let fovTarget = cfg.fov.base;
|
|
743
|
+
if (cfg.fov.sprintAdd !== 0) {
|
|
744
|
+
// Add proportionally — at sprint-speed cap we hit sprintAdd
|
|
745
|
+
const sprintness = clamp((state.speed - cfg.motion.walkSpeed)
|
|
746
|
+
/ Math.max(cfg.motion.sprintSpeed - cfg.motion.walkSpeed, 1e-3), 0, 1);
|
|
747
|
+
fovTarget += cfg.fov.sprintAdd * sprintness;
|
|
748
|
+
}
|
|
749
|
+
if (state.crouchActive) fovTarget += cfg.fov.crouchAdd;
|
|
750
|
+
|
|
751
|
+
criticallyDampedSpringStep(runtime.fovSpring, fovTarget, cfg.fov.smoothHalfLife, dt);
|
|
752
|
+
// Write directly to the underlying Three.js camera. Going through
|
|
753
|
+
// camera.fov.set() fires onChanged which triggers a full camera
|
|
754
|
+
// rebuild in CameraSystem — far too expensive to do per frame.
|
|
755
|
+
// The CameraSystem's visibility-construction hook calls
|
|
756
|
+
// updateProjectionMatrix() each frame anyway.
|
|
757
|
+
if (camera.object !== null) {
|
|
758
|
+
camera.object.fov = runtime.fovSpring.value;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
// ---------------------------------------------------------------------------
|
|
764
|
+
// helpers
|
|
765
|
+
// ---------------------------------------------------------------------------
|
|
766
|
+
|
|
767
|
+
/**
|
|
768
|
+
* Exponential approach with half-life parameterization.
|
|
769
|
+
* @param {number} current
|
|
770
|
+
* @param {number} target
|
|
771
|
+
* @param {number} halfLife
|
|
772
|
+
* @param {number} dt
|
|
773
|
+
* @returns {number}
|
|
774
|
+
*/
|
|
775
|
+
function exponentialApproach(current, target, halfLife, dt) {
|
|
776
|
+
if (halfLife <= 0) return target;
|
|
777
|
+
const alpha = 1 - Math.exp(-LN2 * dt / halfLife);
|
|
778
|
+
return current + (target - current) * alpha;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
/**
|
|
782
|
+
* Detect that phase value crossed a boundary in [0,1) between two ticks.
|
|
783
|
+
* Handles the wraparound case where phase jumps from e.g. 0.95 to 0.05.
|
|
784
|
+
*
|
|
785
|
+
* @param {number} prev previous phase in [0,1)
|
|
786
|
+
* @param {number} next current phase in [0,1)
|
|
787
|
+
* @param {number} boundary in [0,1)
|
|
788
|
+
* @returns {boolean}
|
|
789
|
+
*/
|
|
790
|
+
function phaseCrossed(prev, next, boundary) {
|
|
791
|
+
if (next >= prev) {
|
|
792
|
+
// no wrap
|
|
793
|
+
return prev < boundary && next >= boundary;
|
|
794
|
+
} else {
|
|
795
|
+
// wrapped past 1.0
|
|
796
|
+
return prev < boundary || next >= boundary;
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
|