Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/player physics and camera collisions #162

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"publish:mainnet": "git tag mainnet-$(node -p -e \"require('./package.json').version\") && git push origin mainnet-$(node -p -e \"require('./package.json').version\")"
},
"dependencies": {
"@aresrpg/aresrpg-engine": "2.3.1",
"@aresrpg/aresrpg-engine": "^2.6.1",
"@aresrpg/aresrpg-protocol": "^5.0.1",
"@aresrpg/aresrpg-sdk": "4.1.7",
"@aresrpg/aresrpg-world": "1.3.9",
Expand Down
14 changes: 14 additions & 0 deletions src/core/game/game.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import CameraControls from 'camera-controls'
import { create_client } from '@aresrpg/aresrpg-protocol'
import { useWebSocket } from '@vueuse/core'
import { ref, watch } from 'vue'
import { VoxelmapCollider, VoxelmapCollisions } from '@aresrpg/aresrpg-engine'

import { combine } from '../utils/iterator.js'
import ui_fps from '../modules/ui_fps.js'
Expand Down Expand Up @@ -66,6 +67,7 @@ import { i18n } from '../../i18n.js'
import game_entites_stroll from '../modules/game_entites_stroll.js'
import player_entities_interract from '../modules/player_entities_interract.js'
import game_fights from '../modules/game_fights.js'
import { world_patch_size } from '../utils/terrain/world_settings.js'

import { handle_server_error, notify_reconnected } from './error_handler.js'
import { get_spells } from './spells_per_class.js'
Expand Down Expand Up @@ -449,6 +451,14 @@ function connect_ws() {
})
}

const voxelmap_collider = new VoxelmapCollider({
chunkSize: { x: world_patch_size, y: world_patch_size, z: world_patch_size },
voxelsChunkOrdering: 'zyx',
})
const voxelmap_collisions = new VoxelmapCollisions({
voxelmapCollider: voxelmap_collider,
})

const context = {
events,
actions,
Expand Down Expand Up @@ -505,6 +515,10 @@ const context = {
// context.camera_controls.maxAzimuthAngle = Infinity // Allow full horizontal rotation
// context.camera_controls.minAzimuthAngle = -Infinity // Allow full horizontal rotation
},
physics: {
voxelmap_collider,
voxelmap_collisions,
},
}

const modules = MODULES.map(create => create())
Expand Down
87 changes: 68 additions & 19 deletions src/core/modules/game_camera.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { on } from 'events'
import { aiter } from 'iterator-helper'
import CameraControls from 'camera-controls'
import { clamp, smootherstep } from 'three/src/math/MathUtils.js'
import { Vector3 } from 'three'

import { abortable, state_iterator } from '../utils/iterator.js'
import { context, current_three_character } from '../game/game.js'
Expand All @@ -13,34 +14,84 @@ import { is_hovering_mob_group } from './player_entities_interract.js'
const CAMERA_MIN_DISTANCE = 0.001
const CAMERA_DISTANCE_STEP = 1
const CAMERA_MAX_DISTANCE = 50
const CAMERA_DISTANCE_MAX_SPEED = 15
const ENABLE_CAMERA_COLLISIONS = true

