前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >微信小程序体验3D物理引擎-ammo.js

微信小程序体验3D物理引擎-ammo.js

作者头像
周星星9527
发布2020-05-31 10:11:41
3.3K0
发布2020-05-31 10:11:41
举报

效果如下:

点击体验3D物理引擎bullet的javascript版本。源码参考了:https://github.com/THISISAGOODNAME/learn-ammojs,感谢原作者!

代码语言:javascript
复制
<html lang="en">  <head>    <title>Ammo.js softbody volume demo</title>    <meta charset="utf-8">    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">    <link type="text/css" rel="stylesheet" href="main.css">    <style>      body {        color: #333;      }</style>  </head>  <body>    <div id="info">      Ammo.js physics soft body volume demo<br/>      Click to throw a ball    </div>    <div id="container"></div>
    <script src="js/libs/ammo.js"></script>
    <script type="module">
      import * as THREE from '../build/three.module.js';
      import Stats from './jsm/libs/stats.module.js';
      import { OrbitControls } from './jsm/controls/OrbitControls.js';      import { BufferGeometryUtils } from './jsm/utils/BufferGeometryUtils.js';
      // Graphics variables      var container, stats;      var camera, controls, scene, renderer;      var textureLoader;      var clock = new THREE.Clock();      var clickRequest = false;      var mouseCoords = new THREE.Vector2();      var raycaster = new THREE.Raycaster();      var ballMaterial = new THREE.MeshPhongMaterial( { color: 0x202020 } );      var pos = new THREE.Vector3();      var quat = new THREE.Quaternion();
      // Physics variables      var gravityConstant = - 9.8;      var physicsWorld;      var rigidBodies = [];      var softBodies = [];      var margin = 0.05;      var transformAux1;      var softBodyHelpers;
      Ammo().then( function ( AmmoLib ) {
        Ammo = AmmoLib;
        init();        animate();
      } );
      function init() {
        initGraphics();
        initPhysics();
        createObjects();
        initInput();
      }
      function initGraphics() {
        container = document.getElementById( 'container' );
        camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.2, 2000 );
        scene = new THREE.Scene();        scene.background = new THREE.Color( 0xbfd1e5 );
        camera.position.set( - 7, 5, 8 );
        renderer = new THREE.WebGLRenderer();        renderer.setPixelRatio( window.devicePixelRatio );        renderer.setSize( window.innerWidth, window.innerHeight );        renderer.shadowMap.enabled = true;        container.appendChild( renderer.domElement );
        controls = new OrbitControls( camera, renderer.domElement );        controls.target.set( 0, 2, 0 );        controls.update();
        textureLoader = new THREE.TextureLoader();
        var ambientLight = new THREE.AmbientLight( 0x404040 );        scene.add( ambientLight );
        var light = new THREE.DirectionalLight( 0xffffff, 1 );        light.position.set( - 10, 10, 5 );        light.castShadow = true;        var d = 20;        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 );
        stats = new Stats();        stats.domElement.style.position = 'absolute';        stats.domElement.style.top = '0px';        container.appendChild( stats.domElement );

        window.addEventListener( 'resize', onWindowResize, false );
      }
      function initPhysics() {
        // Physics configuration
        var collisionConfiguration = new Ammo.btSoftBodyRigidBodyCollisionConfiguration();        var dispatcher = new Ammo.btCollisionDispatcher( collisionConfiguration );        var broadphase = new Ammo.btDbvtBroadphase();        var solver = new Ammo.btSequentialImpulseConstraintSolver();        var 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 ) );
        transformAux1 = new Ammo.btTransform();        softBodyHelpers = new Ammo.btSoftBodyHelpers();
      }
      function createObjects() {
        // Ground        pos.set( 0, - 0.5, 0 );        quat.set( 0, 0, 0, 1 );        var ground = createParalellepiped( 40, 1, 40, 0, pos, quat, new THREE.MeshPhongMaterial( { color: 0xFFFFFF } ) );        ground.castShadow = true;        ground.receiveShadow = true;        textureLoader.load( "textures/grid.png", function ( texture ) {
          texture.wrapS = THREE.RepeatWrapping;          texture.wrapT = THREE.RepeatWrapping;          texture.repeat.set( 40, 40 );          ground.material.map = texture;          ground.material.needsUpdate = true;
        } );
        // Create soft volumes        var volumeMass = 15;
        var sphereGeometry = new THREE.SphereBufferGeometry( 1.5, 40, 25 );        sphereGeometry.translate( 5, 5, 0 );        createSoftVolume( sphereGeometry, volumeMass, 250 );
        var boxGeometry = new THREE.BoxBufferGeometry( 1, 1, 5, 4, 4, 20 );        boxGeometry.translate( - 2, 5, 0 );        createSoftVolume( boxGeometry, volumeMass, 120 );
        // Ramp        pos.set( 3, 1, 0 );        quat.setFromAxisAngle( new THREE.Vector3( 0, 0, 1 ), 30 * Math.PI / 180 );        var obstacle = createParalellepiped( 10, 1, 4, 0, pos, quat, new THREE.MeshPhongMaterial( { color: 0x606060 } ) );        obstacle.castShadow = true;        obstacle.receiveShadow = true;
      }
      function processGeometry( bufGeometry ) {
        // Ony consider the position values when merging the vertices        var posOnlyBufGeometry = new THREE.BufferGeometry();        posOnlyBufGeometry.setAttribute( 'position', bufGeometry.getAttribute( 'position' ) );        posOnlyBufGeometry.setIndex( bufGeometry.getIndex() );
        // Merge the vertices so the triangle soup is converted to indexed triangles        var indexedBufferGeom = BufferGeometryUtils.mergeVertices( posOnlyBufGeometry );
        // Create index arrays mapping the indexed vertices to bufGeometry vertices        mapIndices( bufGeometry, indexedBufferGeom );
      }
      function isEqual( x1, y1, z1, x2, y2, z2 ) {
        var delta = 0.000001;        return Math.abs( x2 - x1 ) < delta &&            Math.abs( y2 - y1 ) < delta &&            Math.abs( z2 - z1 ) < delta;
      }
      function mapIndices( bufGeometry, indexedBufferGeom ) {
        // Creates ammoVertices, ammoIndices and ammoIndexAssociation in bufGeometry
        var vertices = bufGeometry.attributes.position.array;        var idxVertices = indexedBufferGeom.attributes.position.array;        var indices = indexedBufferGeom.index.array;
        var numIdxVertices = idxVertices.length / 3;        var numVertices = vertices.length / 3;
        bufGeometry.ammoVertices = idxVertices;        bufGeometry.ammoIndices = indices;        bufGeometry.ammoIndexAssociation = [];
        for ( var i = 0; i < numIdxVertices; i ++ ) {
          var association = [];          bufGeometry.ammoIndexAssociation.push( association );
          var i3 = i * 3;
          for ( var j = 0; j < numVertices; j ++ ) {
            var j3 = j * 3;            if ( isEqual( idxVertices[ i3 ], idxVertices[ i3 + 1 ], idxVertices[ i3 + 2 ],              vertices[ j3 ], vertices[ j3 + 1 ], vertices[ j3 + 2 ] ) ) {
              association.push( j3 );
            }
          }
        }
      }
      function createSoftVolume( bufferGeom, mass, pressure ) {
        processGeometry( bufferGeom );
        var volume = new THREE.Mesh( bufferGeom, new THREE.MeshPhongMaterial( { color: 0xFFFFFF } ) );        volume.castShadow = true;        volume.receiveShadow = true;        volume.frustumCulled = false;        scene.add( volume );
        textureLoader.load( "textures/colors.png", function ( texture ) {
          volume.material.map = texture;          volume.material.needsUpdate = true;
        } );
        // Volume physic object
        var volumeSoftBody = softBodyHelpers.CreateFromTriMesh(          physicsWorld.getWorldInfo(),          bufferGeom.ammoVertices,          bufferGeom.ammoIndices,          bufferGeom.ammoIndices.length / 3,          true );
        var sbConfig = volumeSoftBody.get_m_cfg();        sbConfig.set_viterations( 40 );        sbConfig.set_piterations( 40 );
        // Soft-soft and soft-rigid collisions        sbConfig.set_collisions( 0x11 );
        // Friction        sbConfig.set_kDF( 0.1 );        // Damping        sbConfig.set_kDP( 0.01 );        // Pressure        sbConfig.set_kPR( pressure );        // Stiffness        volumeSoftBody.get_m_materials().at( 0 ).set_m_kLST( 0.9 );        volumeSoftBody.get_m_materials().at( 0 ).set_m_kAST( 0.9 );
        volumeSoftBody.setTotalMass( mass, false );        Ammo.castObject( volumeSoftBody, Ammo.btCollisionObject ).getCollisionShape().setMargin( margin );        physicsWorld.addSoftBody( volumeSoftBody, 1, - 1 );        volume.userData.physicsBody = volumeSoftBody;        // Disable deactivation        volumeSoftBody.setActivationState( 4 );
        softBodies.push( volume );
      }
      function createParalellepiped( sx, sy, sz, mass, pos, quat, material ) {
        var threeObject = new THREE.Mesh( new THREE.BoxBufferGeometry( sx, sy, sz, 1, 1, 1 ), material );        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 );
          // Disable deactivation          body.setActivationState( 4 );
        }
        physicsWorld.addRigidBody( body );
        return body;
      }
      function initInput() {
        window.addEventListener( 'mousedown', function ( event ) {
          if ( ! clickRequest ) {
            mouseCoords.set(              ( event.clientX / window.innerWidth ) * 2 - 1,              - ( event.clientY / window.innerHeight ) * 2 + 1            );
            clickRequest = true;
          }
        }, false );
      }
      function processClick() {
        if ( clickRequest ) {
          raycaster.setFromCamera( mouseCoords, camera );
          // Creates a ball          var ballMass = 3;          var ballRadius = 0.4;
          var ball = new THREE.Mesh( new THREE.SphereBufferGeometry( ballRadius, 18, 16 ), ballMaterial );          ball.castShadow = true;          ball.receiveShadow = true;          var ballShape = new Ammo.btSphereShape( ballRadius );          ballShape.setMargin( margin );          pos.copy( raycaster.ray.direction );          pos.add( raycaster.ray.origin );          quat.set( 0, 0, 0, 1 );          var ballBody = createRigidBody( ball, ballShape, ballMass, pos, quat );          ballBody.setFriction( 0.5 );
          pos.copy( raycaster.ray.direction );          pos.multiplyScalar( 14 );          ballBody.setLinearVelocity( new Ammo.btVector3( pos.x, pos.y, pos.z ) );
          clickRequest = false;
        }
      }
      function onWindowResize() {
        camera.aspect = window.innerWidth / window.innerHeight;        camera.updateProjectionMatrix();
        renderer.setSize( window.innerWidth, window.innerHeight );
      }
      function animate() {
        requestAnimationFrame( animate );
        render();        stats.update();
      }
      function render() {
        var deltaTime = clock.getDelta();
        updatePhysics( deltaTime );
        processClick();
        renderer.render( scene, camera );
      }
      function updatePhysics( deltaTime ) {
        // Step world        physicsWorld.stepSimulation( deltaTime, 10 );
        // Update soft volumes        for ( var i = 0, il = softBodies.length; i < il; i ++ ) {
          var volume = softBodies[ i ];          var geometry = volume.geometry;          var softBody = volume.userData.physicsBody;          var volumePositions = geometry.attributes.position.array;          var volumeNormals = geometry.attributes.normal.array;          var association = geometry.ammoIndexAssociation;          var numVerts = association.length;          var nodes = softBody.get_m_nodes();          for ( var j = 0; j < numVerts; j ++ ) {
            var node = nodes.at( j );            var nodePos = node.get_m_x();            var x = nodePos.x();            var y = nodePos.y();            var z = nodePos.z();            var nodeNormal = node.get_m_n();            var nx = nodeNormal.x();            var ny = nodeNormal.y();            var nz = nodeNormal.z();
            var assocVertex = association[ j ];
            for ( var k = 0, kl = assocVertex.length; k < kl; k ++ ) {
              var indexVertex = assocVertex[ k ];              volumePositions[ indexVertex ] = x;              volumeNormals[ indexVertex ] = nx;              indexVertex ++;              volumePositions[ indexVertex ] = y;              volumeNormals[ indexVertex ] = ny;              indexVertex ++;              volumePositions[ indexVertex ] = z;              volumeNormals[ indexVertex ] = nz;
            }
          }
          geometry.attributes.position.needsUpdate = true;          geometry.attributes.normal.needsUpdate = true;
        }
        // Update rigid bodies        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() );
          }
        }
      }</script>
  </body></html>

