update client to threejs v167 modules and add basic visuals for volumetric smokes

This commit is contained in:
Andy Kernel 2024-08-26 19:47:23 +02:00 committed by solcloud
parent 2250a3d4b4
commit f9aa6625f9
14 changed files with 221 additions and 110 deletions

View File

@ -33,6 +33,7 @@ const EventList = {
ThrowEvent: 11,
DropEvent: 12,
GrillEvent: 13,
SmokeEvent: 14,
}
// server/src/Enum/GameOverReason.php
@ -106,6 +107,8 @@ const SoundType = {
ITEM_DROP_LAND: 21,
FLAME_EXTINGUISH: 22,
FLAME_PLAYER_HIT: 23,
SMOKE_SPAWN: 24,
SMOKE_FADE: 25,
}
// server/src/Enum/ArmorType.php

View File

@ -109,7 +109,11 @@ export class EventProcessor {
}
eventsCallback[EventList.GrillEvent] = function (data) {
game.grillStart(data.position, data.maxTime, data.maxFlames)
game.grillStart(data.id, data.position, data.size, data.time, data.count)
}
eventsCallback[EventList.SmokeEvent] = function (data) {
game.smokeStart(data.id, data.position, data.size, data.time, data.count)
}
this.#callbacks = eventsCallback

View File

