import * as mars3d from "mars3d" export let map // mars3d.Map三维地图对象 // 需要覆盖config.json中地图属性参数(当前示例框架中自动处理合并) export const mapOptions = { scene: { center: { lat: 31.834317, lng: 117.2199, alt: 87, heading: 30, pitch: -29 }, fxaa: true } } /** * 初始化地图业务,生命周期钩子函数(必须) * 框架在地图初始化完成后自动调用该函数 * @param {mars3d.Map} mapInstance 地图对象 * @returns {void} 无 */ export function onMounted(mapInstance) { map = mapInstance // 记录map globalNotify("操作提示", `鼠标左键单击进行发射`) addDemo() } /** * 释放当前地图业务的生命周期函数 * @returns {void} 无 */ export function onUnmounted() { map = null } function addDemo() { const center = Cesium.Cartesian3.fromDegrees(117.220206, 31.834866, 50) const modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(center) const meshVisualizer = new Cesium.MeshVisualizer({ modelMatrix: modelMatrix, up: { y: 1 }, referenceAxisParameter: { length: 100, width: 0.05, headLength: 2, headWidth: 0.1 } }) map.scene.primitives.add(meshVisualizer) meshVisualizer.showReference = true // 显示坐标轴 function createRandomColor() { return Cesium.Color.fromRandom({ alpha: 1 }) // fromRgba(Math.floor(Math.random() * (1 << 24))); } function createMaterial(color) { if (typeof color === "string") { // } else if (Cesium.defined(color)) { color = Cesium.Color.fromRgba(color).withAlpha(1) } else { color = createRandomColor() } return new Cesium.MeshPhongMaterial({ defaultColor: color, side: Cesium.MeshMaterial.Sides.DOUBLE, translucent: false }) } // const groundMaterial = new Cesium.MeshPhongMaterial({ // defaultColor: "rgb(255,0,0)", // side: Cesium.MeshMaterial.Sides.DOUBLE, // translucent: false // }) Cesium.Cartesian3.prototype.set = function (x, y, z) { this.x = x this.y = y this.z = z } Cesium.Cartesian2.prototype.set = function (x, y) { this.x = x this.y = y } Cesium.Quaternion.prototype.set = function (x, y, z, w) { this.x = x this.y = y this.z = z this.w = w } Ammo().then(function () { // - Global variables - // Graphics variables // const clock = new THREE.Clock() const mouseCoords = new THREE.Vector2() // const raycaster = new THREE.Raycaster() const ballMaterial = createMaterial(0x202020) // Physics variables const gravityConstant = 7.8 let collisionConfiguration let dispatcher let broadphase let solver let physicsWorld const margin = 0.05 const convexBreaker = new THREE.ConvexObjectBreaker() // Rigid bodies include all movable objects const rigidBodies = [] const pos = new THREE.Vector3() const quat = new THREE.Quaternion() const transformAux1 = new Ammo.btTransform() const tempBtVec3_1 = new Ammo.btVector3(0, 0, 0) // const time = 0 const objectsToRemove = [] for (let i = 0; i < 500; i++) { objectsToRemove[i] = null } let numObjectsToRemove = 0 const impactPoint = new THREE.Vector3() const impactNormal = new THREE.Vector3() function initPhysics() { // Physics configuration collisionConfiguration = new Ammo.btDefaultCollisionConfiguration() dispatcher = new Ammo.btCollisionDispatcher(collisionConfiguration) broadphase = new Ammo.btDbvtBroadphase() solver = new Ammo.btSequentialImpulseConstraintSolver() physicsWorld = new Ammo.btDiscreteDynamicsWorld(dispatcher, broadphase, solver, collisionConfiguration) physicsWorld.setGravity(new Ammo.btVector3(0, -gravityConstant, 0)) } function createObject(mass, halfExtents, pos, quat, material) { // y,z调换位置 const object = new THREE.Mesh(new THREE.BoxGeometry(halfExtents.x * 2, halfExtents.y * 2, halfExtents.z * 2), material) object.position.copy(pos) object.quaternion.copy(quat) // object.position.y += 3; convexBreaker.prepareBreakableObject(object, mass, new THREE.Vector3(), new THREE.Vector3(), true) createDebrisFromBreakableObject(object) } function createObjects() { // Ground pos.set(0, -0.5, 0) quat.set(0, 0, 0, 1) const ground = createParalellepipedWithPhysics(40, 1, 40, 0, pos, quat, createMaterial(0xffffff)) // Tower 1 const towerMass = 1000 const towerHalfExtents = new THREE.Vector3(2, 5, 2) pos.set(-8, 5, 0) quat.set(0, 0, 0, 1) createObject(towerMass, towerHalfExtents, pos, quat, createMaterial("rgb(247,174,68)")) // Tower 2 pos.set(8, 5, 0) quat.set(0, 0, 0, 1) createObject(towerMass, towerHalfExtents, pos, quat, createMaterial("rgb(247,174,68)")) // Bridge const bridgeMass = 100 const bridgeHalfExtents = new THREE.Vector3(7, 0.2, 1.5) pos.set(0, 10.2, 0) quat.set(0, 0, 0, 1) createObject(bridgeMass, bridgeHalfExtents, pos, quat, createMaterial("rgb(247,174,68)")) // Stones const stoneMass = 120 const stoneHalfExtents = new THREE.Vector3(1, 2, 0.15) const numStones = 8 quat.set(0, 0, 0, 1) for (let i = 0; i < numStones; i++) { pos.set(0, 2, 15 * (0.5 - i / (numStones + 1))) createObject(stoneMass, stoneHalfExtents, pos, quat, createMaterial(0xb0b0b0)) } // Mountain const mountainMass = 860 const mountainHalfExtents = new THREE.Vector3(4, 5, 4) pos.set(5, mountainHalfExtents.y * 0.5, -7) quat.set(0, 0, 0, 1) const mountainPoints = [] // y,z调换位置 mountainPoints.push(new THREE.Vector3(mountainHalfExtents.x, -mountainHalfExtents.y, mountainHalfExtents.z)) mountainPoints.push(new THREE.Vector3(-mountainHalfExtents.x, -mountainHalfExtents.y, mountainHalfExtents.z)) mountainPoints.push(new THREE.Vector3(mountainHalfExtents.x, -mountainHalfExtents.y, -mountainHalfExtents.z)) mountainPoints.push(new THREE.Vector3(-mountainHalfExtents.x, -mountainHalfExtents.y, -mountainHalfExtents.z)) mountainPoints.push(new THREE.Vector3(0, mountainHalfExtents.y, 0)) const mountain = new THREE.Mesh(new THREE.ConvexGeometry(mountainPoints), createMaterial("rgb(247,174,68)")) mountain.position.copy(pos) mountain.quaternion.copy(quat) convexBreaker.prepareBreakableObject(mountain, mountainMass, new THREE.Vector3(), new THREE.Vector3(), true) createDebrisFromBreakableObject(mountain) } function createParalellepipedWithPhysics(sx, sy, sz, mass, pos, quat, material) { const box = Cesium.BoxGeometry.createGeometry( Cesium.BoxGeometry.fromDimensions({ dimensions: new Cesium.Cartesian3(sx, sy, sz), vertexFormat: new Cesium.VertexFormat({ position: true, normal: true }) }) ) const object = new Cesium.Mesh(box, material) object.quaternion = new Cesium.Quaternion() const shape = new Ammo.btBoxShape(new Ammo.btVector3(sx * 0.5, sy * 0.5, sz * 0.5)) shape.setMargin(margin) createRigidBody(object, shape, mass, pos, quat) return object } function createDebrisFromBreakableObject(object) { object.castShadow = true object.receiveShadow = true const shape = createConvexHullPhysicsShape(object.geometry.vertices) shape.setMargin(margin) const body = createRigidBody(object, shape, object.userData.mass, null, null, object.userData.velocity, object.userData.angularVelocity) // Set pointer back to the three object only in the debris objects const btVecUserData = new Ammo.btVector3(0, 0, 0) btVecUserData.threeObject = object body.setUserPointer(btVecUserData) } function removeDebris(object) { meshVisualizer.remove(object) physicsWorld.removeRigidBody(object.userData.physicsBody) } function createConvexHullPhysicsShape(points) { const shape = new Ammo.btConvexHullShape() for (let i = 0, il = points.length; i < il; i++) { const p = points[i] tempBtVec3_1.setValue(p.x, p.y, p.z) // y,z调换位置 const lastOne = i === il - 1 shape.addPoint(tempBtVec3_1, lastOne) } return shape } function createRigidBody(object, physicsShape, mass, pos, quat, vel, angVel) { if (pos) { object.position.copy(pos) } else { pos = object.position } if (quat) { if (object.quaternion) { object.quaternion.copy(quat) } } else { quat = object.quaternion } const transform = new Ammo.btTransform() transform.setIdentity() transform.setOrigin(new Ammo.btVector3(pos.x, pos.y, pos.z)) transform.setRotation(new Ammo.btQuaternion(quat.x, quat.y, quat.z, quat.w)) const motionState = new Ammo.btDefaultMotionState(transform) const localInertia = new Ammo.btVector3(0, 0, 0) physicsShape.calculateLocalInertia(mass, localInertia) const rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, motionState, physicsShape, localInertia) const body = new Ammo.btRigidBody(rbInfo) body.setFriction(0.5) if (vel) { body.setLinearVelocity(new Ammo.btVector3(vel.x, vel.y, vel.z)) } if (angVel) { body.setAngularVelocity(new Ammo.btVector3(angVel.x, angVel.y, angVel.z)) } object.userData.physicsBody = body object.userData.collided = false meshVisualizer.add(object) if (mass > 0) { rigidBodies.push(object) // Disable deactivation body.setActivationState(4) } physicsWorld.addRigidBody(body) return body } function updatePhysics(deltaTime) { // Step world physicsWorld.stepSimulation(deltaTime, 10) // Update rigid bodies for (let i = 0, il = rigidBodies.length; i < il; i++) { const objThree = rigidBodies[i] const objPhys = objThree.userData.physicsBody const ms = objPhys.getMotionState() if (ms) { ms.getWorldTransform(transformAux1) const p = transformAux1.getOrigin() const q = transformAux1.getRotation() objThree.position.set(p.x(), p.y(), p.z()) if (objThree.quaternion) { objThree.quaternion.set(q.x(), q.y(), q.z(), q.w()) } objThree.userData.collided = false objThree.modelMatrixNeedsUpdate = true } } for (let i = 0, il = dispatcher.getNumManifolds(); i < il; i++) { const contactManifold = dispatcher.getManifoldByIndexInternal(i) const rb0 = contactManifold.getBody0() const rb1 = contactManifold.getBody1() const threeObject0 = Ammo.castObject(rb0.getUserPointer(), Ammo.btVector3).threeObject const threeObject1 = Ammo.castObject(rb1.getUserPointer(), Ammo.btVector3).threeObject if (!threeObject0 && !threeObject1) { continue } const userData0 = threeObject0 ? threeObject0.userData : null const userData1 = threeObject1 ? threeObject1.userData : null const breakable0 = userData0 ? userData0.breakable : false const breakable1 = userData1 ? userData1.breakable : false const collided0 = userData0 ? userData0.collided : false const collided1 = userData1 ? userData1.collided : false if ((!breakable0 && !breakable1) || (collided0 && collided1)) { continue } let contact = false let maxImpulse = 0 for (let j = 0, jl = contactManifold.getNumContacts(); j < jl; j++) { const contactPoint = contactManifold.getContactPoint(j) if (contactPoint.getDistance() < 0) { contact = true const impulse = contactPoint.getAppliedImpulse() if (impulse > maxImpulse) { maxImpulse = impulse const pos = contactPoint.get_m_positionWorldOnB() const normal = contactPoint.get_m_normalWorldOnB() impactPoint.set(pos.x(), pos.y(), pos.z()) impactNormal.set(normal.x(), normal.y(), normal.z()) } break } } // If no point has contact, abort if (!contact) { continue } // Subdivision const fractureImpulse = 250 if (breakable0 && !collided0 && maxImpulse > fractureImpulse) { const debris = convexBreaker.subdivideByImpact(threeObject0, impactPoint, impactNormal, 1, 2, 1.5) const numObjects = debris.length for (let j = 0; j < numObjects; j++) { createDebrisFromBreakableObject(debris[j]) } objectsToRemove[numObjectsToRemove++] = threeObject0 userData0.collided = true } if (breakable1 && !collided1 && maxImpulse > fractureImpulse) { const debris = convexBreaker.subdivideByImpact(threeObject1, impactPoint, impactNormal, 1, 2, 1.5) const numObjects = debris.length for (let j = 0; j < numObjects; j++) { createDebrisFromBreakableObject(debris[j]) } objectsToRemove[numObjectsToRemove++] = threeObject1 userData1.collided = true } } for (let i = 0; i < numObjectsToRemove; i++) { removeDebris(objectsToRemove[i]) } numObjectsToRemove = 0 } const ray = new Cesium.Ray() let clickRequest = false let start = false let hasInit = false let startTime = new Date() const rayDir = new Cesium.Cartesian3() function initInput() { const scene = map.scene const handler = new Cesium.ScreenSpaceEventHandler(scene.canvas) const lastMesh = null handler.setInputAction(function (movement) { if (!clickRequest) { Cesium.Cartesian2.clone(movement.position, mouseCoords) clickRequest = true } }, Cesium.ScreenSpaceEventType.LEFT_DOWN) } function processClick() { if (clickRequest) { meshVisualizer.getPickRay(mouseCoords, ray) if (!ray) { clickRequest = false return } // Creates a ball const ballMass = 35 const ballRadius = 0.4 const ball = new Cesium.Mesh( new Cesium.SphereGeometry({ radius: ballRadius, stackPartitions: 14, slicePartitions: 10 }), ballMaterial ) const ballShape = new Ammo.btSphereShape(ballRadius) ballShape.setMargin(margin) Cesium.Cartesian3.clone(ray.direction, rayDir) Cesium.Cartesian3.subtract(ray.origin, ray.direction, pos) quat.set(0, 0, 0, 1) const ballBody = createRigidBody(ball, ballShape, ballMass, pos, quat) // ballBody.setFriction(0.5); Cesium.Cartesian3.normalize(rayDir, rayDir) Cesium.Cartesian3.multiplyByScalar(rayDir, 50, rayDir) ballBody.setLinearVelocity(new Ammo.btVector3(rayDir.x, rayDir.y, rayDir.z)) clickRequest = false } } const hs = false function update(frameState) { if (hs) { return } const deltaTime = (new Date() - startTime) / 1000.0 updatePhysics(deltaTime) processClick() startTime = new Date() // hs = true; } setTimeout(function () { if (!hasInit) { // - Init - initPhysics() createObjects() initInput() hasInit = true } if (!start) { startTime = new Date() meshVisualizer.beforeUpdate.addEventListener(update) start = true } else { meshVisualizer.beforeUpdate.removeEventListener(update) start = false } }, 1000 * 3) }) }