添加物理地形。

This commit is contained in:
liteng 2018-09-30 21:42:02 +08:00
parent 46332d7571
commit 774ae2fdcb
2 changed files with 93 additions and 216 deletions

View File

@ -91,7 +91,16 @@ TerrainMenu.prototype.createShaderTerrain = function () {
// ----------------------------- 创建物理地形 ---------------------------------
TerrainMenu.prototype.createPhysicsTerrain = function () {
this.app.editor.execute(new AddObjectCommand(new PhysicsTerrain()));
var terrain = new PhysicsTerrain();
this.app.editor.execute(new AddObjectCommand(terrain));
this.app.physics.world.addRigidBody(terrain.userData.physicsBody);
this.app.physics.rigidBodies.push(terrain);
// this.app.on(`animate.${this.id}`, (clock, deltaTime) => {
// terrain.update(deltaTime, this.app.physics.world);
// });
};
// ---------------------------- 升高地形 -----------------------------------

View File

@ -20,7 +20,7 @@ function PhysicsTerrain() {
var heightData = this.generateHeight(terrainWidth, terrainDepth, terrainMinHeight, terrainMaxHeight);
for (var i = 0, j = 0, l = vertices.length; i < l; i++, j += 3) {
for (var i = 0, j = 0, l = vertices.length; i < l; i++ , j += 3) {
// j + 1 because it is the y component that we modify
vertices[j + 1] = heightData[i];
}
@ -35,6 +35,7 @@ function PhysicsTerrain() {
// 创建网格
THREE.Mesh.call(this, geometry, material);
this.name = '地形';
this.castShadow = true;
this.receiveShadow = true;
@ -47,6 +48,21 @@ function PhysicsTerrain() {
material.map = texture;
material.needsUpdate = true;
});
// 创建地形刚体
var groundShape = this.createTerrainShape(terrainWidth, terrainDepth, terrainWidthExtents, terrainDepthExtents, heightData, terrainMinHeight, terrainMaxHeight);
var groundTransform = new Ammo.btTransform();
groundTransform.setIdentity();
// 由于子弹将其重新定位在其边界框上,因此会改变地形。
groundTransform.setOrigin(new Ammo.btVector3(0, (terrainMinHeight + terrainMaxHeight) / 2, 0));
// 初始化刚体参数
var groundMass = 0;
var groundLocalInertia = new Ammo.btVector3(0, 0, 0); // 惯性
var groundMotionState = new Ammo.btDefaultMotionState(groundTransform);
var groundBody = new Ammo.btRigidBody(new Ammo.btRigidBodyConstructionInfo(groundMass, groundMotionState, groundShape, groundLocalInertia));
this.userData.physicsBody = groundBody;
}
PhysicsTerrain.prototype = Object.create(THREE.Mesh.prototype);
@ -78,222 +94,74 @@ PhysicsTerrain.prototype.generateHeight = function (width, depth, minHeight, max
return data;
};
export default PhysicsTerrain;
/**
* 创建物理地形形状
* @param {*} terrainWidth
* @param {*} terrainDepth
* @param {*} terrainWidthExtents
* @param {*} terrainDepthExtents
* @param {*} heightData
* @param {*} terrainMinHeight
* @param {*} terrainMaxHeight
*/
PhysicsTerrain.prototype.createTerrainShape = function (terrainWidth, terrainDepth, terrainWidthExtents, terrainDepthExtents, heightData, terrainMinHeight, terrainMaxHeight) {
// 此参数并未真正使用因为我们使用的是PHY_FLOAT高度数据类型因此会被忽略。
var heightScale = 1;
// 向上轴 0表示X1表示Y2表示Z。通常使用1=Y。
var upAxis = 1;
// hdt高度数据类型。 使用“PHY_FLOAT”。 可能的值为“PHY_FLOAT”“PHY_UCHAR”“PHY_SHORT”。
var hdt = "PHY_FLOAT";
// 根据您的需要设置(反转三角形)。
var flipQuadEdges = false;
// 在Ammo堆中创建高度数据缓冲区。
var ammoHeightData = Ammo._malloc(4 * terrainWidth * terrainDepth);
// 将javascript高度数据数组复制到Ammo。
var p = 0;
var p2 = 0;
for (var j = 0; j < terrainDepth; j++) {
for (var i = 0; i < terrainWidth; i++) {
// 将32位浮点数写入内存。
Ammo.HEAPF32[ammoHeightData + p2 >> 2] = heightData[p];
p++;
// 4个字节/浮点数
p2 += 4;
}
}
// 创建高度场物理形状
var heightFieldShape = new Ammo.btHeightfieldTerrainShape(
terrainWidth,
terrainDepth,
ammoHeightData,
heightScale,
terrainMinHeight,
terrainMaxHeight,
upAxis,
hdt,
flipQuadEdges
);
// 设置水平缩放
var scaleX = terrainWidthExtents / (terrainWidth - 1);
var scaleZ = terrainDepthExtents / (terrainDepth - 1);
heightFieldShape.setLocalScaling(new Ammo.btVector3(scaleX, 1, scaleZ));
heightFieldShape.setMargin(0.05);
return heightFieldShape;
};
// function init() {
// heightData = generateHeight(terrainWidth, terrainDepth, terrainMinHeight, terrainMaxHeight);
// initGraphics();
// initPhysics();
// }
PhysicsTerrain.prototype.update = function (deltaTime, physicsWorld) {
physicsWorld.stepSimulation(deltaTime, 10);
// function initGraphics() {
// var geometry = new THREE.PlaneBufferGeometry(terrainWidthExtents, terrainDepthExtents, terrainWidth - 1, terrainDepth - 1);
// geometry.rotateX(-Math.PI / 2);
// var vertices = geometry.attributes.position.array;
// for (var i = 0, j = 0, l = vertices.length; i < l; i++, j += 3) {
// // j + 1 because it is the y component that we modify
// vertices[j + 1] = heightData[i];
// }
// geometry.computeVertexNormals();
// var groundMaterial = new THREE.MeshPhongMaterial({
// color: 0xC7C7C7
// });
// terrainMesh = new THREE.Mesh(geometry, groundMaterial);
// terrainMesh.receiveShadow = true;
// terrainMesh.castShadow = true;
// scene.add(terrainMesh);
// var textureLoader = new THREE.TextureLoader();
// textureLoader.load("textures/grid.png", function (texture) {
// texture.wrapS = THREE.RepeatWrapping;
// texture.wrapT = THREE.RepeatWrapping;
// texture.repeat.set(terrainWidth - 1, terrainDepth - 1);
// groundMaterial.map = texture;
// groundMaterial.needsUpdate = true;
// });
// }
var body = this.userData.physics.body;
var state = body.getMotionState();
// 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, -6, 0));
// // Create the terrain body
// var groundShape = createTerrainShape();
// var groundTransform = new Ammo.btTransform();
// groundTransform.setIdentity();
// // Shifts the terrain, since bullet re-centers it on its bounding box.
// groundTransform.setOrigin(new Ammo.btVector3(0, (terrainMaxHeight + terrainMinHeight) / 2, 0));
// var groundMass = 0;
// var groundLocalInertia = new Ammo.btVector3(0, 0, 0);
// var groundMotionState = new Ammo.btDefaultMotionState(groundTransform);
// var groundBody = new Ammo.btRigidBody(new Ammo.btRigidBodyConstructionInfo(groundMass, groundMotionState, groundShape, groundLocalInertia));
// physicsWorld.addRigidBody(groundBody);
// }
if (state) {
var transformAux1 = new Ammo.btTransform();
// function generateHeight(width, depth, minHeight, maxHeight) {
// // Generates the height data (a sinus wave)
// var size = width * depth;
// var data = new Float32Array(size);
// var hRange = maxHeight - minHeight;
// var w2 = width / 2;
// var d2 = depth / 2;
// var phaseMult = 12;
// var p = 0;
// for (var j = 0; j < depth; j++) {
// for (var i = 0; i < width; i++) {
// var radius = Math.sqrt(
// Math.pow((i - w2) / w2, 2.0) +
// Math.pow((j - d2) / d2, 2.0));
// var height = (Math.sin(radius * phaseMult) + 1) * 0.5 * hRange + minHeight;
// data[p] = height;
// p++;
// }
// }
// return data;
// }
state.getWorldTransform(transformAux1);
var p = transformAux1.getOrigin();
var q = transformAux1.getRotation();
this.position.set(p.x(), p.y(), p.z());
this.quaternion.set(q.x(), q.y(), q.z(), q.w());
}
};
// function createTerrainShape() {
// // This parameter is not really used, since we are using PHY_FLOAT height data type and hence it is ignored
// var heightScale = 1;
// // Up axis = 0 for X, 1 for Y, 2 for Z. Normally 1 = Y is used.
// var upAxis = 1;
// // hdt, height data type. "PHY_FLOAT" is used. Possible values are "PHY_FLOAT", "PHY_UCHAR", "PHY_SHORT"
// var hdt = "PHY_FLOAT";
// // Set this to your needs (inverts the triangles)
// var flipQuadEdges = false;
// // Creates height data buffer in Ammo heap
// ammoHeightData = Ammo._malloc(4 * terrainWidth * terrainDepth);
// // Copy the javascript height data array to the Ammo one.
// var p = 0;
// var p2 = 0;
// for (var j = 0; j < terrainDepth; j++) {
// for (var i = 0; i < terrainWidth; i++) {
// // write 32-bit float data to memory
// Ammo.HEAPF32[ammoHeightData + p2 >> 2] = heightData[p];
// p++;
// // 4 bytes/float
// p2 += 4;
// }
// }
// // Creates the heightfield physics shape
// var heightFieldShape = new Ammo.btHeightfieldTerrainShape(
// terrainWidth,
// terrainDepth,
// ammoHeightData,
// heightScale,
// terrainMinHeight,
// terrainMaxHeight,
// upAxis,
// hdt,
// flipQuadEdges
// );
// // Set horizontal scale
// var scaleX = terrainWidthExtents / (terrainWidth - 1);
// var scaleZ = terrainDepthExtents / (terrainDepth - 1);
// heightFieldShape.setLocalScaling(new Ammo.btVector3(scaleX, 1, scaleZ));
// heightFieldShape.setMargin(0.05);
// return heightFieldShape;
// }
// function generateObject() {
// var numTypes = 4;
// var objectType = Math.ceil(Math.random() * numTypes);
// var threeObject = null;
// var shape = null;
// var objectSize = 3;
// var margin = 0.05;
// switch (objectType) {
// case 1:
// // Sphere
// var radius = 1 + Math.random() * objectSize;
// threeObject = new THREE.Mesh(new THREE.SphereBufferGeometry(radius, 20, 20), createObjectMaterial());
// shape = new Ammo.btSphereShape(radius);
// shape.setMargin(margin);
// break;
// case 2:
// // Box
// var sx = 1 + Math.random() * objectSize;
// var sy = 1 + Math.random() * objectSize;
// var sz = 1 + Math.random() * objectSize;
// threeObject = new THREE.Mesh(new THREE.BoxBufferGeometry(sx, sy, sz, 1, 1, 1), createObjectMaterial());
// shape = new Ammo.btBoxShape(new Ammo.btVector3(sx * 0.5, sy * 0.5, sz * 0.5));
// shape.setMargin(margin);
// break;
// case 3:
// // Cylinder
// var radius = 1 + Math.random() * objectSize;
// var height = 1 + Math.random() * objectSize;
// threeObject = new THREE.Mesh(new THREE.CylinderBufferGeometry(radius, radius, height, 20, 1), createObjectMaterial());
// shape = new Ammo.btCylinderShape(new Ammo.btVector3(radius, height * 0.5, radius));
// shape.setMargin(margin);
// break;
// default:
// // Cone
// var radius = 1 + Math.random() * objectSize;
// var height = 2 + Math.random() * objectSize;
// threeObject = new THREE.Mesh(new THREE.ConeBufferGeometry(radius, height, 20, 2), createObjectMaterial());
// shape = new Ammo.btConeShape(radius, height);
// break;
// }
// threeObject.position.set((Math.random() - 0.5) * terrainWidth * 0.6, terrainMaxHeight + objectSize + 2, (Math.random() - 0.5) * terrainDepth * 0.6);
// var mass = objectSize * 5;
// var localInertia = new Ammo.btVector3(0, 0, 0);
// shape.calculateLocalInertia(mass, localInertia);
// var transform = new Ammo.btTransform();
// transform.setIdentity();
// var pos = threeObject.position;
// transform.setOrigin(new Ammo.btVector3(pos.x, pos.y, pos.z));
// var motionState = new Ammo.btDefaultMotionState(transform);
// var rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, motionState, shape, localInertia);
// var body = new Ammo.btRigidBody(rbInfo);
// threeObject.userData.physicsBody = body;
// threeObject.receiveShadow = true;
// threeObject.castShadow = true;
// scene.add(threeObject);
// dynamicObjects.push(threeObject);
// physicsWorld.addRigidBody(body);
// }
// function createObjectMaterial() {
// var c = Math.floor(Math.random() * (1 << 24));
// return new THREE.MeshPhongMaterial({
// color: c
// });
// }
// function animate() {
// requestAnimationFrame(animate);
// render();
// stats.update();
// }
// function render() {
// var deltaTime = clock.getDelta();
// if (dynamicObjects.length < maxNumObjects && time > timeNextSpawn) {
// generateObject();
// timeNextSpawn = time + objectTimePeriod;
// }
// updatePhysics(deltaTime);
// renderer.render(scene, camera);
// time += deltaTime;
// }
// function updatePhysics(deltaTime) {
// physicsWorld.stepSimulation(deltaTime, 10);
// // Update objects
// for (var i = 0, il = dynamicObjects.length; i < il; i++) {
// var objThree = dynamicObjects[i];
// var objPhys = objThree.userData.physicsBody;
// var ms = objPhys.getMotionState();
// if (ms) {
// ms.getWorldTransform(transformAux1);
// var p = transformAux1.getOrigin();
// var q = transformAux1.getRotation();
// objThree.position.set(p.x(), p.y(), p.z());
// objThree.quaternion.set(q.x(), q.y(), q.z(), q.w());
// }
// }
// }
export default PhysicsTerrain;