@ -1,7 +1,9 @@
import {EventProcessor} from "./EventProcessor.js";
import {Player} from "./Player.js";
import {InventorySlot, SoundType} from "./Enums.js";
import {SoundRepository} from "./SoundRepository.js";
import * as THREE from 'three' // fixme try remove from game, maybe only allow import Vec3...
import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js' // todo remove
import {EventProcessor} from "./EventProcessor.js"
import {Player} from "./Player.js"
import {InventorySlot, SoundType} from "./Enums.js"
import {SoundRepository} from "./SoundRepository.js"
export class Game {
#world
@ -21,13 +23,13 @@ export class Game {
#endCallback
#soundRepository
#hudDebounceTicks = 1
#bombTimerId = null;
#bombTimerId = null
#eventProcessor
#dropItems = {};
#throwables = {};
#flammable = {};
#roundIntervalIds = [];
#roundDamage = {did: {},got: {}}
#dropItems = {}
#throwables = {}
#volumetrics = {}
#roundIntervalIds = []
#roundDamage = {did: {}, got: {}}
#playerSlotsVisibleModels = [
InventorySlot.SLOT_KNIFE, InventorySlot.SLOT_PRIMARY, InventorySlot.SLOT_SECONDARY,
InventorySlot.SLOT_BOMB, InventorySlot.SLOT_GRENADE_DECOY, InventorySlot.SLOT_GRENADE_MOLOTOV,
@ -56,7 +58,8 @@ export class Game {
this.#roundIntervalIds = []
Object.keys(this.#dropItems).forEach((id) => this.itemPickUp(id))
Object.keys(this.#throwables).forEach((id) => this.removeGrenade(id))
Object.keys(this.#flammable).forEach((fireId) => Object.keys(this.#flammable[fireId]).forEach((flameId) => this.destroyFlame(fireId, flameId)))
Object.keys(this.#volumetrics).forEach((groupId) => Object.keys(this.#volumetrics[groupId]).forEach((itemId) => this.#world.destroyObject(this.#volumetrics[groupId][itemId])))
this.#volumetrics = {}
this.#world.reset()
}
@ -97,7 +100,7 @@ export class Game {
this.#paused = false
this.#hud.clearTopMessage()
this.#hud.updateRoundDamage(null)
this.#roundDamage = {did: {},got: {}}
this.#roundDamage = {did: {}, got: {}}
console.log("Game unpause")
}
@ -120,7 +123,7 @@ export class Game {
roundEnd(attackersWins, newRoundNumber, score) {
let winner = attackersWins ? 'Attackers' : 'Defenders'
console.log("Round " + this.#round + " ended. Round wins: " + winner)
this.score = score;
this.score = score
this.#round = newRoundNumber
this.#hud.displayTopMessage(winner + ' wins')
this.#hud.requestFullScoreBoardUpdate(this.score)
@ -200,13 +203,19 @@ export class Game {
this.itemPickUp(data.extra.id)
}
if (data.type === SoundType.FLAME_SPAWN) {
this.spawnFlame(data.position, data.extra.size, data.extra.height, data.extra.fire, `${data.position.x}-${data.position.y}-${data.position.z}`)
this.spawnFlame(data.position, data.extra.height, data.extra.id, `${data.position.x}-${data.position.y}-${data.position.z}`)
}
if (data.type === SoundType.SMOKE_SPAWN) {
this.spawnSmoke(data.position, data.extra.height, data.extra.id, `${data.position.x}-${data.position.y}-${data.position.z}`)
}
if (data.type === SoundType.FLAME_EXTINGUISH) {
this.destroyFlame(data.extra.fire, `${data.position.x}-${data.position.y}-${data.position.z}`)
this.destroyFlame(data.extra.id, `${data.position.x}-${data.position.y}-${data.position.z}`)
}
if (data.type === SoundType.SMOKE_FADE) {
this.smokeFade(data.extra.id)
}
if (data.type === SoundType.ITEM_DROP_AIR) {
const item = this.#dropItems[data.extra.id];
const item = this.#dropItems[data.extra.id]
item.rotation.x -= 0.1
item.rotation.y -= 0.1
item.rotation.z -= 0.1
@ -216,7 +225,7 @@ export class Game {
if (data.item.slot === InventorySlot.SLOT_BOMB) {
this.bombDropPosition = data.position
}
const item = this.#dropItems[data.extra.id];
const item = this.#dropItems[data.extra.id]
item.rotation.set(0, 0, 0)
item.rotateOnWorldAxis(new THREE.Vector3(0, 1, 0), Math.random() * 6.28)
item.position.set(data.position.x, data.position.y, -data.position.z)
@ -225,7 +234,7 @@ export class Game {
clearInterval(this.#bombTimerId)
}
if (data.type === SoundType.GRENADE_AIR || data.type === SoundType.GRENADE_BOUNCE || data.type === SoundType.GRENADE_LAND) {
const grenade = this.#throwables[data.extra.id];
const grenade = this.#throwables[data.extra.id]
grenade.rotation.x += 0.1
grenade.rotation.y += 0.1
grenade.rotation.z += 0.1
@ -266,33 +275,23 @@ export class Game {
const direction = grenade.getWorldPosition(new THREE.Vector3()).sub(sightPosition).normalize()
if (this.#world.getCamera().getWorldDirection(new THREE.Vector3()).dot(direction) <= 0) { // flash behind spectator
this.removeGrenade(throwableId)
return;
return
}
const ray = new THREE.Raycaster(sightPosition, direction)
const intersects = ray.intersectObjects([grenade, ...this.getMapObjects()], false);
const intersects = ray.intersectObjects([grenade, ...this.getMapObjects()], false)
if (intersects.length >= 1 && intersects[0].object === grenade) {
this.#hud.showFlashBangScreen()
}
this.removeGrenade(throwableId)
return;
return
}
if (item.slot === InventorySlot.SLOT_GRENADE_SMOKE) {
const grenade = this.#throwables[throwableId]
const smoke = new THREE.Mesh(new THREE.DodecahedronGeometry(300, 1), new THREE.MeshStandardMaterial({color: 0xdadada}))
smoke.material.side = THREE.DoubleSide
smoke.position.y = 150
grenade.add(smoke)
game.#roundIntervalIds.push(setTimeout(() => this.removeGrenade(throwableId), 18000))
return;
}
if (item.slot === InventorySlot.SLOT_GRENADE_MOLOTOV || item.slot === InventorySlot.SLOT_GRENADE_HE) {
this.removeGrenade(throwableId)
return;
if (item.slot === InventorySlot.SLOT_GRENADE_HE) {
this.removeGrenade(throwableId) // fixme add some cool effect
return
}
console.warn("No handler for grenade: ", item)
game.#roundIntervalIds.push(setTimeout(() => this.removeGrenade(throwableId), 1000)) // todo responsive volumetric smokes, etc.
game.#roundIntervalIds.push(setTimeout(() => this.removeGrenade(throwableId), 500))
}
itemDrop(item, id) {
@ -316,26 +315,67 @@ export class Game {
delete this.#throwables[id]
}
grillStart(position, maxTimeMs, maxFlamesCount) {
grillStart(fireId, position, size, maxTimeMs, maxPartCount) {
this.#volumetrics[fireId] = {}
this.#volumetrics[fireId]['size'] = size
this.#world.playSound('338301_4811732-lq.mp3', position, false)
}
spawnFlame(point, size, height, fireId, flameId) {
if (this.#flammable[fireId] === undefined) {
this.#flammable[fireId] = {}
}
height = lerp(height, randomInt(16, 26), Math.min(Math.sqrt(Object.keys(this.#flammable[fireId]).length) / randomInt(7, 9), 1))
spawnFlame(point, height, fireId, partId) {
const size = this.#volumetrics[fireId]['size']
height = lerp(height, randomInt(16, 26), Math.min(Math.sqrt(Object.keys(this.#volumetrics[fireId]).length) / randomInt(7, 9), 1))
const flame = this.#world.spawnFlame(size, height)
this.#flammable[fireId][flameId] = flame
this.#volumetrics[fireId][partId] = flame
flame.position.set(point.x, point.y + (height / 2), -1 * point.z)
flame.rotation.x = randomInt(-10, 10) / 100;
flame.rotation.x = randomInt(-10, 10) / 100
flame.rotateOnWorldAxis(new THREE.Vector3(0, 1, 0), Math.random() * 6.28)
}
destroyFlame(fireId, flameId) {
const flame = this.#flammable[fireId][flameId]
const flame = this.#volumetrics[fireId][flameId]
this.#world.destroyObject(flame)
delete this.#flammable[fireId][flameId]
delete this.#volumetrics[fireId][flameId]
}
smokeStart(smokeId, position, size, maxTimeMs, maxPartCount) {
this.#volumetrics[smokeId] = {}
this.#volumetrics[smokeId]['size'] = size
this.#volumetrics[smokeId]['count'] = maxPartCount
this.#volumetrics[smokeId]['geometries'] = []
}
spawnSmoke(point, height, smokeId, partId) {
const size = this.#volumetrics[smokeId]['size']
const geo = new THREE.BoxGeometry(size, height, size)
geo.translate(point.x, point.y + (geo.parameters.height / 2), -point.z)
this.#volumetrics[smokeId]['geometries'].push(geo)
const len = this.#volumetrics[smokeId]['geometries'].length
if (len % 10 !== 0 && len < this.#volumetrics[smokeId]['count']) {
return
}
let geometry = BufferGeometryUtils.mergeGeometries(this.#volumetrics[smokeId]['geometries'])
geometry = BufferGeometryUtils.mergeVertices(geometry, 2)
geometry.computeBoundingBox()
let mesh = this.#volumetrics[smokeId]['mesh']
if (mesh) {
this.#world.destroyObject(mesh)
}
mesh = this.#world.spawnSmoke(geometry)
this.#volumetrics[smokeId]['mesh'] = mesh
if (len === this.#volumetrics[smokeId]['count']) {
this.#volumetrics[smokeId]['range'] = Math.ceil(mesh.geometry.getIndex().count / 50)
mesh.geometry.setDrawRange(0, mesh.geometry.getIndex().count)
}
}
smokeFade(smokeId) {
const range = this.#volumetrics[smokeId]['range']
const mesh = this.#volumetrics[smokeId]['mesh']
this.#roundIntervalIds.push(setInterval(() => mesh.geometry.setDrawRange(0, mesh.geometry.drawRange.count - range), 50))
}
bombPlanted(timeMs, position) {
@ -347,7 +387,7 @@ export class Game {
this.#hud.bombPlanted(bombSecCount)
const tenSecWarningSecCount = Math.round(timeMs / 1000 - 10)
let tickSecondsCount = 0;
let tickSecondsCount = 0
let bombTimerId = setInterval(function () {
if (tickSecondsCount === bombSecCount) {
clearInterval(bombTimerId)
@ -356,7 +396,7 @@ export class Game {
world.playSound('88532__northern87__woosh-northern87.wav', null, true)
}
world.playSound('536422__rudmer-rotteveel__setting-electronic-timer-1-beep.wav', position, false)
tickSecondsCount++;
tickSecondsCount++
}, 1000)
this.#bombTimerId = bombTimerId
}
@ -393,7 +433,7 @@ export class Game {
}
this.playerMe = new Player(options.player, this.#world.createPlayerMe())
this.players[playerId] = this.playerMe;
this.players[playerId] = this.playerMe
this.playerSpectate = this.playerMe
if (this.#readyCallback) {
@ -430,7 +470,7 @@ export class Game {
const myId = this.playerSpectate.getId()
let aliveAvailableSpectateMates = this.getMyTeamPlayers().filter((player) => player.isAlive() && myId !== player.getId())
if (aliveAvailableSpectateMates.length === 0) {
return;
return
}
let ids = aliveAvailableSpectateMates.map((player) => player.getId()).sort()
@ -565,7 +605,7 @@ export class Game {
#otherPlayersInventoryChanged(player, data) {
const world = this.#world
const hand = player.get3DObject().getObjectByName('hand')
const belt = player.get3DObject().getObjectByName('belt');
const belt = player.get3DObject().getObjectByName('belt')
if (hand.children.length === 1) {
const lastHandItemModel = hand.children[0]

View File

@ -1,3 +1,6 @@
import * as THREE from 'three'
import {GLTFLoader} from 'three/addons/loaders/GLTFLoader.js';
import * as SkeletonUtils from 'three/addons/utils/SkeletonUtils.js';
import {ItemId} from "./Enums.js";
export class ModelRepository {
@ -7,6 +10,7 @@ export class ModelRepository {
#meshes = {}
#materials = {
caps: {},
smoke: null,
outfitTeam: null,
outfitOpponent: null,
}
@ -16,7 +20,7 @@ export class ModelRepository {
#mapObjects = [];
constructor() {
this.#gltfLoader = new THREE.GLTFLoader()
this.#gltfLoader = new GLTFLoader()
this.#textureLoader = new THREE.TextureLoader()
}
@ -53,7 +57,7 @@ export class ModelRepository {
}
})
const sun = new THREE.DirectionalLight(0xffeac2, .9)
const sun = new THREE.DirectionalLight(0xffeac2, 4)
sun.position.set(4000, 4999, -4000)
sun.castShadow = true
sun.shadow.mapSize.width = 4096
@ -63,7 +67,7 @@ export class ModelRepository {
sun.shadow.camera.right = 2000
sun.shadow.camera.top = 0
sun.shadow.camera.bottom = -3000
model.scene.add(sun, new THREE.AmbientLight(0xcfe4bb, .4))
model.scene.add(sun, new THREE.AmbientLight(0xcfe4bb, 1))
return model.scene
})
}
@ -77,7 +81,7 @@ export class ModelRepository {
}
getPlayer(colorIndex, isOpponent) {
const clone = THREE.SkeletonUtils.clone(this.#models.player)
const clone = SkeletonUtils.clone(this.#models.player)
const player = clone.getObjectByName('player')
const headWear = player.getObjectByName('Wolf3D_Headwear')
@ -101,6 +105,10 @@ export class ModelRepository {
return this.#meshes.playerHitMesh.clone()
}
getSmokeMaterial() {
return this.#materials.smoke
}
getModelForItem(item) {
if (item.id === ItemId.Bomb) {
return this.getBomb()
@ -235,6 +243,14 @@ export class ModelRepository {
const sprite = new THREE.Sprite(material);
sprite.scale.set(35, 45, 30);
this.#materials.smoke = new THREE.MeshStandardMaterial({ // todo better material with cool displacement map etc.
color: 0x798aa0,
map: texture,
blending: THREE.AdditiveBlending,
side: THREE.FrontSide,
})
this.#meshes.playerHitMesh = sprite
}))

View File

@ -1,3 +1,5 @@
import {AnimationMixer, Vector3} from 'three'
export class Player {
data = {
id: null,
@ -45,7 +47,7 @@ export class Player {
setAnimations(animations) {
animations.forEach((clip) => {
const mixer = new THREE.AnimationMixer(this.#threeObject)
const mixer = new AnimationMixer(this.#threeObject)
const action = mixer.clipAction(clip);
if (clip.name === 'crouch') {
action.play()
@ -114,7 +116,7 @@ export class Player {
}
getSightPositionThreeVector() {
return new THREE.Vector3(
return new Vector3(
this.data.position.x,
this.data.position.y + this.data.sight,
-this.data.position.z,

View File

@ -136,7 +136,7 @@ export class SoundRepository {
if (type === SoundType.FLAME_PLAYER_HIT) {
return '512138__beezlefm__item-sound.wav'
}
if (type === SoundType.FLAME_SPAWN || type === SoundType.FLAME_EXTINGUISH) {
if (type === SoundType.FLAME_SPAWN || type === SoundType.FLAME_EXTINGUISH || type === SoundType.SMOKE_SPAWN || type === SoundType.SMOKE_FADE) { // fixme make some noise
return null
}
if (type === SoundType.GRENADE_AIR) {

View File

@ -1,5 +1,7 @@
import * as Enum from "./Enums.js";
import {ModelRepository} from "./ModelRepository.js";
import * as THREE from 'three'
import * as Enum from "./Enums.js"
import {RGBELoader} from "three/addons/loaders/RGBELoader.js"
import {ModelRepository} from "./ModelRepository.js"
export class World {
#scene
@ -9,13 +11,11 @@ export class World {
#audioLoader
#modelRepository
#decals = []
#flames = []
#cache = {}
volume = 30
constructor() {
THREE.Cache.enabled = true
THREE.ColorManagement.legacyMode = false
this.#audioLoader = new THREE.AudioLoader()
this.#modelRepository = new ModelRepository()
@ -51,15 +51,15 @@ export class World {
}
const renderer = new THREE.WebGLRenderer(glParameters)
renderer.outputEncoding = THREE.sRGBEncoding
renderer.toneMapping = THREE.ACESFilmicToneMapping
renderer.toneMapping = THREE.CineonToneMapping
renderer.toneMappingExposure = setting.getExposure()
renderer.physicallyCorrectLights = false
renderer.setSize(window.innerWidth, window.innerHeight)
if (!setting.shouldPreferPerformance()) {
new THREE.RGBELoader().load('./resources/img/orlando_stadium_1k.hdr', function (texture) {
new RGBELoader().load('./resources/img/orlando_stadium_1k.hdr', function (texture) {
texture.mapping = THREE.EquirectangularReflectionMapping
texture.toneMapped = true
scene.environment = texture
scene.environmentIntensity = 0.4
})
renderer.shadowMap.enabled = true
renderer.shadowMap.type = THREE.PCFSoftShadowMap
@ -196,7 +196,12 @@ export class World {
mesh.receiveShadow = true
this.#scene.add(mesh)
this.#flames.push(mesh)
return mesh
}
spawnSmoke(geometry) {
const mesh = new THREE.Mesh(geometry, this.#modelRepository.getSmokeMaterial())
this.#scene.add(mesh)
return mesh
}
@ -234,8 +239,6 @@ export class World {
reset() {
this.clearDecals()
this.#flames.forEach((item) => this.destroyObject(item))
this.#flames = []
const bomb = this.#modelRepository.getBomb()
if (bomb.parent && bomb.parent.name === 'MainScene') {
bomb.visible = false
@ -243,6 +246,10 @@ export class World {
}
destroyObject(object) {
if (object.name === undefined) {
return
}
if (object.name === `item-${Enum.ItemId.Bomb}`) {
if (object.parent && object.parent.name === 'MainScene') {
object.visible = false

View File

@ -1,3 +1,4 @@
import * as THREE from 'three'
import {Color, InventorySlot} from "../Enums.js";
export class Radar {

View File

@ -5,6 +5,7 @@ import {World} from "./World.js";
import Stats from "../threejs/Stats.js";
import {Setting} from "./Setting.js";
import {PlayerAction} from "./PlayerAction.js";
import {PointerLockControls} from "three/addons/controls/PointerLockControls.js";
let launchGame
(function () {
@ -51,7 +52,7 @@ let launchGame
const setting = new Setting(settingString)
const canvas = await world.init(map, setting)
const pointerLock = new THREE.PointerLockControls(world.getCamera(), canvasParent)
const pointerLock = new PointerLockControls(world.getCamera(), canvasParent)
pointerLock.pointerSpeed = setting.getSensitivity()
hud.createHud(elementHud, map, setting)

View File

@ -1,14 +1,14 @@
function degreeToRadian(degree) {
return THREE.MathUtils.degToRad(degree)
return degree * Math.PI / 180
}
function radianToDegree(radian) {
return Math.round(THREE.MathUtils.radToDeg(radian))
return Math.round(radian * 180 / Math.PI)
}
function threeRotationToServer(eulerYXZ) {
const horizontal = THREE.MathUtils.radToDeg(eulerYXZ.y)
const vertical = THREE.MathUtils.radToDeg(eulerYXZ.x)
const horizontal = eulerYXZ.y * 180 / Math.PI
const vertical = eulerYXZ.x * 180 / Math.PI
if (horizontal === 0) {
return [0, vertical]
@ -38,11 +38,11 @@ function scopeLevelToZoom(scopeLevel) {
}
function randomInt(start, end) {
return THREE.MathUtils.randInt(start, end)
return start + Math.floor(Math.random() * (end - start + 1));
}
function lerp(start, end, percentage) {
return THREE.MathUtils.lerp(start, end, percentage)
return (1 - percentage) * start + percentage * end;
}
function msToTick(timeMs) {

View File

@ -4,7 +4,9 @@ require __DIR__ . '/../vendor/autoload.php';
use cs\Enum\SoundType;
use cs\Event\EventList;
use cs\Event\GrillEvent;
use cs\Event\KillEvent;
use cs\Event\SmokeEvent;
use cs\Event\SoundEvent;
use cs\Event\ThrowEvent;
@ -55,7 +57,14 @@ $frameIdEnd = null;
width: 100%;
}
</style>
<script src="./assets/threejs/three.js"></script>
<script type="importmap">
{
"imports": {
"three": "./assets/threejs/three.min.js",
"three/addons/": "./assets/threejs/"
}
}
</script>
<script src="./assets/js/utils.js"></script>
</head>
<body>
@ -90,7 +99,9 @@ $frameIdEnd = null;
<input type="range" id="progress" min="0" value="0" max="<?= $frameIdEnd ?>">
</div>
</div>
<script>
<script type="module">
import * as THREE from 'three'
import {OrbitControls} from 'three/addons/controls/OrbitControls.js'
const renderer = {
gl: null,
@ -113,7 +124,7 @@ $frameIdEnd = null;
})))
const lastObject = this.fillWorld(floors, walls)
this.camera.lookAt(lastObject)
new THREE.OrbitControls(this.camera, this.gl.domElement);
new OrbitControls(this.camera, this.gl.domElement);
},
fillWorld: function (floors, walls) {
let lastMesh
@ -216,12 +227,12 @@ $frameIdEnd = null;
return player
},
showTrajectory: false,
throwables: [],
flammable: [],
createBox: function (width, height, depth, color) {
throwables: {},
volumetrics: {},
createVolume: function (width, height, depth, color) {
let box = new THREE.Mesh(
new THREE.BoxGeometry(width, height, depth),
new THREE.MeshBasicMaterial({color: color}),
new THREE.MeshBasicMaterial({color: color, transparent: true, opacity: 0.4}),
)
this.scene.add(box)
return box
@ -254,6 +265,9 @@ $frameIdEnd = null;
self.throwables[event.data.id] = ball
}
}
if (event.code === <?= EventList::map[GrillEvent::class] ?> || event.code === <?= EventList::map[SmokeEvent::class] ?>) {
self.volumetrics[event.data.id] = {}
}
if (event.code === <?= EventList::map[SoundEvent::class] ?>) {
if ([<?= SoundType::GRENADE_LAND->value ?>, <?= SoundType::GRENADE_BOUNCE->value ?>, <?= SoundType::GRENADE_AIR->value ?>].includes(event.data.type)) {
const ball = self.showTrajectory ? self.createBall(self.throwables[event.data.extra.id]) : self.throwables[event.data.extra.id]
@ -263,18 +277,17 @@ $frameIdEnd = null;
setTimeout(() => ball.visible = false, 2000)
}
}
if (event.data.type === <?= SoundType::FLAME_SPAWN->value ?>) {
const color = new THREE.Color(`hsl(53, 100%, ${Math.random() * 70 + 20}%, 1)`)
let flame = self.createBox(event.data.extra.size, event.data.extra.height, event.data.extra.size, color)
if (self.flammable[event.data.extra.fire] === undefined) {
self.flammable[event.data.extra.fire] = {}
}
self.flammable[event.data.extra.fire][`${event.data.position.x}-${event.data.position.y}-${event.data.position.z}`] = flame
flame.position.set(event.data.position.x, event.data.position.y + (event.data.extra.height / 2), -event.data.position.z)
if (event.data.type === <?= SoundType::FLAME_SPAWN->value ?> || event.data.type === <?= SoundType::SMOKE_SPAWN->value ?>) {
const color = event.data.type === <?= SoundType::FLAME_SPAWN->value ?>
? new THREE.Color(`hsl(53, 100%, ${Math.random() * 70 + 20}%, 1)`)
: new THREE.Color(0x798aa0)
let mesh = self.createVolume(31, event.data.extra.height, 31, color)
self.volumetrics[event.data.extra.id][`${event.data.position.x}-${event.data.position.y}-${event.data.position.z}`] = mesh
mesh.position.set(event.data.position.x, event.data.position.y + (event.data.extra.height / 2), -event.data.position.z)
}
if (event.data.type === <?= SoundType::FLAME_EXTINGUISH->value ?>) {
const flame = self.flammable[event.data.extra.fire][`${event.data.position.x}-${event.data.position.y}-${event.data.position.z}`]
flame.visible = false
if (event.data.type === <?= SoundType::FLAME_EXTINGUISH->value ?> || event.data.type === <?= SoundType::SMOKE_FADE->value ?>) {
const mesh = self.volumetrics[event.data.extra.id][`${event.data.position.x}-${event.data.position.y}-${event.data.position.z}`]
mesh.visible = false
}
}
})
@ -298,7 +311,7 @@ $frameIdEnd = null;
const driver = {
frameId: <?= $frameIdStart ?>,
defaultAnimationSpeedMs: 100,
defaultAnimationSpeedMs: 30,
getFrameId: function () {
return this.frameId;
}

View File

@ -48,7 +48,14 @@
tickMs: 0,
}
</script>
<script src="./assets/threejs/three.js?v=144"></script>
<script type="importmap">
{
"imports": {
"three": "./assets/threejs/three.min.js?v167",
"three/addons/": "./assets/threejs/"
}
}
</script>
<script src="./assets/js/utils.js"></script>
<script type="module">
import {Setting} from "./assets/js/Setting.js";

View File

@ -38,13 +38,23 @@ foreach ($map->getBoxes() as $box) {
<head>
<meta charset="UTF-8">
<title>Map Generator</title>
<script src="./assets/threejs/three.js"></script>
<script type="importmap">
{
"imports": {
"three": "./assets/threejs/three.min.js",
"three/addons/": "./assets/threejs/"
}
}
</script>
</head>
<body style="margin:0">
<div style="position:absolute;<?= $radarMapGenerator ? 'display:none;' : '' ?>">
<textarea class="map">Generating...</textarea>
</div>
<script>
<script type="module">
import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
const forRadar = <?= ($radarMapGenerator ? 'true' : 'false') ?>;
let camera, scene, renderer, controls, map, extra;
const worldMaterial = new THREE.MeshLambertMaterial({color: 0x9f998e})
@ -73,7 +83,7 @@ foreach ($map->getBoxes() as $box) {
}
camera.position.y = 4000
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls = new OrbitControls(camera, renderer.domElement);
}
function createRamp(startX = 1507.1, startY = 0, endX = 1676, endY = 140.1, depth = 80) {
@ -130,7 +140,7 @@ foreach ($map->getBoxes() as $box) {
map.add(mesh)
})
const s1 = new THREE.SpotLight(0xFFFFFF, .6)
const s1 = new THREE.SpotLight(0xFFFFFF, 5_000_000)
s1.castShadow = true
s1.shadow.mapSize.width = 2048
s1.shadow.mapSize.height = 2048
@ -138,8 +148,8 @@ foreach ($map->getBoxes() as $box) {
s1.shadow.camera.far = maxHeight * 10
s1.position.set(center.position.x, maxHeight * 2.5, center.position.z)
s1.target = center
const d1 = new THREE.DirectionalLight(0xffeac2, 0.6);
const a1 = new THREE.AmbientLight(0xDADADA, .8)
const d1 = new THREE.DirectionalLight(0xf0eadf, 5);
const a1 = new THREE.AmbientLight(0xDADADA, 1)
extra.add(s1, d1, a1);
scene.add(map, extra)

View File

@ -62,20 +62,30 @@ $slots = [
<head>
<meta charset="UTF-8">
<title>Player model Generator</title>
<script src="./assets/threejs/three.js"></script>
<script type="importmap">
{
"imports": {
"three": "./assets/threejs/three.min.js",
"three/addons/": "./assets/threejs/"
}
}
</script>
<script src="./assets/js/utils.js"></script>
</head>
<body style="margin:0">
<script>
<script type="module">
import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import {ModelRepository} from "./assets/js/ModelRepository.js";
let camera, scene, renderer, controls;
////
// fixme: add gui controls
const opacityPlayer = 1.0
const opacityHitBoxes = 0.0
const opacityBoundingBox = 0.0
////
</script>
<script>
let camera, scene, renderer, controls;
const materialDefault = new THREE.MeshBasicMaterial({color: 0x664b17, transparent: true, opacity: opacityHitBoxes, depthTest: false})
const materialArm = new THREE.MeshBasicMaterial({color: 0x114b3d, transparent: true, opacity: opacityHitBoxes, depthTest: false})
@ -103,7 +113,7 @@ $slots = [
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls = new OrbitControls(camera, renderer.domElement);
controls.target.set(0, 110, 0)
controls.update()
}
@ -130,9 +140,6 @@ $slots = [
init()
extra()
animate()
</script>
<script type="module">
import {ModelRepository} from "./assets/js/ModelRepository.js";
const modelRepository = new ModelRepository()
await modelRepository.loadAll()