/** @type {Type.Module} */
export default function () {
let wanted_distance = 0

return {
tick(state, { camera_controls, dispatch, camera }, delta) {
tick(state, { camera_controls, dispatch, camera, physics }, delta) {
const player = current_three_character(state)
if (!player?.position) return

const camera_state = state.settings.camera

if (!camera_state.is_free) {
if (camera_state.is_free) {
camera_controls.update(delta)
} else {
const { x, y, z } = player.position

// Set the perspective camera position to follow the player
const center_camera_on_head =
1 - smootherstep(camera_controls.distance, 0, 10)
const head_height = 1
const y_shift = state.current_fight
? -5
: head_height * center_camera_on_head
camera_controls.moveTo(x, y + y_shift, z, true)
camera_controls.setTarget(x, y + y_shift, z, true)
const head_height = player.height
const y_shift = state.current_fight ? -5 : head_height
const camera_target = new Vector3(x, y + y_shift, z)
camera_controls.moveTo(
camera_target.x,
camera_target.y,
camera_target.z,
true,
)
camera_controls.setTarget(
camera_target.x,
camera_target.y,
camera_target.z,
true,
)

if (typeof player.object3d !== 'undefined') {
player.object3d.visible = camera_controls.distance > 0.75
}

camera_controls.update(delta)

const camera_distance_speed =
delta *
CAMERA_DISTANCE_MAX_SPEED *
Math.min(3, Math.abs(wanted_distance - camera_controls.distance))
if (camera_controls.distance < wanted_distance) {
camera_controls.distance = Math.min(
wanted_distance,
camera_controls.distance + camera_distance_speed,
)
} else if (camera_controls.distance > wanted_distance) {
camera_controls.distance = Math.max(
wanted_distance,
camera_controls.distance - camera_distance_speed,
)
}

if (ENABLE_CAMERA_COLLISIONS) {
const epsilon = 1e-2
const potential_position = camera_target
.clone()
.addScaledVector(
new Vector3()
.subVectors(camera.position, camera_target)
.normalize(),
CAMERA_MAX_DISTANCE,
)
const intersection = physics.voxelmap_collisions.rayCast(
camera_target,
potential_position,
)
const intersection_distance = intersection?.distance ?? Infinity
if (camera_controls.distance > intersection_distance - epsilon) {
camera_controls.distance = intersection_distance - epsilon
}
}
}
camera_controls.update(delta)

const is_underwater = camera.position.y <= sea_level
if (camera_state.is_underwater !== is_underwater) {
Expand Down Expand Up @@ -73,12 +124,13 @@ export default function () {
camera_controls.dollyTo(8)
camera_controls.rotate(0, 1)

wanted_distance = camera_controls.distance

// let is_dragging = false

const set_distance = distance => {
distance = clamp(distance, CAMERA_MIN_DISTANCE, CAMERA_MAX_DISTANCE)
distance = Math.round(distance)
camera_controls.dollyTo(distance, true)
wanted_distance = Math.round(distance)
}

const on_mouse_down = () => {
Expand All @@ -88,12 +140,9 @@ export default function () {
}

const on_mouse_wheel = event => {
const delta_abs = Math.max(
CAMERA_DISTANCE_STEP,
0.35 * camera_controls.distance,
)
const delta_abs = Math.max(CAMERA_DISTANCE_STEP, 0.35 * wanted_distance)
const delta = delta_abs * Math.sign(event.deltaY)
set_distance(camera_controls.distance + delta)
set_distance(wanted_distance + delta)
}

renderer.domElement.addEventListener('mousedown', on_mouse_down, {
Expand Down Expand Up @@ -133,7 +182,7 @@ export default function () {
renderer.domElement.addEventListener('wheel', on_mouse_wheel, {
signal,
})
set_distance(camera_controls.distance)
set_distance(wanted_distance)
// @ts-ignore
camera_controls.mouseButtons.right = CameraControls.ACTION.ROTATE
// @ts-ignore
Expand Down
22 changes: 16 additions & 6 deletions src/core/modules/game_terrain.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { setInterval } from 'timers/promises'

import {
EComputationMethod,
HeightmapViewer,
TerrainViewer,
VoxelmapViewer,
VoxelmapVisibilityComputer,
Expand Down Expand Up @@ -147,7 +148,7 @@ export default function () {
},
}

const patch_size = { xz: 64, y: 64 }
const patch_size = { xz: world_patch_size, y: world_patch_size }
const min_patch_id_y = Math.floor(min_altitude / patch_size.y)
const max_patch_id_y = Math.floor(max_altitude / patch_size.y)
const voxelmap_viewer = new VoxelmapViewer(
Expand All @@ -162,7 +163,12 @@ export default function () {
},
},
)
const terrain_viewer = new TerrainViewer(map, voxelmap_viewer)
const heightmap_viewer = new HeightmapViewer(map, {
basePatchSize: patch_size.xz,
voxelRatio: 2,
maxLevel: 5,
})
const terrain_viewer = new TerrainViewer(heightmap_viewer, voxelmap_viewer)
terrain_viewer.parameters.lod.enabled = true

const voxelmap_visibility_computer = new VoxelmapVisibilityComputer(
Expand Down Expand Up @@ -229,10 +235,8 @@ export default function () {
tick() {
terrain_viewer.update()
},
observe({ camera, events, signal, scene, get_state }) {
observe({ camera, events, signal, scene, get_state, physics }) {
window.dispatchEvent(new Event('assets_loading'))
// this notify the player_movement module that the terrain is ready
events.emit('CHUNKS_LOADED')

scene.add(terrain_viewer.container)

Expand Down Expand Up @@ -334,7 +338,13 @@ export default function () {
const data = fill_chunk_from_patch(cached_patch, chunk_bbox)
const size = Math.round(Math.pow(data.length, 1 / 3))
const dimensions = new Vector3(size, size, size)
const chunk = { data, size: dimensions, isEmpty: false }
const chunk = {
data,
size: dimensions,
isEmpty: false,
dataOrdering: 'zyx',
}
physics.voxelmap_collider.setChunk(patch_id, chunk)
// feed engine with chunk
voxelmap_viewer.enqueuePatch(patch_id, chunk)
// query the map (asynchronous) to get data for this patch
Expand Down
Loading