先不说了,广告时间又到了,现在植入广告:几个《传热学》相关的小程序总结如下,可在微信中点击体验:

  1. 有限元三角单元网格自动剖分
  2. Delaunay三角化初体验 (理论戳这)
  3. Contour等值线绘制 (理论戳这)
  4. 2D非稳态温度场有限元分析
  5. 1D稳态导热温度场求解 (源码戳这)
  6. 1D非稳态导热温度场求解程序 (源码戳这)
  7. 2D稳态导热温度场求解 (源码戳这)
  8. 普朗克黑体单色辐射力

《传热学》相关小程序演示动画如下(其中下图1D非稳态导热计算发散,调小时间步长后重新计算,结果收敛!):

黑体单色辐射力如下图,可见温度越高,同频率辐射力越大:

《(计算)流体力学》中的几个小程序,可在微信中点击体验:

  1. Blasius偏微分方程求解速度边界层理论这里
  2. 理想流体在管道中的有势流动源码戳这
  3. 涡量-流函数法求解顶驱方腔流动源码戳这
  4. SIMPLE算法求解顶驱方腔流动源码戳这
  5. Lattice Boltzmann Method计算绕流演示参考源码
  6. 流体力学实验在线演示进行演示

关于《(计算)流体力学》相关的几个小程序演示动画如下:

LBM(=Lattice Boltzmann Method)计算得到的圆柱绕流“卡门涡街”演示(由于网格较少,分辨率低,圆柱近乎正方形):

顺便,《(热工过程)自动控制》中关于PID控制器的仿真可点击此处体验:PID控制演示小程序,(PID控制相关视频见:基础/整定/重要补充)。动画如下:

(正文完!)

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-05-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 传输过程数值模拟学习笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云开发 CloudBase
云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档