mirror of
https://gitee.com/ice-gl/icegl-three-vue-tres.git
synced 2025-04-05 06:22:43 +08:00
增加地形的物理场景
This commit is contained in:
parent
88b570084c
commit
d3d944b548
BIN
public/plugins/cannonPhysics/preview/terrainBalls.png
Normal file
BIN
public/plugins/cannonPhysics/preview/terrainBalls.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 114 KiB |
78
src/plugins/cannonPhysics/components/terrainBallsCannon.vue
Normal file
78
src/plugins/cannonPhysics/components/terrainBallsCannon.vue
Normal file
@ -0,0 +1,78 @@
|
||||
<template></template>
|
||||
<script setup lang="ts">
|
||||
import { World, Sphere, Body, Heightfield, Vec3 } from 'cannon-es'
|
||||
import { useRenderLoop } from '@tresjs/core'
|
||||
|
||||
const props = defineProps({
|
||||
sphereGroup: {
|
||||
default: null as any,
|
||||
},
|
||||
plane: {
|
||||
default: null as any,
|
||||
},
|
||||
})
|
||||
|
||||
const world = new World()
|
||||
world.gravity.set(0, -9.82, 0)
|
||||
|
||||
const sphereBodyList = [] as any[]
|
||||
props.sphereGroup.children.forEach((sphere: any) => {
|
||||
const sphereSize = sphere.geometry.parameters.radius
|
||||
const mass = 1
|
||||
|
||||
const sphereShape = new Sphere(sphereSize)
|
||||
|
||||
const sphereBody = new Body({ mass })
|
||||
sphereBody.addShape(sphereShape)
|
||||
sphereBody.position.copy(sphere.position)
|
||||
// sphereBody.material = new Material({ restitution: 10 })
|
||||
|
||||
sphereBodyList.push(sphereBody)
|
||||
world.addBody(sphereBody)
|
||||
})
|
||||
|
||||
// 不太建议用 planeGeometry
|
||||
let planeData = [] as any[]
|
||||
const pos = props.plane.geometry.getAttribute('position')
|
||||
const widthSegments = props.plane.geometry.parameters.widthSegments + 1
|
||||
const heightSegments = props.plane.geometry.parameters.heightSegments + 1
|
||||
|
||||
for (let i = 0; i < heightSegments; i++) {
|
||||
planeData.push([])
|
||||
}
|
||||
for (let i = 0; i < widthSegments; i++) {
|
||||
for (let j = 0; j < heightSegments; j++) {
|
||||
planeData[j][i] = pos.getZ((widthSegments - 1 - i) * heightSegments + j)
|
||||
}
|
||||
}
|
||||
|
||||
// planeData[0] = [0, 10, -10]
|
||||
// planeData[1] = [0, 0, 0]
|
||||
// planeData[2] = [0, 0, 0]
|
||||
// console.log(planeData)
|
||||
const terrainShape = new Heightfield(planeData, { elementSize: props.plane.geometry.parameters.width / (widthSegments - 1) })
|
||||
const terrainBody = new Body({ mass: 0 })
|
||||
terrainBody.addShape(
|
||||
terrainShape,
|
||||
new Vec3(
|
||||
-props.plane.geometry.parameters.width / 2, // X 偏移到左下角
|
||||
-props.plane.geometry.parameters.height / 2,
|
||||
0,
|
||||
),
|
||||
)
|
||||
terrainBody.quaternion.set(...(props.plane.quaternion.toArray() as [number, number, number, number]))
|
||||
// terrainBody.quaternion.setFromEuler(-Math.PI * 0.5, 0, 0, 'XYZ')
|
||||
// terrainBody.position.set(-50, 0, 50)
|
||||
world.addBody(terrainBody)
|
||||
|
||||
const { onLoop } = useRenderLoop()
|
||||
onLoop(({ delta }) => {
|
||||
world.step(1 / 120, delta)
|
||||
|
||||
sphereBodyList.forEach((sphereBody, index) => {
|
||||
props.sphereGroup.children[index].position.copy(sphereBody.position)
|
||||
})
|
||||
|
||||
// props.plane.position.copy(terrainBody.position)
|
||||
})
|
||||
</script>
|
47
src/plugins/cannonPhysics/components/terrainBallsThree.vue
Normal file
47
src/plugins/cannonPhysics/components/terrainBallsThree.vue
Normal file
@ -0,0 +1,47 @@
|
||||
<!--
|
||||
* @Description:
|
||||
* @Version: 1.668
|
||||
* @Autor: 地虎降天龙
|
||||
* @Date: 2025-01-02 14:09:15
|
||||
* @LastEditors: 地虎降天龙
|
||||
* @LastEditTime: 2025-01-03 08:38:28
|
||||
-->
|
||||
<template>
|
||||
<TresGroup ref="sphereGroupRef">
|
||||
<template v-for="i in 10" :key="i">
|
||||
<TresMesh v-for="j in 10" :key="j" :position="[i * 10 - 55, 26, j * 10 - 55]" cast-shadow>
|
||||
<TresSphereGeometry :args="[0.6, 16, 16]" />
|
||||
<TresMeshStandardMaterial color="#ffffff" />
|
||||
</TresMesh>
|
||||
</template>
|
||||
</TresGroup>
|
||||
<TresMesh ref="planeRef" :geometry="planeGeometry" :rotation-x="-Math.PI / 2" receive-shadow>
|
||||
<TresMeshPhysicalMaterial :roughness="1" :metalness="0" color="gray" :side="THREE.DoubleSide"/>
|
||||
</TresMesh>
|
||||
|
||||
<terrainBallsCannon v-if="planeRef" :sphereGroup="sphereGroupRef" :plane="planeRef" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import * as THREE from 'three'
|
||||
import { createNoise2D } from 'simplex-noise'
|
||||
import terrainBallsCannon from './terrainBallsCannon.vue'
|
||||
|
||||
const sphereGroupRef = ref()
|
||||
const planeRef = ref()
|
||||
|
||||
const noise2D = createNoise2D()
|
||||
const elevation = (x: number, y: number) => {
|
||||
var major = 0.8 * 4 * noise2D(0.1 * x, 0.1 * y),
|
||||
minor = 0.2 * noise2D(0.3 * x, 0.3 * y)
|
||||
return (major + minor) * 1.2
|
||||
}
|
||||
const planeGeometry = new THREE.PlaneGeometry(100, 100, 49, 49)
|
||||
const pos = planeGeometry.getAttribute('position')
|
||||
for (var i = 0; i < pos.count; i++) pos.setZ(i, elevation(pos.getX(i), pos.getY(i)))
|
||||
planeGeometry.computeBoundingSphere()
|
||||
planeGeometry.computeVertexNormals()
|
||||
|
||||
console.log(planeGeometry)
|
||||
</script>
|
@ -43,7 +43,8 @@ const planeShape = new Plane()
|
||||
const planeBody = new Body({ mass: 0 })
|
||||
planeBody.addShape(planeShape)
|
||||
planeBody.position.copy(props.plane.position)
|
||||
planeBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0)
|
||||
planeBody.quaternion.set(...(props.plane.quaternion.toArray() as [number, number, number, number]))
|
||||
// planeBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0)
|
||||
planeBody.material = new Material()
|
||||
|
||||
// 创建接触材料,定义球体和平面之间的摩擦力和弹性
|
||||
|
@ -4,7 +4,7 @@
|
||||
* @Autor: 地虎降天龙
|
||||
* @Date: 2024-12-30 11:15:55
|
||||
* @LastEditors: 地虎降天龙
|
||||
* @LastEditTime: 2025-01-02 09:45:03
|
||||
* @LastEditTime: 2025-01-02 13:58:17
|
||||
*/
|
||||
export default {
|
||||
name: 'cannonPhysics',
|
||||
@ -26,5 +26,13 @@ export default {
|
||||
disableFPSGraph: false,
|
||||
disableSrcBtn: false,
|
||||
},
|
||||
{
|
||||
src: 'plugins/cannonPhysics/preview/terrainBalls.png',
|
||||
type: 'img',
|
||||
name: 'terrainBalls',
|
||||
title: '地形球',
|
||||
disableFPSGraph: false,
|
||||
disableSrcBtn: false,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
58
src/plugins/cannonPhysics/pages/terrainBalls.vue
Normal file
58
src/plugins/cannonPhysics/pages/terrainBalls.vue
Normal file
@ -0,0 +1,58 @@
|
||||
<!--
|
||||
* @Description:
|
||||
* @Version: 1.668
|
||||
* @Autor: 地虎降天龙
|
||||
* @Date: 2024-12-30 11:15:55
|
||||
* @LastEditors: 地虎降天龙
|
||||
* @LastEditTime: 2025-01-03 08:38:12
|
||||
-->
|
||||
<template>
|
||||
<TresCanvas v-bind="state" window-size>
|
||||
<TresPerspectiveCamera :position="[110, 70, 20]" :fov="45" :near="0.1" :far="1000" />
|
||||
<OrbitControls v-bind="controlsState" />
|
||||
<TresAmbientLight :intensity="2" />
|
||||
|
||||
<TresDirectionalLight ref="TDirectionalLight" :position="[10, 8, 4]" :intensity="2" cast-shadow />
|
||||
<TresDirectionalLight :position="[10, 2, 4]" :intensity="2" cast-shadow />
|
||||
|
||||
<terrainBallsThree />
|
||||
</TresCanvas>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { SRGBColorSpace, BasicShadowMap, NoToneMapping } from 'three'
|
||||
import { reactive, shallowRef, watchEffect } from 'vue'
|
||||
import { TresCanvas } from '@tresjs/core'
|
||||
import { OrbitControls } from '@tresjs/cientos'
|
||||
import terrainBallsThree from '../components/terrainBallsThree.vue'
|
||||
|
||||
const state = reactive({
|
||||
clearColor: '#201919',
|
||||
shadows: true,
|
||||
alpha: false,
|
||||
|
||||
shadowMapType: BasicShadowMap,
|
||||
outputColorSpace: SRGBColorSpace,
|
||||
toneMapping: NoToneMapping,
|
||||
})
|
||||
|
||||
const controlsState = reactive({
|
||||
enableDamping: true,
|
||||
dampingFactor: 0.05,
|
||||
enableZoom: true,
|
||||
autoRotate: false,
|
||||
})
|
||||
|
||||
const TDirectionalLight = shallowRef()
|
||||
watchEffect(() => {
|
||||
if (TDirectionalLight.value) {
|
||||
TDirectionalLight.value.shadow.mapSize.set(1000, 1000)
|
||||
TDirectionalLight.value.shadow.camera.near = 0.5
|
||||
TDirectionalLight.value.shadow.camera.far = 50000
|
||||
TDirectionalLight.value.shadow.camera.top = 20
|
||||
TDirectionalLight.value.shadow.camera.right = 20
|
||||
TDirectionalLight.value.shadow.camera.left = -20
|
||||
TDirectionalLight.value.shadow.camera.bottom = -20
|
||||
}
|
||||
})
|
||||
</script>
|
@ -4,7 +4,7 @@
|
||||
* @Autor: 地虎降天龙
|
||||
* @Date: 2024-12-30 11:15:55
|
||||
* @LastEditors: 地虎降天龙
|
||||
* @LastEditTime: 2025-01-02 11:00:35
|
||||
* @LastEditTime: 2025-01-02 15:42:42
|
||||
-->
|
||||
<template>
|
||||
<TresCanvas v-bind="state" window-size>
|
||||
@ -27,14 +27,16 @@
|
||||
<TresMeshToonMaterial />
|
||||
</TresMesh>
|
||||
|
||||
<theBasicCannon v-if="planeRef" :sphere="sphereRef" :sphere2="sphereRef2" :plane="planeRef" />
|
||||
<theBasicCannon v-if="planeRef" :sphere="sphereRef" :sphere2="sphereRef2" :plane="planeRef" />
|
||||
|
||||
<TresDirectionalLight ref="TDirectionalLight" :position="[10, 8, 4]" :intensity="1" cast-shadow />
|
||||
<TresDirectionalLight :position="[10, 2, 4]" :intensity="1" cast-shadow />
|
||||
|
||||
<TresGridHelper />
|
||||
</TresCanvas>
|
||||
<h1 class="text-center text-white w-full absolute">使用cannon-es库 API详见:<a target="_black" href="https://pmndrs.github.io/cannon-es/docs/">cannon-es/docs</a></h1>
|
||||
<h1 class="text-center text-white w-full absolute">
|
||||
使用cannon-es库 API详见:<a target="_black" href="https://pmndrs.github.io/cannon-es/docs/">cannon-es/docs</a>
|
||||
</h1>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
Loading…
x
Reference in New Issue
Block a user