zoukankan      html  css  js  c++  java
  • 【three.js练习程序】创建简单物理地形

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <title>ceshi</title>
        <style>
            body
            {
                margin: 0;
                overflow: hidden;
            }
        </style>
        <script src="./build/three.js"></script>
        <script src="./examples/js/libs/ammo.js"></script>
        <script src="./examples/js/controls/OrbitControls.js"></script>
        <script src="./examples/js/ImprovedNoise.js"></script>
    </head>
    <body>
        <div id="ThreeJs">
        </div>
        <script>
            var camera, controls, scene, renderer;
            var clock = new THREE.Clock();
    
            // 物理引擎相关变量
            var gravityConstant = -9.8;
            var collisionConfiguration;
            var dispatcher;
            var broadphase;
            var solver;
            var physicsWorld;
            var rigidBodies = [];
            var margin = 0.05;
            var transformAux1 = new Ammo.btTransform();
            var time = 0;
    
            // 高度场相关
            var terrainWidthExtents = 50;
            var terrainDepthExtents = 50;
            var terrainWidth = 50;
            var terrainDepth = 50;
            var terrainHalfWidth = terrainWidth / 2;
            var terrainHalfDepth = terrainDepth / 2;
            var terrainMaxHeight = 50;
            var terrainMinHeight = -20;
            var heightData = null;
            var ammoHeightData = null;
    
            init();
            animate();
    
            function init() {
    
                heightData = generateHeight(terrainWidth, terrainDepth);
    
                initGraphics();
                initPhysics();
                createObjects();
            }
    
            function initGraphics() {
                // three.js基本场景配置
                camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 2000);
                camera.position.x = 50;
                camera.position.y = 50;
                camera.position.z = 50;
    
                controls = new THREE.OrbitControls(camera);
                controls.target.y = 2;
    
                renderer = new THREE.WebGLRenderer();
                renderer.setClearColor(new THREE.Color("#bfd1e5"));
                renderer.shadowMapEnabled = true;
                renderer.setSize(window.innerWidth, window.innerHeight);
                // 场景
                scene = new THREE.Scene();
                // 环境光
                var ambientLight = new THREE.AmbientLight(0x404040);
                scene.add(ambientLight);
                // 线性光
                var light = new THREE.DirectionalLight(0xffffff, 1);
                light.position.set(-20, 20, 10);
                light.castShadow = true;
                var d = 50;
                light.shadow.camera.left = -d;
                light.shadow.camera.right = d;
                light.shadow.camera.top = d;
                light.shadow.camera.bottom = -d;
    
                light.shadow.camera.near = 2;
                light.shadow.camera.far = 50;
    
                light.shadow.mapSize.x = 1024;
                light.shadow.mapSize.y = 1024;
                scene.add(light);
    
                var axes = new THREE.AxisHelper(50);               //创建三轴表示
                scene.add(axes);
                // 添加窗口大小变化监听
                window.addEventListener('resize', onWindowResize, false);
            }
    
            function onWindowResize() {
                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();
                renderer.setSize(window.innerWidth, window.innerHeight);
            }
    
            function initPhysics() {
                // bullet基本场景配置
                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 createObjects() {
                var pos = new THREE.Vector3();
                var quat = new THREE.Quaternion();
    
                //创建物理地形
                var geometry = new THREE.PlaneBufferGeometry(50, 50, 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 groundShape = createTerrainShape(heightData);
                var groundTransform = new Ammo.btTransform();
                groundTransform.setIdentity();
                // 设置bullet计算时物体中心
                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);
    
                //创建50个小球
                for (var i = 0; i < 50; i++) {
                    var ballMass = 1.2;
                    var ballRadius = 0.5;
    
                    var ball = new THREE.Mesh(new THREE.SphereGeometry(ballRadius, 20, 20), createRendomColorObjectMeatrial());
                    ball.castShadow = true;
                    ball.receiveShadow = true;
                    var ballShape = new Ammo.btSphereShape(ballRadius);
                    ballShape.setMargin(margin);
                    pos.set(Math.random() + 10, 3 * (i + 1) + 20, Math.random() - 10);
                    quat.set(0, 0, 0, 1);
                    createRigidBody(ball, ballShape, ballMass, pos, quat);
                    ball.userData.physicsBody.setFriction(1.5);
                }
    
                //创建50个方块
                for (var i = 0; i < 50; i++) {
                    pos.set(Math.random() - 10, 3 * (i + 1) + 20, Math.random() + 10);
                    quat.set(0, 0, 0, 1);
                    createParallellepiped(1, 1, 1, 1, pos, quat, createRendomColorObjectMeatrial());
                }
            }
    
            function createRendomColorObjectMeatrial() {
                var color = Math.floor(Math.random() * (1 << 24));
                return new THREE.MeshPhongMaterial({ color: color });
            }
    
            function createParallellepiped(sx, sy, sz, mass, pos, quat, material) {
                var threeObject = new THREE.Mesh(new THREE.BoxGeometry(sx, sy, sz, 1, 1, 1), material);
                threeObject.castShadow = true;
                threeObject.receiveShadow = true;
                var 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.copy(quat);
                var 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));
                var motionState = new Ammo.btDefaultMotionState(transform);
                var localInertia = new Ammo.btVector3(0, 0, 0);
                physicsShape.calculateLocalInertia(mass, localInertia);
                var rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, motionState, physicsShape, localInertia);
                var body = new Ammo.btRigidBody(rbInfo);
                threeObject.userData.physicsBody = body;
                scene.add(threeObject);
                if (mass > 0) {
                    rigidBodies.push(threeObject);
                    body.setActivationState(4);
                }
                physicsWorld.addRigidBody(body);
                return body;
            }
    
            function animate() {
                requestAnimationFrame(animate);
                var deltaTime = clock.getDelta();
                updatePhysics(deltaTime);
                controls.update(deltaTime);
                renderer.render(scene, camera);
                time += deltaTime;
            }
    
            function updatePhysics(deltaTime) {
                physicsWorld.stepSimulation(deltaTime);
                // 更新物体位置
                for (var i = 0, iL = rigidBodies.length; i < iL; i++) {
                    var objThree = rigidBodies[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());
                    }
                }
            }
    
            function generateHeight(width, height) {
                var size = width * height, data = new Float32Array(size),
                    perlin = new ImprovedNoise(), quality = 1, z = Math.random() * 10;
                for (var j = 0; j < 4; j++) {
                    for (var i = 0; i < size; i++) {
                        var x = i % width, y = ~ ~(i / width);
                        data[i] += Math.abs(perlin.noise(x / quality, y / quality, z) * quality);
                    }
                    quality *= 3;
                }
                return data;
            }
    
            // 生成物理引擎用高度场
            function createTerrainShape(heightData) {
                // 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;
            }
    
            document.getElementById("ThreeJs").appendChild(renderer.domElement);
        </script>
    </html>

  • 相关阅读:
    【CF 359B】Permutation
    如何更新 DevC++ 的编译器
    【LG 2801】教主的魔法
    矩阵浅谈
    NOI 系列赛常见技术问题整理
    Treap 浅谈
    DP 优化浅谈
    友链
    【CF 708C】Centroids
    我跳过的坑
  • 原文地址:https://www.cnblogs.com/tiandsp/p/8455228.html
Copyright © 2011-2022 走看看