diff --git a/packages/website/public/assets/images/3d/grid.png b/packages/website/public/assets/images/3d/grid.png index 0351cf368..868507a97 100644 Binary files a/packages/website/public/assets/images/3d/grid.png and b/packages/website/public/assets/images/3d/grid.png differ diff --git a/packages/website/src/components/MixerScene/components/Model.tsx b/packages/website/src/components/MixerScene/components/Model.tsx index b4825b06e..a068e13f7 100644 --- a/packages/website/src/components/MixerScene/components/Model.tsx +++ b/packages/website/src/components/MixerScene/components/Model.tsx @@ -1,4 +1,3 @@ -import { Vector3 } from 'three'; import { awaitableModelDefinitions } from '../../../core/awaitables/modelDefinitions'; import { jsxTree } from '../../../core/blitzkit/jsxTree'; import { useModel } from '../../../hooks/useModel'; @@ -32,17 +31,6 @@ export function Model() { const gunTurretModel = gunTankModel.turrets[gun.turret.id]; const gunModel = gunTurretModel.guns[gun.gun.id]; - const turretDelta = new Vector3( - tankModel.turret_origin.x - turretTankModel.turret_origin.x, - tankModel.turret_origin.y - turretTankModel.turret_origin.y, - tankModel.turret_origin.z - turretTankModel.turret_origin.z, - ); - const gunDelta = new Vector3( - turretModel.gun_origin.x - gunTurretModel.gun_origin.x, - turretModel.gun_origin.z - gunTurretModel.gun_origin.z, - turretModel.gun_origin.y - gunTurretModel.gun_origin.y, - ); - return ( {hullNodes.map((node) => { @@ -55,7 +43,7 @@ export function Model() { return jsxTree(node, { mesh(_, props, key) { - return ; + return ; }, }); })} @@ -86,7 +74,7 @@ export function Model() { return jsxTree(node, { mesh(_, props, key) { - return ; + return ; }, }); })} @@ -126,7 +114,7 @@ export function Model() { return jsxTree(node, { mesh(_, props, key) { - return ; + return ; }, }); })} diff --git a/packages/website/src/components/MixerScene/index.tsx b/packages/website/src/components/MixerScene/index.tsx index 7c37e7076..b30b214b9 100644 --- a/packages/website/src/components/MixerScene/index.tsx +++ b/packages/website/src/components/MixerScene/index.tsx @@ -1,4 +1,4 @@ -import { Environment, OrbitControls } from '@react-three/drei'; +import { OrbitControls } from '@react-three/drei'; import { Canvas } from '@react-three/fiber'; import { Suspense } from 'react'; import { Model } from './components/Model'; @@ -6,16 +6,16 @@ import { Model } from './components/Model'; export function MixerScene() { return ( - - {/* - */} + + diff --git a/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/Lighting.tsx b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/Lighting.tsx index c9c3a2449..4e69d5e66 100644 --- a/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/Lighting.tsx +++ b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/Lighting.tsx @@ -1,11 +1,13 @@ import { Environment } from '@react-three/drei'; -import { Euler } from 'three'; +import { useEffect, useRef } from 'react'; +import { DirectionalLight, Euler } from 'three'; import { degToRad } from 'three/src/math/MathUtils.js'; import { useModel } from '../../../../../../hooks/useModel'; import { Duel } from '../../../../../../stores/duel'; import { TankopediaDisplay } from '../../../../../../stores/tankopediaPersistent/constants'; const LIGHTS_COUNT = 5; +const SIZE = 2 ** 11; interface LightingProps { display: TankopediaDisplay; @@ -17,6 +19,29 @@ export function Lighting({ display }: LightingProps) { const isBrighterLighting = !hasPbr && display !== TankopediaDisplay.StaticArmor; + const light = useRef(null); + + useEffect(() => { + if (!light.current) return; + + light.current.shadow.mapSize.width = SIZE; + light.current.shadow.mapSize.height = SIZE; + }, []); + + return ( + <> + + + + + + ); + return ( <> {display !== TankopediaDisplay.StaticArmor && ( diff --git a/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/SceneProps/index.tsx b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/SceneProps/index.tsx index 07313eba2..b3974722d 100644 --- a/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/SceneProps/index.tsx +++ b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/SceneProps/index.tsx @@ -1,108 +1,14 @@ -import { asset, I_HAT, J_HAT } from '@blitzkit/core'; import { useFrame, useLoader } from '@react-three/fiber'; import { clamp } from 'lodash-es'; import { useRef } from 'react'; -import { - Group, - MeshStandardMaterial, - PerspectiveCamera, - TextureLoader, - Vector2, - Vector3, -} from 'three'; -import { GLTFLoader } from 'three/examples/jsm/Addons.js'; -import { degToRad, radToDeg } from 'three/src/math/MathUtils.js'; -import { awaitableEquipmentDefinitions } from '../../../../../../../core/awaitables/equipmentDefinitions'; -import { awaitableModelDefinitions } from '../../../../../../../core/awaitables/modelDefinitions'; -import { awaitableProvisionDefinitions } from '../../../../../../../core/awaitables/provisionDefinitions'; -import { applyPitchYawLimits } from '../../../../../../../core/blitz/applyPitchYawLimits'; -import { modelTransformEvent } from '../../../../../../../core/blitzkit/modelTransform'; -import { tankCharacteristics } from '../../../../../../../core/blitzkit/tankCharacteristics'; -import { Duel } from '../../../../../../../stores/duel'; +import { MeshStandardMaterial, TextureLoader } from 'three'; import { TankopediaEphemeral } from '../../../../../../../stores/tankopediaEphemeral'; -import { aimTarget } from './constants'; - -const [modelDefinitions, equipmentDefinitions, provisionDefinitions] = - await Promise.all([ - awaitableModelDefinitions, - awaitableEquipmentDefinitions, - awaitableProvisionDefinitions, - ]); - -const emptyVector = new Vector2(); export function SceneProps() { const disturbed = TankopediaEphemeral.use((state) => state.disturbed); - const targetCircleWrapper = useRef(null); - const clientTargetCircle = useRef(null); - const serverTargetCircle = useRef(null); const material = useRef(null); - const playground = useRef(null); - const turret = Duel.use((state) => state.protagonist.turret); - const track = Duel.use((state) => state.protagonist.track); - const tank = Duel.use((state) => state.protagonist.tank); - const gun = Duel.use((state) => state.protagonist.gun); - const shell = Duel.use((state) => state.protagonist.shell); - const engine = Duel.use((state) => state.protagonist.engine); - const tankModelDefinition = modelDefinitions.models[tank.id]; - const trackModelDefinition = - modelDefinitions.models[tank.id].tracks[track.id]; - const gunModelDefinition = - tankModelDefinition.turrets[turret.id].guns[gun.id]; - const turretModelDefinition = tankModelDefinition.turrets[turret.id]; - const hullOrigin = new Vector3( - trackModelDefinition.origin.x, - trackModelDefinition.origin.y, - -trackModelDefinition.origin.z, - ); - const turretOrigin = new Vector3( - tankModelDefinition.turret_origin.x, - tankModelDefinition.turret_origin.y, - -tankModelDefinition.turret_origin.z, - ); - const gunOriginOnlyY = new Vector3(0, turretModelDefinition.gun_origin.y, 0); - const shellOrigin = hullOrigin.clone().add(turretOrigin).add(gunOriginOnlyY); - const camouflage = Duel.use((state) => state.protagonist.camouflage); - const crewSkills = TankopediaEphemeral.use((state) => state.skills); - const consumables = Duel.use((state) => state.protagonist.consumables); - const provisions = Duel.use((state) => state.protagonist.provisions); - const assaultDistance = Duel.use( - (state) => state.protagonist.assaultDistance, - ); - const equipmentMatrix = Duel.use( - (state) => state.protagonist.equipmentMatrix, - ); - const characteristics = tankCharacteristics( - { - tank, - turret, - gun, - shell, - consumables, - applyDynamicArmor: false, - applyReactiveArmor: false, - applySpallLiner: false, - camouflage, - crewSkills, - engine, - equipmentMatrix, - provisions, - stockEngine: tank.engines[0], - stockGun: tank.turrets[0].guns[0], - stockTurret: tank.turrets[0], - stockTrack: tank.tracks[0], - track, - assaultDistance, - }, - { equipmentDefinitions, provisionDefinitions, tankModelDefinition }, - ); - let { dispersion } = characteristics; - const gunRotationSpeed = degToRad(characteristics.gunTraverseSpeed); - const mockTank = useLoader(GLTFLoader, asset(`3d/tanks/models/6929.glb`)); const texture = useLoader(TextureLoader, '/assets/images/3d/grid.png'); - const path = new Vector3(); - const direction = new Vector3(); texture.anisotropy = 2; useFrame(({ camera }) => { @@ -110,129 +16,10 @@ export function SceneProps() { material.current.opacity = clamp(0.5 * camera.position.y, 0, 1); }); - let lastTime = 0; - let DEBUG_disable = false; - - useFrame(({ raycaster, camera, clock, gl }) => { - if ( - !playground.current || - !targetCircleWrapper.current || - DEBUG_disable || - !clientTargetCircle.current || - !serverTargetCircle.current - ) { - return; - } - - raycaster.setFromCamera(emptyVector, camera); - - const cameraIntersections = raycaster.intersectObjects( - playground.current.children, - true, - ); - - let length: number; - - if (cameraIntersections.length === 0) { - length = characteristics.shellRange; - direction.set(0, 0, -1).applyQuaternion(camera.quaternion); - aimTarget.copy(shellOrigin).addScaledVector(direction, length); - } else { - aimTarget.copy(cameraIntersections[0].point); - path.copy(aimTarget).sub(shellOrigin); - length = path.length(); - direction.copy(path).normalize(); - } - - const deltaT = clock.elapsedTime - lastTime; - lastTime = clock.elapsedTime; - - const desiredPitch = Math.asin(direction.y); - const desiredYaw = Math.atan2(-direction.x, -direction.z); - const deltaPitch = desiredPitch - modelTransformEvent.last!.pitch; - const deltaYaw = desiredYaw - modelTransformEvent.last!.yaw; - const deltaAngle = Math.sqrt(deltaPitch ** 2 + deltaYaw ** 2); - const maxPossibleRotationInTime = gunRotationSpeed * deltaT; - const clampedDeltaAngle = Math.min(maxPossibleRotationInTime, deltaAngle); - const clampedDeltaPitch = - deltaAngle === 0 ? 0 : deltaPitch * (clampedDeltaAngle / deltaAngle); - const clampedDeltaYaw = - deltaAngle === 0 ? 0 : deltaYaw * (clampedDeltaAngle / deltaAngle); - - const [pitch, yaw] = applyPitchYawLimits( - modelTransformEvent.last!.pitch + clampedDeltaPitch, - modelTransformEvent.last!.yaw + clampedDeltaYaw, - gunModelDefinition.pitch, - turretModelDefinition.yaw, - ); - - const actualDeltaPitch = pitch - modelTransformEvent.last!.pitch; - const actualDeltaYaw = yaw - modelTransformEvent.last!.yaw; - const deltaRotation = Math.abs(actualDeltaPitch) + Math.abs(actualDeltaYaw); - const rotationSpeed = deltaRotation / deltaT; - const turretTraversePenalty = - characteristics.dispersionTurretTraversing * radToDeg(rotationSpeed); - - const penalty = Math.sqrt(1 + turretTraversePenalty ** 2); - - if (penalty === 1) { - const dispersionMod = dispersion - characteristics.dispersion; - const dispersionModDischarged = - dispersionMod * Math.exp(-deltaT / characteristics.aimTime) - - dispersionMod; - - dispersion += dispersionModDischarged; - } else { - dispersion = Math.max(characteristics.dispersion * penalty, dispersion); - } - - modelTransformEvent.dispatch({ pitch, yaw }); - - const gunDirection = new Vector3(0, 0, -1) - .applyAxisAngle(I_HAT, pitch) - .applyAxisAngle(J_HAT, yaw); - - raycaster.set(shellOrigin, gunDirection); - - const gunIntersections = raycaster.intersectObjects( - playground.current.children, - true, - ); - - let gunTarget: Vector3; - - if (gunIntersections.length === 0) { - gunTarget = shellOrigin - .clone() - .add(gunDirection.multiplyScalar(characteristics.shellRange)); - } else { - gunTarget = gunIntersections[0].point; - } - - targetCircleWrapper.current.position.copy(gunTarget); - - const distanceToTarget = gunTarget.distanceTo(shellOrigin); - const dispersionRadius = dispersion * (distanceToTarget / 100); - const dispersionSize = 2 * dispersionRadius; - const targetDistanceFromCamera = gunTarget.distanceTo(camera.position); - const angularSize = - 2 * Math.atan(dispersionSize / (2 * targetDistanceFromCamera)); - const verticalScreenSize = - 2 * Math.tan(degToRad((camera as PerspectiveCamera).fov) / 2); - const screenSpaceSize = - gl.domElement.clientHeight * (angularSize / verticalScreenSize); - const targetCircleDotsSize = `${screenSpaceSize}px`; - - clientTargetCircle.current.style.width = targetCircleDotsSize; - clientTargetCircle.current.style.height = targetCircleDotsSize; - serverTargetCircle.current.style.width = targetCircleDotsSize; - serverTargetCircle.current.style.height = targetCircleDotsSize; - }); - if (!disturbed) return null; return ( - + diff --git a/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/TankModel.tsx b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/TankModel.tsx index bed2eb857..724112693 100644 --- a/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/TankModel.tsx +++ b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/TankModel.tsx @@ -105,6 +105,7 @@ export function TankModel() { key={key} onPointerDown={onPointerDown} castShadow + receiveShadow /> ); }, @@ -184,6 +185,7 @@ export function TankModel() { key={key} onPointerDown={onPointerDown} castShadow + receiveShadow /> ); }, @@ -269,6 +271,7 @@ export function TankModel() { key={key} onPointerDown={onPointerDown} castShadow + receiveShadow /> ); }, diff --git a/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/index.tsx b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/index.tsx index 548902c5f..00a9754ed 100644 --- a/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/index.tsx +++ b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/index.tsx @@ -55,7 +55,6 @@ export const TankSandbox = forwardRef( const hasDownImprovedVerticalStabilizer = useEquipment(124); const protagonistGun = Duel.use((state) => state.protagonist.gun); const protagonistTurret = Duel.use((state) => state.protagonist.turret); - const mutateDuel = Duel.useMutation(); const tankModelDefinition = useTankModelDefinition(); const turretModelDefinition = tankModelDefinition.turrets[protagonistTurret.id]; @@ -199,7 +198,7 @@ export const TankSandbox = forwardRef( localClippingEnabled: true, preserveDrawingBuffer: true, }} - shadows + shadows="soft" onPointerDown={handlePointerDown} onPointerMissed={() => { mutateTankopediaEphemeral((draft) => {