import * as mars3d from "mars3d" export let map // mars3d.Map三维地图对象 // 需要覆盖config.json中地图属性参数(当前示例框架中自动处理合并) export const mapOptions = { scene: { center: { lat: 31.83456, lng: 117.219861, alt: 83, heading: 46, pitch: -35 }, fxaa: true } } /** * 初始化地图业务,生命周期钩子函数(必须) * 框架在地图初始化完成后自动调用该函数 * @param {mars3d.Map} mapInstance 地图对象 * @returns {void} 无 */ export function onMounted(mapInstance) { map = mapInstance // 记录map globalNotify("操作提示", `Q 逆时针摆动、 A 顺时针摆动`) addDemo() } /** * 释放当前地图业务的生命周期函数 * @returns {void} 无 */ export function onUnmounted() { map = null } function addDemo() { const MeshVisualizer = Cesium.MeshVisualizer const Mesh = Cesium.Mesh const MeshMaterial = Cesium.MeshMaterial const MeshPhongMaterial = Cesium.MeshPhongMaterial const center = Cesium.Cartesian3.fromDegrees(117.220206, 31.834866, 50) const modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(center) const meshVisualizer = new 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() { return new MeshPhongMaterial({ defaultColor: createRandomColor(), side: MeshMaterial.Sides.DOUBLE, translucent: false }) } Cesium.Cartesian3.prototype.set = function (x, y, z) { this.x = x this.y = y this.z = z } Cesium.Cartesian3.prototype.copy = function (src) { this.x = src.x this.y = src.y this.z = src.z } Cesium.Cartesian2.prototype.set = function (x, y) { this.x = x this.y = y } Cesium.Cartesian2.prototype.copy = function (src) { this.x = src.x this.y = src.y } Cesium.Quaternion.prototype.set = function (x, y, z, w) { this.x = x this.y = y this.z = z this.w = w } Cesium.Quaternion.prototype.copy = function (src) { this.x = src.x this.y = src.y this.z = src.z this.w = src.w } Ammo().then(function () { // - Global variables - // - Global variables - // Physics variables const gravityConstant = -9.8 let collisionConfiguration let dispatcher let broadphase let solver let physicsWorld const rigidBodies = [] const margin = 0.05 let hinge let rope const transformAux1 = new Ammo.btTransform() const time = 0 let armMovement = 0 let softBodySolver function initPhysics() { // Physics configuration collisionConfiguration = new Ammo.btSoftBodyRigidBodyCollisionConfiguration() dispatcher = new Ammo.btCollisionDispatcher(collisionConfiguration) broadphase = new Ammo.btDbvtBroadphase() solver = new Ammo.btSequentialImpulseConstraintSolver() softBodySolver = new Ammo.btDefaultSoftBodySolver() physicsWorld = new Ammo.btSoftRigidDynamicsWorld(dispatcher, broadphase, solver, collisionConfiguration, softBodySolver) physicsWorld.setGravity(new Ammo.btVector3(0, gravityConstant, 0)) physicsWorld.getWorldInfo().set_m_gravity(new Ammo.btVector3(0, gravityConstant, 0)) } function createObjects() { const pos = new Cesium.Cartesian3() const quat = new Cesium.Quaternion() // Ground pos.set(0, -0.5, 0) quat.set(0, 0, 0, 1) const ground = createParalellepiped( 40, 1, 40, 0, pos, quat, new MeshPhongMaterial({ defaultColor: Cesium.Color.fromRgba(0xffffff).withAlpha(1), // "rgb(200,200,200)", side: MeshMaterial.Sides.DOUBLE, translucent: false }) ) ground.quaternion = null // Ball const ballMass = 1.2 const ballRadius = 0.6 const ball = new Mesh( new Cesium.SphereGeometry({ radius: ballRadius, stackPartitions: 20, slicePartitions: 20 }), new MeshPhongMaterial({ defaultColor: Cesium.Color.fromRgba(0x000000).withAlpha(1), side: MeshMaterial.Sides.DOUBLE, translucent: false }) ) const ballShape = new Ammo.btSphereShape(ballRadius) ballShape.setMargin(margin) pos.set(-3, 2, 0) quat.set(0, 0, 0, 1) createRigidBody(ball, ballShape, ballMass, pos, quat) ball.physicsBody.setFriction(0.5) // Wall const brickMass = 0.5 const brickLength = 1.2 const brickDepth = 0.6 const brickHeight = brickLength * 0.5 const numBricksLength = 6 const numBricksHeight = 8 const z0 = -numBricksLength * brickLength * 0.5 pos.set(0, brickHeight * 0.5, z0) quat.set(0, 0, 0, 1) for (let j = 0; j < numBricksHeight; j++) { const oddRow = j % 2 === 1 pos.z = z0 if (oddRow) { pos.z -= 0.25 * brickLength } const nRow = oddRow ? numBricksLength + 1 : numBricksLength for (let i = 0; i < nRow; i++) { let brickLengthCurrent = brickLength let brickMassCurrent = brickMass if (oddRow && (i === 0 || i === nRow - 1)) { brickLengthCurrent *= 0.5 brickMassCurrent *= 0.5 } const brick = createParalellepiped(brickDepth, brickHeight, brickLengthCurrent, brickMassCurrent, pos, quat, createMaterial()) if (oddRow && (i === 0 || i === nRow - 2)) { pos.z += 0.75 * brickLength } else { pos.z += brickLength } } pos.y += brickHeight } // The rope // Rope graphic object const ropeNumSegments = 10 const ropeLength = 4 const ropeMass = 3 const ropePos = ball.position.clone() ropePos.y += ballRadius const segmentLength = ropeLength / ropeNumSegments const ropeMaterial = new MeshMaterial({ defaultColor: Cesium.Color.fromRgba(0x000000).withAlpha(1), side: MeshMaterial.Sides.DOUBLE, translucent: false, wireframe: true }) const ropePositions = [] const ropeIndices = [] for (let i = 0; i < ropeNumSegments + 1; i++) { ropePositions.push(ropePos.x, ropePos.y + i * segmentLength, ropePos.z) } for (let i = 0; i < ropeNumSegments; i++) { ropeIndices.push(i, i + 1) } const ropeGeometry = new Cesium.Geometry({ attributes: { position: new Cesium.GeometryAttribute({ componentDatatype: Cesium.ComponentDatatype.DOUBLE, componentsPerAttribute: 3, values: new Float32Array(ropePositions) }) }, indices: new Uint16Array(ropeIndices), primitiveType: Cesium.PrimitiveType.LINES, boundingSphere: Cesium.BoundingSphere.fromVertices(ropePositions) }) rope = new Mesh(ropeGeometry, ropeMaterial) meshVisualizer.add(rope) // Rope physic object const softBodyHelpers = new Ammo.btSoftBodyHelpers() const ropeStart = new Ammo.btVector3(ropePos.x, ropePos.y, ropePos.z) const ropeEnd = new Ammo.btVector3(ropePos.x, ropePos.y + ropeLength, ropePos.z) const ropeSoftBody = softBodyHelpers.CreateRope(physicsWorld.getWorldInfo(), ropeStart, ropeEnd, ropeNumSegments - 1, 0) const sbConfig = ropeSoftBody.get_m_cfg() sbConfig.set_viterations(10) sbConfig.set_piterations(10) ropeSoftBody.setTotalMass(ropeMass, false) Ammo.castObject(ropeSoftBody, Ammo.btCollisionObject) .getCollisionShape() .setMargin(margin * 3) physicsWorld.addSoftBody(ropeSoftBody, 1, -1) rope.physicsBody = ropeSoftBody // Disable deactivation ropeSoftBody.setActivationState(4) // The base const armMass = 2 const armLength = 3 const pylonHeight = ropePos.y + ropeLength const baseMaterial = createMaterial() pos.set(ropePos.x, 0.1, ropePos.z - armLength) quat.set(0, 0, 0, 1) const base = createParalellepiped(1, 0.2, 1, 0, pos, quat, baseMaterial) base.quaternion = null pos.set(ropePos.x, 0.5 * pylonHeight, ropePos.z - armLength) const pylon = createParalellepiped(0.4, pylonHeight, 0.4, 0, pos, quat, baseMaterial) pylon.quaternion = null pos.set(ropePos.x, pylonHeight + 0.2, ropePos.z - 0.5 * armLength) const arm = createParalellepiped(0.4, 0.4, armLength + 0.4, armMass, pos, quat, baseMaterial) // Glue the rope extremes to the ball and the arm const influence = 1 ropeSoftBody.appendAnchor(0, ball.physicsBody, true, influence) ropeSoftBody.appendAnchor(ropeNumSegments, arm.physicsBody, true, influence) // Hinge constraint to move the arm const pivotA = new Ammo.btVector3(0, pylonHeight * 0.5, 0) const pivotB = new Ammo.btVector3(0, -0.2, -armLength * 0.5) const axis = new Ammo.btVector3(0, 1, 0) hinge = new Ammo.btHingeConstraint(pylon.physicsBody, arm.physicsBody, pivotA, pivotB, axis, axis, true) physicsWorld.addConstraint(hinge, true) } function createParalellepiped(sx, sy, sz, mass, pos, quat, material) { const box = Cesium.BoxGeometry.fromDimensions({ dimensions: new Cesium.Cartesian3(sx, sy, sz), vertexFormat: new Cesium.VertexFormat({ position: true, normal: true }) }) const threeObject = new Mesh(box, material) const shape = new Ammo.btBoxShape(new Ammo.btVector3(sx * 0.5, sy * 0.5, sz * 0.5)) shape.setMargin(margin) createRigidBody(threeObject, shape, mass, pos, quat) return threeObject } function createRigidBody(threeObject, physicsShape, mass, pos, quat) { threeObject.position.copy(pos) threeObject.quaternion = new Cesium.Quaternion(quat) 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) threeObject.physicsBody = body meshVisualizer.add(threeObject) if (mass > 0) { rigidBodies.push(threeObject) // Disable deactivation body.setActivationState(4) } physicsWorld.addRigidBody(body) } function initInput() { window.addEventListener( "keydown", function (event) { switch (event.keyCode) { // Q case 81: armMovement = 1 break // A case 65: armMovement = -1 break } }, false ) window.addEventListener( "keyup", function (event) { armMovement = 0 }, false ) } function updatePhysics(deltaTime) { // Hinge control hinge.enableAngularMotor(true, 1.5 * armMovement, 50) // Step world physicsWorld.stepSimulation(deltaTime, 10) // Update rope const softBody = rope.physicsBody const ropePositions = rope.geometry.attributes.position.values const numVerts = ropePositions.length / 3 const nodes = softBody.get_m_nodes() let indexFloat = 0 for (let i = 0; i < numVerts; i++) { const node = nodes.at(i) const nodePos = node.get_m_x() ropePositions[indexFloat++] = nodePos.x() ropePositions[indexFloat++] = nodePos.y() ropePositions[indexFloat++] = nodePos.z() } rope.geometry.attributes.position.needsUpdate = true // Update rigid bodies for (let i = 0, il = rigidBodies.length; i < il; i++) { const objThree = rigidBodies[i] const objPhys = objThree.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.modelMatrixNeedsUpdate = true } } } let init = false let start = false let startTime = new Date() function update(frameState) { const deltaTime = (new Date() - startTime) / 1000.0 updatePhysics(deltaTime) startTime = new Date() } setTimeout(function () { if (!init) { // - Init - initPhysics() createObjects() initInput() init = true } if (!start) { startTime = new Date() meshVisualizer.beforeUpdate.addEventListener(update) start = true } else { meshVisualizer.beforeUpdate.removeEventListener(update) start = false } }, 1000 * 3) }) }