end0tknr's kipple - web写経開発

太宰府天満宮の狛犬って、妙にカワイイ

hands-on three.js

hands-on three.js + cannon.js - end0tknr's kipple - web写経開発 の更に前段です。

目次

1. cubeのグルーピング

以下の実際の動作は、github pagesをご覧ください。 https://end0tknr.github.io/sandbox/threejs_cannonjs_misc/test_threejs.html

f:id:end0tknr:20210813075300p:plain

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/109/three.min.js"></script>
  <script src="https://cdn.jsdelivr.net/gh/mrdoob/three.js@r109/examples/js/controls/OrbitControls.js"></script>
  
</head>
<body>
  <div id="stage"></div>

  <script>
  (function() {
    'use strict';

    var width = 500;
    var height = 250;

      var scene = new THREE.Scene();

      // mesh 物体
      var head = new THREE.Mesh(
          new THREE.BoxGeometry(20, 20, 20),
          new THREE.MeshLambertMaterial({ color: 0xff0000 })
      );
      head.position.set(0, 40, 0);
      // scene.add(head);
      
      var body = new THREE.Mesh(
          new THREE.BoxGeometry(40, 60, 40),
          new THREE.MeshLambertMaterial({ color: 0xff0000 })
      );
      body.position.set(0, 0, 0);
      // scene.add(body);

      // 頭部と体をグループ化
      var person = new THREE.Group();
      person.add(head);
      person.add(body);
      scene.add(person);

      // light
      var light = new THREE.DirectionalLight(0xffffff, 1);
      light.position.set(0, 100, 30);
      scene.add(light);
      // 環境光源
      var ambient = new THREE.AmbientLight(0x404040);
      scene.add(ambient);

      // camera
      var camera = new THREE.PerspectiveCamera(45, width / height, 1, 1000);
      camera.position.set(200, 100, 300);
      camera.lookAt(scene.position);

      // helper
      var gridHelper = new THREE.GridHelper(200, 50);
      scene.add(gridHelper);
      var axisHelper = new THREE.AxisHelper(1000);
      scene.add(axisHelper);
      var lightHelper = new THREE.DirectionalLightHelper(light, 20);
      scene.add(lightHelper);

        // controls - マウスによる操作
        var controls = new THREE.OrbitControls(camera);
        // controls.autoRotate = true;

      // renderer
      var renderer = new THREE.WebGLRenderer({ antialias: true });
      renderer.setSize(width, height);
      renderer.setClearColor(0xefefef);
      renderer.setPixelRatio(window.devicePixelRatio);
      document.getElementById('stage').appendChild(renderer.domElement);
      
      function render() {
          requestAnimationFrame(render);

          person.rotation.y += 0.01;

          controls.update();
          
          renderer.render(scene, camera);
      }
      render();
  })();
  </script>
  
</body>
</html>

2. 様々な形状の表示

https://ozateck.sakura.ne.jp/wordpress/category/three-js/

上記urlからの写経です。

https://end0tknr.github.io/sandbox/threejs_cannonjs_shimeji/index.html

f:id:end0tknr:20210816050639p:plain

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <!-- refer to https://ozateck.sakura.ne.jp/wordpress/ -->
  <title>Hello Three.js</title>
  <style>
    body{
    margin: 0;
    overflow: hidden;
    }
  </style>
</head>
<body>
  <div id="stage"></div>
  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/97/three.min.js"></script>
  <!-- 処理速度やフレームレートを確認する為 -->
  <script src="https://cdn.jsdelivr.net/npm/stats-js@1.0.1/build/stats.min.js"></script>
  <!-- マウスによるカメラ操作 -->
  <script
    src="https://cdn.jsdelivr.net/npm/three@0.105.2/examples/js/controls/TrackballControls.js">
  </script>
  <script src="https://cdn.jsdelivr.net/npm/cannon@0.6.2/build/cannon.min.js"></script>
  <script src="js/app.js"></script>
</body>
</html>
// refer to http://ozateck.sakura.ne.jp/wordpress

var width  = 480;
var height = 320;
var fov    = 60;
var aspect = width / height;
var near   = 1;
var far    = 1000;

//座標系   Z↑ /Y
//          │/
//         原点─→X

// Scene - 各オブジェクト(円、四角等)を表示
var scene = new THREE.Scene();
 
// Axes - 空間のx, y, zを原点から表す
var axes = new THREE.AxisHelper(20);
scene.add(axes);
 
// Camera - 空間の視点をを決める.
// コンストラクタの引数はそれぞれ、
// 視野角(fov)、縦横幅比率(aspect)、描画領域(近い方)、描画領域(遠い方)
var camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(0, 100, 200);
camera.lookAt(scene.position);

// Controls - マウスによる操作.
// google chromeで以下のerrorとなる場合、その更に下のurlが参考になります。
// [Intervention] Unable to preventDefault inside passive event listener
// due to target being treated as passive.
// https://note.com/cfbif/n/n92195df174bf
var controls = new THREE.TrackballControls(camera);
controls.rotateSpeed = 5.0; //回転速度
controls.zoomSpeed = 0.5;//ズーム速度
controls.panSpeed = 2.0;//パン速度


// Light - 空間への光の方向を決める
var directionalLight = new THREE.DirectionalLight(0xffffff);
directionalLight.position.set(0, 0.7, 0.7);
scene.add(directionalLight);
 
// Stats - 処理速度やフレームレートを確認する為
var stats = new Stats();
stats.setMode(0);
stats.domElement.style.position = "absolute";
stats.domElement.style.left = "0px";
stats.domElement.style.top  = "0px";
document.getElementById("stage").appendChild(stats.domElement);

// Plane

// Three.js のobjectには、Geometry, Material, Mesh を使う。
// Geometryはオブジェクトの形や大きさ. Materialにはオブジェクトの色や質感を表す。
// Meshオブジェクトに、これらの2つのオブジェクトを指定しインスタンス化

var geometry = new THREE.PlaneGeometry(200, 250);
var material = new THREE.MeshBasicMaterial({color: 0x666666});
var plane = new THREE.Mesh(geometry, material);
plane.position.set(0, 0, 0);
plane.rotation.set(-90 * Math.PI / 180, 0, 0);
scene.add(plane);


// Particles
var geometry = new THREE.Geometry();
var material = new THREE.PointsMaterial({color: 0xffffff,
                     size: 4,
                     vertexColors: true});
for(var x=0; x<10; x++){
    for(var y=0; y<10; y++){
    var particle = new THREE.Vector3(x*10, y*10, 0);
    geometry.vertices.push(particle);
    geometry.colors.push(new THREE.Color(Math.random() * 0x00ffff));
    }
}
// 複数の点が、1コとobjectとして扱われます
var points = new THREE.Points(geometry, material);
scene.add(points);


// Cubeを作成し、その後、順に削除
for(var i=0; i<20; i++){
    var x = getRandom(-80, 80);
    var y = getRandom(  0, 80);
    var z = getRandom(-80, 80);

    var mesh = new THREE.Mesh(
    new THREE.BoxBufferGeometry(10,10,10),
    new THREE.MeshBasicMaterial({color: 0xccffcc, wireframe: true}) );
    mesh.position.set(x, y, z);
    
    mesh.name = "tmpCube";
    scene.add(mesh);
}
 
function getRandom(min, max){
    return Math.floor(Math.random()*(max-min+1))+min;
}
 
function removeCube(){
    var total = scene.children.length;
    for(var i=0; i<total; i++){
    var obj = scene.children[i];
    if(obj.name == "tmpCube"){
        scene.remove(obj);
        return;
    }
    }
}
setInterval(removeCube, 2000);


// Cube
var geometry = new THREE.BoxGeometry(30, 30, 30);
var material = new THREE.MeshNormalMaterial();
var cube = new THREE.Mesh(geometry, material);
cube.position.set(0, 50, 0);
scene.add(cube);
 
// Earth
var txLoader = new THREE.TextureLoader();
var earth = null;
txLoader.load(
    "img/earth.jpg", function(texture){
    var geometry = new THREE.SphereGeometry(30, 30, 30);
    var material = new THREE.MeshBasicMaterial({map:texture, overdraw:0.5});
    earth = new THREE.Mesh(geometry, material);
    earth.position.set(-30, 50, 100);
    scene.add(earth);
    });

// Moon
var moon = null;
txLoader.load(
    "img/moon.jpg", function(texture){
    var geometry = new THREE.SphereGeometry(10, 10, 10);
    var material = new THREE.MeshBasicMaterial({map:texture, overdraw:0.5});
    moon = new THREE.Mesh(geometry, material);
    moon.position.set(50, 50, 50);
    // moon.position.set(100, 50, 0);
    scene.add(moon);
    });

// Renderer - 毎フレーム描画
var renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(width, height);
renderer.setClearColor(0xcccccc);
renderer.setPixelRatio(window.devicePixelRatio);
document.getElementById("stage").appendChild(renderer.domElement);
 
// Radian -  360 = 2 * PI  , 180 = PI
var radius = 50;
var degree = 0;

// Loop
loop();
function loop(){
    stats.update();
 
    // Earth, Moon を回転
    degree += 0.5;
    if(360 <= degree) degree = 0;
    
    var radian = degree * Math.PI / 180;
    var x = radius * Math.cos(radian);
    var y = radius * Math.sin(radian);
    if(earth != null){
    earth.rotation.set(0, radian, 0);
    }
    if(moon != null){
    moon.rotation.set(0, radian, 0);
    // 以下の x,y を入れ替えると、逆向きに回転します
    moon.position.set(y-30, 50, x+50);
    }
    // Cube を回転
    cube.rotation.x += 0.05;
    cube.rotation.y += 0.05;

    controls.update();
    
    renderer.render(scene, camera);
    window.requestAnimationFrame(loop ); //再帰呼び出し
};

3. 音声の可視化

https://ozateck.sakura.ne.jp/wordpress/category/three-js/

先程と同様、上記urlからの写経です。

ブラウザ経由で、PCのマイクを使用しますので、 PCに向かって、手を叩くと、緑色の部分に波形として現れます。

ただし、navigator.getUserMedia() は、depricated のようですので、 そのうち、navigator.mediaDevices.getUserMedia() へ書き換える必要があります。

https://end0tknr.github.io/sandbox/threejs_cannonjs_shimeji/index_audio.html

f:id:end0tknr:20210816050700p:plain

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <!-- refer to https://ozateck.sakura.ne.jp/wordpress/ -->
  <title>Hello Three.js</title>
  <style>
    body{
    margin: 0;
    overflow: hidden;
    }
  </style>
</head>
<body>
  <h1>マイクからの入力音声を波形として表示</h1>
  <div id="stage"></div>
  <audio muted></audio>
  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/97/three.min.js"></script>
  <!-- 処理速度やフレームレートを確認する為 -->
  <script src="https://cdn.jsdelivr.net/npm/stats-js@1.0.1/build/stats.min.js"></script>
  <!-- マウスによるカメラ操作 -->
  <script
    src="https://cdn.jsdelivr.net/npm/three@0.105.2/examples/js/controls/TrackballControls.js">
  </script>
  <script src="https://cdn.jsdelivr.net/npm/cannon@0.6.2/build/cannon.min.js"></script>
  
  <script src="js/app_audio.js"></script>
</body>
</html>
console.log("Hello Three.js!!");

var width  = 480;
var height = 320;
var fov    = 60;
var aspect = width / height;
var near   = 1;
var far    = 1000;

// Scene
var scene = new THREE.Scene();

// Axes
var axes = new THREE.AxisHelper(20);
scene.add(axes);

// Stats
var stats = new Stats();
stats.setMode(0);
stats.domElement.style.position = "absolute";
stats.domElement.style.left = "0px";
stats.domElement.style.top  = "0px";
document.getElementById("stage").appendChild(stats.domElement);

// Plane
var geometry = new THREE.PlaneGeometry(100, 200);
var material = new THREE.MeshBasicMaterial({color: 0x666666});
var plane = new THREE.Mesh(geometry, material);
plane.position.set(0, 0, 0);
plane.rotation.set(-90 * Math.PI / 180, 0, 0);
scene.add(plane);

// Camera
var camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(0, 100, 200);
camera.lookAt(scene.position);

// Light
var directionalLight = new THREE.DirectionalLight(0xffffff);
directionalLight.position.set(0, 0.7, 0.7);
scene.add(directionalLight);

// Renderer
var renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(width, height);
renderer.setClearColor(0x333333);
renderer.setPixelRatio(window.devicePixelRatio);
document.getElementById("stage").appendChild(renderer.domElement);

//==========
// Web Audio
navigator.getUserMedia = 
    navigator.getUserMedia || navigator.webkitGetUserMedia ||
    navigator.mozGetUserMedia || navigator.msGetUserMedia;

navigator.getUserMedia({audio : true}, onSuccess, onError);

function onSuccess(stream){
    console.log("onSuccess");

    // document.querySelector("audio").src = URL.createObjectURL(stream);
    // ↑old. ↓new
    document.querySelector("audio").src =
    window.URL.createObjectURL(new Blob([stream], {type: "application/zip"}))
    
    var audioContext = new AudioContext();
    var analyser     = audioContext.createAnalyser();
    var timeDomain   = new Float32Array(analyser.frequencyBinCount);
    var frequency    = new Uint8Array(analyser.frequencyBinCount);
    audioContext.createMediaStreamSource(stream).connect(analyser);

    console.log("frequency:" + frequency.length);

    loop();
    function loop(){
    analyser.getFloatTimeDomainData(timeDomain);
    analyser.getByteFrequencyData(frequency);
    // update
    updateThree(timeDomain, frequency);
    window.requestAnimationFrame(loop);
    }
}

function onError(e){
    console.log("onError:" + e);
}

// Points
var points;

// Draw
function updateThree(timeDomain, frequency){

    // Stats
    stats.update();

    // Remove Points
    if(points != null) scene.remove(points);
    
    // Particles
    var geom = new THREE.Geometry();
    var material = new THREE.PointsMaterial({
    color: 0xffffff, size: 2, vertexColors: true, transparent: true, opacity: 1.0
    });

    var paddingX = 2;  // パーティクルの間隔
    var offset   = 4;  // 1024を4分割
    var total    = Math.floor(frequency.length / offset);

    var minX     = total * paddingX / -2;  // 開始位置x
    var maxY     = 100;// 最大の高さy

    console.log(total);

    for(var i=0; i<total; i++){

    var particle = new THREE.Vector3(
        minX + i * paddingX,
        Math.max(0, frequency[i*offset] * maxY / 255), 0);
    geom.vertices.push(particle);
    
    var color = new THREE.Color(0x00ff00);
    color.setHSL(color.getHSL().h, color.getHSL().s, color.getHSL().l);
    geom.colors.push(color);
    }
    
    // Add Points
    points = new THREE.Points(geom, material);
    scene.add(points);
    
    // Render
    renderer.render(scene, camera);
}

4. 音声の可視化 その2

「音声の可視化 」の別バージョンです。

PCマイクに向かって、手をたたくと、cubeが追加されます。

反応が悪いようですが、src修正は実施していません。

https://end0tknr.github.io/sandbox/threejs_cannonjs_shimeji/index_audio_2.html

f:id:end0tknr:20210816050714p:plain

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <!-- refer to https://ozateck.sakura.ne.jp/wordpress/ -->
  <title>Hello Three.js</title>
  <style>
    body{
    margin: 0;
    overflow: hidden;
    }
  </style>
</head>
<body>
  <h1>マイクからの(拍手)入力で、Cubeを追加</h1>

  反応が悪いようですが、js srcは修正していません。
  
  <div id="stage"></div>
  <audio muted></audio>
  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/97/three.min.js"></script>
  <!-- 処理速度やフレームレートを確認する為 -->
  <script src="https://cdn.jsdelivr.net/npm/stats-js@1.0.1/build/stats.min.js"></script>
  <!-- マウスによるカメラ操作 -->
  <script
    src="https://cdn.jsdelivr.net/npm/three@0.105.2/examples/js/controls/TrackballControls.js">
  </script>
  <script src="https://cdn.jsdelivr.net/npm/cannon@0.6.2/build/cannon.min.js"></script>
  
  <script src="js/app_audio_2.js"></script>
</body>
</html>
console.log("Hello Three.js!!");

var width  = 480;
var height = 320;
var fov    = 60;
var aspect = width / height;
var near   = 1;
var far    = 1000;

// Scene
var scene = new THREE.Scene();

// Axes
var axes = new THREE.AxisHelper(20);
scene.add(axes);

// Stats
var stats = new Stats();
stats.setMode(0);
stats.domElement.style.position = "absolute";
stats.domElement.style.left = "0px";
stats.domElement.style.top  = "0px";
document.getElementById("stage").appendChild(stats.domElement);

// Plane
var geometry = new THREE.PlaneGeometry(100, 200);
var material = new THREE.MeshBasicMaterial({color: 0x666666});
var plane = new THREE.Mesh(geometry, material);
plane.position.set(0, 0, 0);
plane.rotation.set(-90 * Math.PI / 180, 0, 0);
scene.add(plane);

// Camera
var camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(0, 100, 200);
camera.lookAt(scene.position);

// Light
var directionalLight = new THREE.DirectionalLight(0xffffff);
directionalLight.position.set(0, 0.7, 0.7);
scene.add(directionalLight);

// Renderer
var renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(width, height);
renderer.setClearColor(0x333333);
renderer.setPixelRatio(window.devicePixelRatio);
document.getElementById("stage").appendChild(renderer.domElement);

//==========
// Web Audio
navigator.getUserMedia = 
    navigator.getUserMedia || navigator.webkitGetUserMedia || 
    navigator.mozGetUserMedia || navigator.msGetUserMedia;

navigator.getUserMedia({audio : true}, onSuccess, onError);

function onSuccess(stream){
    console.log("onSuccess");

    //document.querySelector("audio").src = URL.createObjectURL(stream);
    // ↑old. ↓new
    document.querySelector("audio").src =
    window.URL.createObjectURL(new Blob([stream], {type: "application/zip"}))

    
    var audioContext = new AudioContext();
    var analyser     = audioContext.createAnalyser();
    var timeDomain   = new Float32Array(analyser.frequencyBinCount);
    var frequency    = new Uint8Array(analyser.frequencyBinCount);
    audioContext.createMediaStreamSource(stream).connect(analyser);

    //==========
    // 拍手を検出するクラス
    // 第一引数:次の拍手までの待機時間(ミリ秒)
    // 第二引数:反応する拍手音量の閾値
    var cManager = new CrapManager(500, 2);

    loop();
    function loop(){
    // Analiser
    analyser.getFloatTimeDomainData(timeDomain);
    analyser.getByteFrequencyData(frequency);
    
    // Stats
    stats.update();

    // 拍手の判定

    if(cManager.trigger(timeDomain, frequency) == true){
        console.log("start addCube()");
        // Cubeを追加する
        addCube();
    }
    
    // Render
    renderer.render(scene, camera);
    // Request
    window.requestAnimationFrame(loop);
    }
}

function onError(e){
    console.log("onError:" + e);
}

//==========
// Cubeをランダムで配置する関数
function addCube(){
    var x = getRandom(-80, 80);
    var y = getRandom(0, 80);
    var z = getRandom(-80, 80);
    var cube = createCube(5, 0xffffff, x, y, z);
    scene.add(cube);
}

//==========
// Cubeを指定のサイズ、色、座標で作り出す関数
function createCube(size, color, x, y, z){
    var geometry = new THREE.BoxBufferGeometry(size, size, size);
    var material = new THREE.MeshBasicMaterial({color: 0xccffcc, wireframe: true});
    var mesh = new THREE.Mesh(geometry, material);
    mesh.position.set(x, y, z);
    return mesh;
}

function getRandom(min, max){
    return Math.floor(Math.random()*(max-min+1))+min;
}

//==========
// CrapManager
class CrapManager{
    constructor(interTime, interThreshold){
    this.interTime      = interTime;
    this.interThreshold = interThreshold;
    this.interFlg       = false;
    }
    
    trigger(timeDomain, frequency){
    if(this.interFlg == true) return false;
    
    var offset = 30;
    var total  = Math.floor(frequency.length / offset);
    var volume = 0;
    for(var i=0; i<total; i+=offset){
        volume += frequency[i * offset];
    }
    
    if(volume < this.interThreshold) return false;

    this.interFlg = true;
    setInterval(()=>{this.interFlg = false;}, this.interTime);
    return true;
    }
}

ibisPaint , gimp , inkscape の練習

として、ラスターデータ (jpg) を下絵に、ベクターデータ(svg)にしてみた。以下

<sodipodi:namedview id="namedview7" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageshadow="2" inkscape:pageopacity="0.0" inkscape:pagecheckerboard="0" inkscape:document-units="mm" showgrid="false" inkscape:zoom="3.1108586" inkscape:cx="149.9586" inkscape:cy="152.85169" inkscape:window-width="2063" inkscape:window-height="1343" inkscape:window-x="157" inkscape:window-y="16" inkscape:window-maximized="0" inkscape:current-layer="layer1" />

hands-on three.js + cannon.js

本来、 hands-on three.js + cannon.js - 3D迷路 (like fps ?) - end0tknr's kipple - web写経開発 より前に、記載すべき入門的なものですが、今更、post。

目次

three.js + cannon.js 1 - 物体の落下

https://end0tknr.github.io/sandbox/threejs_cannonjs_misc/test_cannonjs_1.html

f:id:end0tknr:20210813075321p:plain

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <!-- refer to https://liginc.co.jp/378458 -->
  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/109/three.min.js"></script>
  <script src="https://cdn.jsdelivr.net/gh/mrdoob/three.js@r109/examples/js/controls/OrbitControls.js"></script>

  <script src="https://cdn.jsdelivr.net/npm/cannon@0.6.2/build/cannon.min.js"></script>
  
</head>
<body>
  <div id="stage"></div>

  <div class="kakudo">
    手玉を突く角度
    <input type="range" class="range js-range"
           value="0" step="0.1" min="-4" max="4">
    <button class="js-fire">発射</button>
    </div> <!-- #kakudo -->

  <script>
    (function() {
        var controls;
        var scene;
        var camera;
        var renderer;
        
        var phySphere;
        var phySphere2;
        var phySphere3;
        
        var viewSphere;
        var viewSphere2;
        var viewSphere3;

        var rand = Math.random()*20 - 10;       // 的玉の位置

        var world = setPhy();
        setView();
        animate();


        function setPhy() {
    
            var world = new CANNON.World();     // 物理世界
            world.gravity.set(0, -9.82, 0);     // 重力

            //「衝突可能性」の剛体同士を探索
            world.broadphase = new CANNON.NaiveBroadphase();
    
            world.solver.iterations = 5;        // 反復計算回数
            world.solver.tolerance = 0.1;       // 許容値
  
            //地面
            var groundMat = new CANNON.Material('groundMat');
            //質量定義
            var phyPlane = new CANNON.Body({
                mass: 0,
                material: groundMat
            });

            phyPlane.addShape(new CANNON.Plane());
            // X軸に90度回転
            phyPlane.quaternion.setFromAxisAngle(
                new CANNON.Vec3(1, 0, 0), -Math.PI / 2 );
            world.add(phyPlane);
  
            var sphereMat = new CANNON.Material('sphereMat');
            //質量定義
            phySphere = new CANNON.Body({
                mass: 1,
                material: sphereMat
            });

            
            phySphere.addShape(new CANNON.Sphere(1));
            phySphere.position.set(20, 1, 0);   //位置
            phySphere.velocity.set(0, 0, 0);    //角速度
            phySphere.angularDamping = 0.1;     //減衰率
            world.add(phySphere);
  
            var sphereMat2 = new CANNON.Material('sphereMat2');
            //質量定義
            phySphere2 = new CANNON.Body({
                mass: 1, material: sphereMat2   });
            
            phySphere2.addShape(new CANNON.Sphere(1));
            phySphere2.position.set(10, 1, 0);  //位置
            phySphere2.velocity.set(0, 0, 0);   //角速度
            phySphere2.angularDamping = 0.1;    //減衰率
            world.add(phySphere2);
  
            var sphereMat3 = new CANNON.Material('sphereMat3');
            //質量定義
            phySphere3 = new CANNON.Body({
                mass: 2,  material: sphereMat3  });
            
            phySphere3.addShape(new CANNON.Sphere(2));
            phySphere3.position.set(-10, 2, rand);      //位置
            phySphere3.velocity.set(0, 0, 0);           //角速度
            phySphere3.angularDamping = 0.1;            //減衰率
            world.add(phySphere3);
  
            //SphereとSphere2が接触した際のContactMaterial
            var sphereSphereCM = new CANNON.ContactMaterial(
                sphereMat,
                sphereMat2,
                {contactEquationRelaxation: 3,          //接触式の緩和性
                 contactEquationStiffness: 10000000,    //接触式の剛性
                 friction: 0.3,                         //摩擦係数
                 frictionEquationRelaxation: 3,         //摩擦式の剛性
                 frictionEquationStiffness: 10000000,   //摩擦式の緩和性
                 restitution: 0.3                       //反発係数
                }
            );
            world.addContactMaterial(sphereSphereCM);
  
            //地面とSphereが接触した際のContactMaterial
            spherePlaneCM = new CANNON.ContactMaterial(
                groundMat,
                sphereMat,
                {friction: 0,   //摩擦係数
                 restitution: 0 //反発係数
                }
            );
            world.addContactMaterial(spherePlaneCM);
  
            //地面とSphereが接触した際のContactMaterial
            spherePlaneCM2 = new CANNON.ContactMaterial(
                groundMat,
                sphereMat2,
                {friction: 0,   //摩擦係数
                 restitution: 0 //反発係数
                }
            );
            world.addContactMaterial(spherePlaneCM2);
  
            world.bsc_dist = new CANNON.Vec3();

            return world;
        }


        function setView() {
            scene = new THREE.Scene();
            scene.fog = new THREE.Fog(0x000000, 1, 100);
            camera = new THREE.PerspectiveCamera(40, 650 / 400, 1, 10000);
            camera.position.set(50, 15, 0);
            camera.lookAt(new THREE.Vector3(0, 0, 0));
            scene.add(camera);
            var light = new THREE.DirectionalLight(0xffffff, 2);
            
            light.position.set(5, 10, -10);
            light.castShadow = true;
            light.shadowMapWidth = 1024;
            light.shadowMapHeight = 1024;
            light.shadowCameraLeft = -10;
            light.shadowCameraRight = 10;
            light.shadowCameraTop = 10;
            light.shadowCameraBottom = -10;
            light.shadowCameraFar = 100;
            light.shadowCameraNear = 0;
            light.shadowDarkness = 0.5;
            scene.add(light);
            var amb = new THREE.AmbientLight(0x999999);
            scene.add(amb);
            
  
            var viewPlane = new THREE.Mesh(
                new THREE.PlaneGeometry(300, 300),
                new THREE.MeshPhongMaterial( {color: 0x333333} )
            );
            viewPlane.rotation.x = -Math.PI / 2;
            viewPlane.position.y = 1 / 30;
            viewPlane.receiveShadow = true;
            scene.add(viewPlane);
            
            viewSphere = new THREE.Mesh(
                new THREE.SphereGeometry(1, 50, 50),
                new THREE.MeshLambertMaterial( {color: 0xffffff} )
            );
            viewSphere.castShadow = true;
            viewSphere.receiveShadow = true;
            viewSphere.position = phySphere.position;
            scene.add(viewSphere);
            
            viewSphere2 = new THREE.Mesh(
                new THREE.SphereGeometry(1, 50, 50),
                new THREE.MeshLambertMaterial( {color: 0xffffff} )
            );
            viewSphere2.castShadow = true;
            viewSphere2.receiveShadow = true;
            viewSphere2.position = phySphere2.position;
            scene.add(viewSphere2);
  
            viewSphere3 = new THREE.Mesh(
                new THREE.SphereGeometry(2, 50, 50),
                new THREE.MeshLambertMaterial(
                    {side: THREE.DoubleSide // 裏からも見える為
                     //map: textureHayachi,
                    }
                )
            );
            viewSphere3.castShadow = true;
            viewSphere3.receiveShadow = true;
            viewSphere3.position = phySphere3.position;
            scene.add(viewSphere3);
 
            renderer = new THREE.WebGLRenderer({antialias: true});
            renderer.setSize(650, 400);
            renderer.setClearColor(0x000000, 1);
            renderer.shadowMapEnabled = true;
            document.body.appendChild(renderer.domElement);
            renderer.render(scene, camera);
            
            // controls
            controls = new THREE.OrbitControls(camera, renderer.domElement);
            controls.minDistance = 0;           //近づける距離の最小値
            controls.maxDistance = 9800;        //遠ざかる距離の最大値
        }


        function animate() {
            requestAnimationFrame(animate);
            
            world.step(1 / 60); // 物理エンジンの時間を進める
            viewSphere.position.copy(phySphere.position);
            viewSphere.quaternion.copy(phySphere.quaternion);
            viewSphere2.position.copy(phySphere2.position);
            viewSphere2.quaternion.copy(phySphere2.quaternion);
            viewSphere3.position.copy(phySphere3.position);
            viewSphere3.quaternion.copy(phySphere3.quaternion);
            controls.update();

            renderer.render(scene, camera);
        }

        var angle = 0;

        $('.js-range').on('change', function(e) {
            e.preventDefault();
            angle = parseInt($(this).val(), 10);
        });

        $('.js-fire').on('click', function(e) {
            e.preventDefault();
            phySphere.velocity.set(-20, 0, -angle);
        });
        
  })();
  </script>
  
</body>
</html>

three.js + cannon.js 2 - ビリヤード ゲーム

Cannon.jsで簡単なゲームを作ってみよう! | 株式会社LIG の写経 + 少々、リファクタリングです。

https://end0tknr.github.io/sandbox/threejs_cannonjs_misc/test_cannonjs_2.html

f:id:end0tknr:20210813075341p:plain

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <!-- refer to https://liginc.co.jp/378458 -->
  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/109/three.min.js"></script>
  <script src="https://cdn.jsdelivr.net/gh/mrdoob/three.js@r109/examples/js/controls/OrbitControls.js"></script>

  <script src="https://cdn.jsdelivr.net/npm/cannon@0.6.2/build/cannon.min.js"></script>
  
</head>
<body>
  <div id="stage"></div>

  <div class="kakudo">
    手玉を突く角度
    <input type="range" class="range js-range"
           value="0" step="0.1" min="-4" max="4">
    <button class="js-fire">発射</button>
    </div> <!-- #kakudo -->

  <script>
    (function() {
        var controls;
        var scene;
        var camera;
        var renderer;
        
        var phySphere;
        var phySphere2;
        var phySphere3;
        
        var viewSphere;
        var viewSphere2;
        var viewSphere3;

        var rand = Math.random()*20 - 10;       // 的玉の位置

        var world = setPhy();
        setView();
        animate();


        function setPhy() {
    
            var world = new CANNON.World();     // 物理世界
            world.gravity.set(0, -9.82, 0);     // 重力

            //「衝突可能性」の剛体同士を探索
            world.broadphase = new CANNON.NaiveBroadphase();
    
            world.solver.iterations = 5;        // 反復計算回数
            world.solver.tolerance = 0.1;       // 許容値
  
            //地面
            var groundMat = new CANNON.Material('groundMat');
            //質量定義
            var phyPlane = new CANNON.Body({
                mass: 0,
                material: groundMat
            });

            phyPlane.addShape(new CANNON.Plane());
            // X軸に90度回転
            phyPlane.quaternion.setFromAxisAngle(
                new CANNON.Vec3(1, 0, 0), -Math.PI / 2 );
            world.add(phyPlane);
  
            var sphereMat = new CANNON.Material('sphereMat');
            //質量定義
            phySphere = new CANNON.Body({
                mass: 1,
                material: sphereMat
            });

            
            phySphere.addShape(new CANNON.Sphere(1));
            phySphere.position.set(20, 1, 0);   //位置
            phySphere.velocity.set(0, 0, 0);    //角速度
            phySphere.angularDamping = 0.1;     //減衰率
            world.add(phySphere);
  
            var sphereMat2 = new CANNON.Material('sphereMat2');
            //質量定義
            phySphere2 = new CANNON.Body({
                mass: 1, material: sphereMat2   });
            
            phySphere2.addShape(new CANNON.Sphere(1));
            phySphere2.position.set(10, 1, 0);  //位置
            phySphere2.velocity.set(0, 0, 0);   //角速度
            phySphere2.angularDamping = 0.1;    //減衰率
            world.add(phySphere2);
  
            var sphereMat3 = new CANNON.Material('sphereMat3');
            //質量定義
            phySphere3 = new CANNON.Body({
                mass: 2,  material: sphereMat3  });
            
            phySphere3.addShape(new CANNON.Sphere(2));
            phySphere3.position.set(-10, 2, rand);      //位置
            phySphere3.velocity.set(0, 0, 0);           //角速度
            phySphere3.angularDamping = 0.1;            //減衰率
            world.add(phySphere3);
  
            //SphereとSphere2が接触した際のContactMaterial
            var sphereSphereCM = new CANNON.ContactMaterial(
                sphereMat,
                sphereMat2,
                {contactEquationRelaxation: 3,          //接触式の緩和性
                 contactEquationStiffness: 10000000,    //接触式の剛性
                 friction: 0.3,                         //摩擦係数
                 frictionEquationRelaxation: 3,         //摩擦式の剛性
                 frictionEquationStiffness: 10000000,   //摩擦式の緩和性
                 restitution: 0.3                       //反発係数
                }
            );
            world.addContactMaterial(sphereSphereCM);
  
            //地面とSphereが接触した際のContactMaterial
            spherePlaneCM = new CANNON.ContactMaterial(
                groundMat,
                sphereMat,
                {friction: 0,   //摩擦係数
                 restitution: 0 //反発係数
                }
            );
            world.addContactMaterial(spherePlaneCM);
  
            //地面とSphereが接触した際のContactMaterial
            spherePlaneCM2 = new CANNON.ContactMaterial(
                groundMat,
                sphereMat2,
                {friction: 0,   //摩擦係数
                 restitution: 0 //反発係数
                }
            );
            world.addContactMaterial(spherePlaneCM2);
  
            world.bsc_dist = new CANNON.Vec3();

            return world;
        }


        function setView() {
            scene = new THREE.Scene();
            scene.fog = new THREE.Fog(0x000000, 1, 100);
            camera = new THREE.PerspectiveCamera(40, 650 / 400, 1, 10000);
            camera.position.set(50, 15, 0);
            camera.lookAt(new THREE.Vector3(0, 0, 0));
            scene.add(camera);
            var light = new THREE.DirectionalLight(0xffffff, 2);
            
            light.position.set(5, 10, -10);
            light.castShadow = true;
            light.shadowMapWidth = 1024;
            light.shadowMapHeight = 1024;
            light.shadowCameraLeft = -10;
            light.shadowCameraRight = 10;
            light.shadowCameraTop = 10;
            light.shadowCameraBottom = -10;
            light.shadowCameraFar = 100;
            light.shadowCameraNear = 0;
            light.shadowDarkness = 0.5;
            scene.add(light);
            var amb = new THREE.AmbientLight(0x999999);
            scene.add(amb);
            
  
            var viewPlane = new THREE.Mesh(
                new THREE.PlaneGeometry(300, 300),
                new THREE.MeshPhongMaterial( {color: 0x333333} )
            );
            viewPlane.rotation.x = -Math.PI / 2;
            viewPlane.position.y = 1 / 30;
            viewPlane.receiveShadow = true;
            scene.add(viewPlane);
            
            viewSphere = new THREE.Mesh(
                new THREE.SphereGeometry(1, 50, 50),
                new THREE.MeshLambertMaterial( {color: 0xffffff} )
            );
            viewSphere.castShadow = true;
            viewSphere.receiveShadow = true;
            viewSphere.position = phySphere.position;
            scene.add(viewSphere);
            
            viewSphere2 = new THREE.Mesh(
                new THREE.SphereGeometry(1, 50, 50),
                new THREE.MeshLambertMaterial( {color: 0xffffff} )
            );
            viewSphere2.castShadow = true;
            viewSphere2.receiveShadow = true;
            viewSphere2.position = phySphere2.position;
            scene.add(viewSphere2);
  
            viewSphere3 = new THREE.Mesh(
                new THREE.SphereGeometry(2, 50, 50),
                new THREE.MeshLambertMaterial(
                    {side: THREE.DoubleSide // 裏からも見える為
                     //map: textureHayachi,
                    }
                )
            );
            viewSphere3.castShadow = true;
            viewSphere3.receiveShadow = true;
            viewSphere3.position = phySphere3.position;
            scene.add(viewSphere3);
 
            renderer = new THREE.WebGLRenderer({antialias: true});
            renderer.setSize(650, 400);
            renderer.setClearColor(0x000000, 1);
            renderer.shadowMapEnabled = true;
            document.body.appendChild(renderer.domElement);
            renderer.render(scene, camera);
            
            // controls
            controls = new THREE.OrbitControls(camera, renderer.domElement);
            controls.minDistance = 0;           //近づける距離の最小値
            controls.maxDistance = 9800;        //遠ざかる距離の最大値
        }


        function animate() {
            requestAnimationFrame(animate);
            
            world.step(1 / 60); // 物理エンジンの時間を進める
            viewSphere.position.copy(phySphere.position);
            viewSphere.quaternion.copy(phySphere.quaternion);
            viewSphere2.position.copy(phySphere2.position);
            viewSphere2.quaternion.copy(phySphere2.quaternion);
            viewSphere3.position.copy(phySphere3.position);
            viewSphere3.quaternion.copy(phySphere3.quaternion);
            controls.update();

            renderer.render(scene, camera);
        }

        var angle = 0;

        $('.js-range').on('change', function(e) {
            e.preventDefault();
            angle = parseInt($(this).val(), 10);
        });

        $('.js-fire').on('click', function(e) {
            e.preventDefault();
            phySphere.velocity.set(-20, 0, -angle);
        });
        
  })();
  </script>
  
</body>
</html>

three.js + cannon.js 3 - ヒンジによるタイヤ接合

Javascript で動く軽量物理エンジン Cannon.js と3Dレンダラ Three.js で書いた短いサンプルコード - Qiita の写経 + 少々、リファクタリングです。

https://end0tknr.github.io/sandbox/threejs_cannonjs_misc/test_cannonjs_3.html

f:id:end0tknr:20210813112529p:plain

<html>
<head>
<!-- refer to https://qiita.com/yamazaki3104/items/fafb7879591caf137b52 -->

<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/109/three.js"></script>
<script src="https://cdn.jsdelivr.net/gh/mrdoob/three.js@r109/examples/js/controls/TrackballControls.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/cannon.js/0.6.2/cannon.js"></script>
</head>

<body>
<script>
    
  class THREEJS {
      
      constructor(){
          const w = document.body.clientWidth
          const h = document.body.clientHeight
          
          this.renderer = new THREE.WebGLRenderer()
          this.renderer.setClearColor( 0x8888dd )
          this.renderer.setSize( w, h )
          
          this.camera = new THREE.PerspectiveCamera( 40, w / h, 0.1, 1000 )
          this.camera.position.x =  0
          this.camera.position.y = 20
          this.camera.position.z = 30
          this.trackball = new THREE.TrackballControls( this.camera )
          
          this.scene = new THREE.Scene()
          
          let directionalLight = new THREE.DirectionalLight( 0xffffff, 1 )
          directionalLight.position.set( 0.2, 0.5, 0.3 )
          this.scene.add( directionalLight )
          
          // this.scene.add( new THREE.AmbientLight( 0x101020 ) )
          
          document.body.appendChild( this.renderer.domElement )
      }
      
      render(){
          for ( let mesh of this.scene.children ) {
              if ( ! mesh.cannon_rigid_body ) continue
              
              mesh.position.copy(   mesh.cannon_rigid_body.position   )
              mesh.quaternion.copy( mesh.cannon_rigid_body.quaternion )
          }
          
          this.trackball.update()
          this.renderer.render( this.scene, this.camera )
      }
  }
  
  class CANNON_PHYSICS {
      constructor( _threejs ) {
          this.cannon_world = new CANNON.World()
          this.cannon_world.gravity.set( 0, -9.80665, 0 )
          this.cannon_world.broadphase = new CANNON.NaiveBroadphase()
        this.cannon_world.solver.iterations = 10

        this.threejs = _threejs
    }

    add_box( _arg ) {
        const body  = new CANNON.Body( {
            mass:       _arg.mass,
            shape:      new CANNON.Box( new CANNON.Vec3(_arg.w/2, _arg.h/2, _arg.d/2 )),
            position:   new CANNON.Vec3( _arg.x, _arg.y, _arg.z ),
            // 摩擦係数 0.1 マテリアルを作成
            material:   new CANNON.Material( { friction: 0.1, } ),
        } )
        this.add_body( body, _arg.color )
    }

    add_body( _body, _color ){
        this.cannon_world.addBody( _body )

        var obj = new THREE.Object3D();
        const color_mat = new THREE.MeshLambertMaterial( { color: _color } )

        // ここのコードは connon.demo.js の 977 shape2mesh() から、まるっと借りてきた。
        for (var l = 0; l < _body.shapes.length; l++) {
            var shape = _body.shapes[l];
            
            var mesh;

            switch(shape.type){
                
            case CANNON.Shape.types.SPHERE:
                var sphere_geometry = new THREE.SphereGeometry( shape.radius, 8, 8);
                mesh = new THREE.Mesh( sphere_geometry, color_mat );
                break;

            case CANNON.Shape.types.PARTICLE:
                mesh = new THREE.Mesh( this.particleGeo, this.particleMaterial );
                var s = this.settings;
                mesh.scale.set(s.particleSize,s.particleSize,s.particleSize);
                break;

            case CANNON.Shape.types.PLANE:
                var geometry = new THREE.PlaneGeometry(10, 10, 4, 4);
                mesh = new THREE.Object3D();
                var submesh = new THREE.Object3D();
                var ground = new THREE.Mesh( geometry, color_mat );
                ground.scale.set(100, 100, 100);
                submesh.add(ground);

                ground.castShadow = true;
                ground.receiveShadow = true;

                mesh.add(submesh);
                break;

            case CANNON.Shape.types.BOX:
                var box_geometry = new THREE.BoxGeometry(
                    shape.halfExtents.x*2,
                    shape.halfExtents.y*2,
                    shape.halfExtents.z*2 );
                mesh = new THREE.Mesh( box_geometry, color_mat );
                break;

            case CANNON.Shape.types.CONVEXPOLYHEDRON:
                var geo = new THREE.Geometry();

                // Add vertices
                for (var i = 0; i < shape.vertices.length; i++) {
                    var v = shape.vertices[i];
                    geo.vertices.push(new THREE.Vector3(v.x, v.y, v.z));
                }

                for(var i=0; i < shape.faces.length; i++){
                    var face = shape.faces[i];

                    // add triangles
                    var a = face[0];
                    for (var j = 1; j < face.length - 1; j++) {
                        var b = face[j];
                        var c = face[j + 1];
                        geo.faces.push(new THREE.Face3(a, b, c));
                    }
                }
                geo.computeBoundingSphere();
                geo.computeFaceNormals();
                mesh = new THREE.Mesh( geo, color_mat );
                break;

            case CANNON.Shape.types.HEIGHTFIELD:
                var geometry = new THREE.Geometry();

                var v0 = new CANNON.Vec3();
                var v1 = new CANNON.Vec3();
                var v2 = new CANNON.Vec3();
                for (var xi = 0; xi < shape.data.length - 1; xi++) {
                    for (var yi = 0; yi < shape.data[xi].length - 1; yi++) {
                        for (var k = 0; k < 2; k++) {
                            shape.getConvexTrianglePillar(xi, yi, k===0);
                            v0.copy(shape.pillarConvex.vertices[0]);
                            v1.copy(shape.pillarConvex.vertices[1]);
                            v2.copy(shape.pillarConvex.vertices[2]);
                            v0.vadd(shape.pillarOffset, v0);
                            v1.vadd(shape.pillarOffset, v1);
                            v2.vadd(shape.pillarOffset, v2);
                            geometry.vertices.push(
                                new THREE.Vector3(v0.x, v0.y, v0.z),
                                new THREE.Vector3(v1.x, v1.y, v1.z),
                                new THREE.Vector3(v2.x, v2.y, v2.z)
                            );
                            var i = geometry.vertices.length - 3;
                            geometry.faces.push(new THREE.Face3(i, i+1, i+2));
                        }
                    }
                }
                geometry.computeBoundingSphere();
                geometry.computeFaceNormals();
                mesh = new THREE.Mesh(geometry, color_mat);
                break;

            case CANNON.Shape.types.TRIMESH:
                var geometry = new THREE.Geometry();

                var v0 = new CANNON.Vec3();
                var v1 = new CANNON.Vec3();
                var v2 = new CANNON.Vec3();
                for (var i = 0; i < shape.indices.length / 3; i++) {
                    shape.getTriangleVertices(i, v0, v1, v2);
                    geometry.vertices.push(
                        new THREE.Vector3(v0.x, v0.y, v0.z),
                        new THREE.Vector3(v1.x, v1.y, v1.z),
                        new THREE.Vector3(v2.x, v2.y, v2.z)
                    );
                    var j = geometry.vertices.length - 3;
                    geometry.faces.push(new THREE.Face3(j, j+1, j+2));
                }
                geometry.computeBoundingSphere();
                geometry.computeFaceNormals();
                mesh = new THREE.Mesh(geometry, color_mat);
                break;

            default:
                throw "Visual type not recognized: "+shape.type;
            }

            var o = _body.shapeOffsets[l];
            var q = _body.shapeOrientations[l];
            mesh.position.set(o.x, o.y, o.z);
            mesh.quaternion.set(q.x, q.y, q.z, q.w);

            obj.add(mesh);
        }

        obj.cannon_rigid_body = _body
        this.threejs.scene.add( obj )
    }


    render( _sec ) {
        this.cannon_world.step( _sec )
        this.threejs.render()
    }
}


  let cannon_phy = new CANNON_PHYSICS( new THREEJS() )
  //床面
  cannon_phy.add_box( {
      mass: 0, x: 0, y: -0.2, z: 0, w: 150, h: 0.4, d: 150, color: 0x333333,
  } )
  
  //ドミノ
  const box_size = 1.5
  for ( let y=0 ; y<16; y++ ) {
      for ( let x=0 ; x<16; x++ ) {
          cannon_phy.add_box( {
              mass: 1,
              x: (x-7) * box_size * 0.95,
              y: box_size * 0.5,
              z: (y-7) * box_size * 1.2,
              w: box_size*0.1, h: box_size*1, d: box_size*1,
              color:0xDCAA6B
          })
      }
  }
  
  
  var w_mat           = new CANNON.Material()
  // CANNON.Cylinder()の引数は
  // radiusTop, radiusBottom, height, numSegments.
  var ws              = new CANNON.Cylinder( 1.9, 1.2, 1, 8 )

  var leftFrontWheel  = new CANNON.Body({
      mass: 1,
      material: w_mat,
      shape: ws,
      position: { x: 5, y:  5+20, z: 0 },
      //以下で向きを決めていますが、結局、HingeConstraintの axisB で補正.
      quaternion: new CANNON.Quaternion( 1, 0, 0, -Math.PI / 4 ) } );
  
  var rightFrontWheel = new CANNON.Body({
      mass: 1,
      material: w_mat,
      shape: ws,
      position: { x: 5, y: -5+20, z: 0 },
      quaternion: new CANNON.Quaternion( 1, 0, 0,  Math.PI / 4 ) } );
  
  var leftRearWheel   = new CANNON.Body({
      mass: 1,
      material: w_mat,
      shape: ws,
      position: { x:-5, y:  5+20, z: 0 },
      quaternion: new CANNON.Quaternion( 1, 0, 0, -Math.PI / 4 ) } );
  
  var rightRearWheel  = new CANNON.Body({
      mass: 1,
      material: w_mat,
      shape: ws,
      position: { x:-5, y: -5+20, z: 0 },
      quaternion: new CANNON.Quaternion( 1, 0, 0,  Math.PI / 4 ) } );

  // シャシー
  var chassis = new CANNON.Body({
      mass: 5,
      shape: new CANNON.Box( new CANNON.Vec3( 5, 2, 0.5 ) ),
      position: { x: 0, y: 20, z: 0 }
  })

  //制約

  //前輪は、やや斜めにして接続(ハンドルを切った状態)
  var constraint_leftFront = new CANNON.HingeConstraint(
      chassis,
      leftFrontWheel,
      { pivotA: new CANNON.Vec3(  5,  5, 0 ),   axisA: new CANNON.Vec3( 1, 1, 0 ),
        pivotB: new CANNON.Vec3(),              axisB: new CANNON.Vec3( 0, 0, -1 ) } );
  cannon_phy.cannon_world.addConstraint( constraint_leftFront );
  
  var constraint_rightFront = new CANNON.HingeConstraint(
      chassis,
      rightFrontWheel,
      { pivotA: new CANNON.Vec3(  5, -5, 0 ),   axisA: new CANNON.Vec3( 1, 1, 0  ),
        pivotB: new CANNON.Vec3(),              axisB: new CANNON.Vec3( 0, 0, -1 ) } );
  cannon_phy.cannon_world.addConstraint( constraint_rightFront );
  
  var constraint_leftRear = new CANNON.HingeConstraint(
      chassis,
      leftRearWheel,
      { pivotA: new CANNON.Vec3( -5,  5, 0 ),   axisA: new CANNON.Vec3( 0, 1, 0 ),
        pivotB: new CANNON.Vec3(),              axisB: new CANNON.Vec3( 0, 0, -1 ) } );
  cannon_phy.cannon_world.addConstraint( constraint_leftRear );

  var constraint_rightRear = new CANNON.HingeConstraint(
      chassis,
      rightRearWheel,
      { pivotA: new CANNON.Vec3( -5, -5, 0 ),   axisA: new CANNON.Vec3( 0, 1, 0 ),
        pivotB: new CANNON.Vec3(),              axisB: new CANNON.Vec3( 0, 0, -1, ) } );
  cannon_phy.cannon_world.addConstraint( constraint_rightRear );

  //前輪駆動として回転させる
  constraint_leftFront.enableMotor();
  constraint_rightFront.enableMotor();
  constraint_leftFront.setMotorSpeed( 7 );
  constraint_rightFront.setMotorSpeed( -7 );
  
  for ( const body of [ chassis,
                        leftFrontWheel,
                        rightFrontWheel,
                        leftRearWheel,
                        rightRearWheel ] ){
      cannon_phy.add_body( body, 0x556677 )
  }
  
  
  function animate(){
      cannon_phy.render( 1 / 60 )
      window.requestAnimationFrame( animate )
  }
  
  window.requestAnimationFrame( animate )

</script>
</body>
</html>

three.js + cannon.js 4 - 2コのピンによる板の接合 (FPS)

先程の「three.js + cannon.js 3 - ヒンジによるタイヤ接合」では、 HingeConstraint により、車体とタイヤを接続しましたが、 以下では、CANNON.PointToPointConstraint により、板を接合しています。

元々、cannon.js の examples にあったものの、写経 + 少々、リファクタリングです。

https://github.com/schteppe/cannon.js/tree/master/examples

https://schteppe.github.io/cannon.js/examples/threejs_fps.html

https://end0tknr.github.io/sandbox/cannonjs_examples/threejs_fps.html

f:id:end0tknr:20210813163119p:plain

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>cannon.js + three.js physics shooter</title>
    <style>
      #blocker {
          position: absolute;
          width: 100%;
          height: 100%;
          background-color: rgba(0,0,0,0.5);
      }
      
      #instructions {
          width: 100%;
          height: 100%;
          color: #ffffff;
          text-align: center;
          font-size:20px;
          padding-top:20px;
          cursor: pointer;
      }
    </style>
    
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/68/three.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/cannon@0.6.2/build/cannon.min.js"></script>
    <!-- マウスによるカメラ制御 -->
    <!-- https://developer.mozilla.org/ja/docs/Web/API/Pointer_Lock_API -->
    <script src="js/PointerLockControls.js"></script>
  </head>
  <body>

    <div id="blocker">
      <div id="instructions">
        Click to play<br/>
        (W,A,S,D = Move, SPACE = Jump, MOUSE = Look, CLICK = Shoot)
      </div>
    </div>

    <script>
      var world;
      var camera;
      var scene;
      var renderer;

      var sphereShape;
      var sphereBody;
      var physicsMaterial;
      var walls=[];
      var balls=[];     var ballMeshes=[];
      var boxes=[];     var boxMeshes=[];

      var geometry;
      var material;
      var mesh;
      var controls,time = Date.now();
      
      var blocker      = document.getElementById( 'blocker' );
      // 操作方法の表示
      var instructions = document.getElementById( 'instructions' );
      

      if ( 'pointerLockElement' in document ) {
          main();
      } else {
          instructions.innerHTML = 'Your browser doesn\'t seem to support Pointer Lock API';
      }

      function main(){
          var element = document.body;

          document.addEventListener(
              'pointerlockchange',
              function ( event ) {
                  if ( document.pointerLockElement === element ) {
                      controls.enabled = true;
                      blocker.style.display = 'none';
                  } else {
                      controls.enabled = false;
                      blocker.style.display = 'box';
                      instructions.style.display = '';
                  }
              },
              false );
          
          document.addEventListener(
              'pointerlockerror',
              function(event){instructions.style.display = ''; },
              false );
          
          instructions.addEventListener(
              'click',
              function ( event ) {
                  instructions.style.display = 'none';
                  
                  // Ask the browser to lock the pointer
                  element.requestPointerLock = element.requestPointerLock;
                  
                  element.requestPointerLock(); },
              false );
          
          initCannon();
          init();
          animate();
      }
      
      function initCannon(){
          // Setup our world
          world = new CANNON.World();
          world.quatNormalizeSkip = 0;
          world.quatNormalizeFast = false;
          
          var solver = new CANNON.GSSolver();
          //接触式の剛性
          world.defaultContactMaterial.contactEquationStiffness = 1e9
          //接触式の緩和性
          world.defaultContactMaterial.contactEquationRelaxation = 4;
          
          solver.iterations = 7;
          solver.tolerance = 0.1;
          
          world.solver = new CANNON.SplitSolver(solver);
          
          world.gravity.set(0,-20,0);
          world.broadphase = new CANNON.NaiveBroadphase();
          
          // Create a slippery material (friction coefficient = 0.0)
          physicsMaterial = new CANNON.Material("slipperyMaterial");
          var physicsContactMaterial =
              new CANNON.ContactMaterial(physicsMaterial,
                                         physicsMaterial,
                                         0.0, // friction coefficient
                                         0.3  // restitution
                                        );
          // We must add the contact materials to the world
          world.addContactMaterial(physicsContactMaterial);
          
          // 自分自身を表す球体
          var mass = 5;
          var radius = 2;
          sphereShape = new CANNON.Sphere(radius);
          sphereBody  = new CANNON.Body({ mass: mass });
          sphereBody.addShape(sphereShape);
          sphereBody.position.set(0, 5, 0);
          sphereBody.linearDamping = 0.9;
          world.addBody(sphereBody);
          
          // 物理的な床面
          var groundShape = new CANNON.Plane();
          var groundBody = new CANNON.Body({ mass: 0 });
          groundBody.addShape(groundShape);
          groundBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1,0,0),-Math.PI/2);
          world.addBody(groundBody);
      }
      
      function init() {
          
          camera = new THREE.PerspectiveCamera(
              75,
              window.innerWidth / window.innerHeight,
              0.1,
              1000 );
          
          scene = new THREE.Scene();
          scene.fog = new THREE.Fog( 0x000000, 0, 500 );
          
          var ambient = new THREE.AmbientLight( 0x111111 );
          scene.add( ambient );
          
          light = new THREE.SpotLight( 0xffffff );
          light.position.set( 10, 30, 20 );
          light.target.position.set( 0, 0, 0 );
          if(true){
              light.castShadow = true;
              
              light.shadowCameraNear = 20;
              light.shadowCameraFar = 50;//camera.far;
              light.shadowCameraFov = 40;
              
              light.shadowMapBias = 0.1;
              light.shadowMapDarkness = 0.7;
              light.shadowMapWidth = 2*512;
              light.shadowMapHeight = 2*512;
              
              //light.shadowCameraVisible = true;
          }
          scene.add( light );
          
          
          controls = new PointerLockControls( camera , sphereBody );
          scene.add( controls.getObject() );
          
          // 画面上の床面
          geometry = new THREE.PlaneGeometry( 300, 300, 50, 50 );
          geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) );
          
          material = new THREE.MeshLambertMaterial( { color: 0xdddddd } );
          
          mesh = new THREE.Mesh( geometry, material );
          mesh.castShadow = true;
          mesh.receiveShadow = true;
          scene.add( mesh );

          
          renderer = new THREE.WebGLRenderer();
          renderer.shadowMapEnabled = true;
          renderer.shadowMapSoft = true;
          renderer.setSize( window.innerWidth, window.innerHeight );
          renderer.setClearColor( scene.fog.color, 1 );
          
          document.body.appendChild( renderer.domElement );
          
          window.addEventListener( 'resize', onWindowResize, false );
          
          // 的となる箱の追加
          var halfExtents = new CANNON.Vec3(1,1,1);
          var boxShape = new CANNON.Box(halfExtents);
          var boxGeometry =
              new THREE.BoxGeometry(halfExtents.x*2,halfExtents.y*2,halfExtents.z*2);
          for(var i=0; i<15; i++){
              var x = (Math.random()-0.5)*20;
              var y = 1 + (Math.random()-0.5)*1;
              var z = (Math.random()-0.5)*20;
              var boxBody = new CANNON.Body({ mass: 5 });
              boxBody.addShape(boxShape);
              var boxMesh = new THREE.Mesh( boxGeometry, material );
              world.addBody(boxBody);
              scene.add(boxMesh);
              boxBody.position.set(x,y,z);
              boxMesh.position.set(x,y,z);
              boxMesh.castShadow = true;
              boxMesh.receiveShadow = true;
              boxes.push(boxBody);
              boxMeshes.push(boxMesh);
          }
          
          // 的となる「連結された箱」の追加
          var size = 0.5;
          var he = new CANNON.Vec3(size,size,size*0.1);
          var boxShape = new CANNON.Box(he);
          var mass = 0;
          var space = 0.1 * size;
          var N = 5;
          var last;
          var boxGeometry = new THREE.BoxGeometry(he.x*2,he.y*2,he.z*2);
          
          for(var i=0; i<N; i++){
              var boxbody = new CANNON.Body({ mass: mass });
              boxbody.addShape(boxShape);
              var boxMesh = new THREE.Mesh(boxGeometry, material);
              boxbody.position.set(5,(N-i)*(size*2+2*space) + size*2+space,0);
              boxbody.linearDamping = 0.01;
              boxbody.angularDamping = 0.01;
              // boxMesh.castShadow = true;
              boxMesh.receiveShadow = true;
              world.addBody(boxbody);
              scene.add(boxMesh);
              boxes.push(boxbody);
              boxMeshes.push(boxMesh);
              
              if(i!=0){
                  // 板の両端で連結
                  var c1 = new CANNON.PointToPointConstraint(
                      boxbody,  new CANNON.Vec3(-size, size+space,0),
                      last,     new CANNON.Vec3(-size,-size-space,0));
                  
                  var c2 = new CANNON.PointToPointConstraint(
                      boxbody,  new CANNON.Vec3(size, size+space,0),
                      last,     new CANNON.Vec3(size,-size-space,0));
                  
                  world.addConstraint(c1);
                  world.addConstraint(c2);
              } else {
                  mass=0.3;
              }
              last = boxbody;
          }
      }
      
      function onWindowResize() {
          camera.aspect = window.innerWidth / window.innerHeight;
          camera.updateProjectionMatrix();
          renderer.setSize( window.innerWidth, window.innerHeight );
      }
      
      var dt = 1/60;
      function animate() {
          requestAnimationFrame( animate );
          if(controls.enabled){
              world.step(dt);
              
              // Update ball positions
              for(var i=0; i<balls.length; i++){
                  ballMeshes[i].position.copy(balls[i].position);
                  ballMeshes[i].quaternion.copy(balls[i].quaternion);
              }
              
              // Update box positions
              for(var i=0; i<boxes.length; i++){
                  boxMeshes[i].position.copy(boxes[i].position);
                  boxMeshes[i].quaternion.copy(boxes[i].quaternion);
              }
          }
          
          controls.update( Date.now() - time );
          renderer.render( scene, camera );
          time = Date.now();
          
      }
      
      var ballShape      = new CANNON.Sphere(0.2);
      var ballGeometry   = new THREE.SphereGeometry(ballShape.radius, 32, 32);
      var shootDirection = new THREE.Vector3();
      var shootVelo = 15;
      var projector = new THREE.Projector();
      
      function getShootDir(targetVec){
          var vector = targetVec;
          targetVec.set(0,0,1);
          projector.unprojectVector(vector, camera);
          var ray = new THREE.Ray(
              sphereBody.position,
              vector.sub(sphereBody.position).normalize() );
          
          targetVec.copy(ray.direction);
      }
      
      window.addEventListener("click",function(e){
          if(controls.enabled==true){
              var x = sphereBody.position.x;
              var y = sphereBody.position.y;
              var z = sphereBody.position.z;
              var ballBody = new CANNON.Body({ mass: 1 });
              ballBody.addShape(ballShape);
              var ballMesh = new THREE.Mesh( ballGeometry, material );
              world.addBody(ballBody);
              scene.add(ballMesh);
              ballMesh.castShadow = true;
              ballMesh.receiveShadow = true;
              balls.push(ballBody);
              ballMeshes.push(ballMesh);
              getShootDir(shootDirection);
              ballBody.velocity.set(  shootDirection.x * shootVelo,
                                      shootDirection.y * shootVelo,
                                      shootDirection.z * shootVelo);
              
              // Move the ball outside the player sphere
              x += shootDirection.x * (sphereShape.radius*1.02 + ballShape.radius);
              y += shootDirection.y * (sphereShape.radius*1.02 + ballShape.radius);
              z += shootDirection.z * (sphereShape.radius*1.02 + ballShape.radius);
              ballBody.position.set(x,y,z);
              ballMesh.position.set(x,y,z);
          }
      });
      
    </script>
  </body>
</html>

hands-on three.js + cannon.js - 3D迷路 (like fps ?)

以下は、 Three.jsとCANNON.jsで作る「VR3D脱出迷路アプリ」作成(その2) - Qiita の写経 + 少々、リファクタリングです。

実際の動作は、github pages の以下でお試し下さい。

https://end0tknr.github.io/sandbox/threejs_cannonjs_3d_maze

<!DOCTYPE html>
<html>
  <head>
    <title>Three.js + Cannon.js 3D maze</title>
    <meta charset="utf-8">

    <script src="https://cdnjs.cloudflare.com/ajax/libs/cannon.js/0.6.2/cannon.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r71/three.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <style>
      body {margin:0;  padding:0;  border:0; }
      img  {position: absolute; width:100px; height:100px}
    </style>
  </head>
  <body>
    <!-- 移動ボタン -->
    <img id="forward"    src="img/forward_btn.png">
    <img id="stop"       src="img/stop_btn.png">
    <img id="back"       src="img/back_btn.png">
    <img id="jump"       src="img/jump_btn.png">
    <img id="turn_right" src="img/right_btn.png">
    <img id="turn_left"  src="img/left_btn.png">
    
    <script src="cannonjs_3d_maze.js"></script>
  </body>
</html>
// refer to https://qiita.com/DAI788/items/1b302ed9840c65e0193c

//迷路の地図データ

// ┌─────┬──────┬───┬─────┐
// │          │            │      │ 【START】│
// │  ┌─┐  │  ───┐  │  │  │  ────┤
// │  │  │  │        │  │  │              │
// │  │  │  │    ┐            │   ──── │
// │  │  │        │        │  │            │
// │      └─┬──┤    ──┘  └────┐  │
// │          │    │                      │  │
// ├───    │    │  │  ───┐        │  │
// │                    │        │  │        │
// │  ────┐        │        │  └────┤
// │          │    │  │  │                  │
// ├─        └──┤  └─┤  ┌─────┐  │
// │                │          │          │  │
// │  ┌───  │  │  ──┐  │        ─┘  │
// │  │        │          │  │              │
// │  └─┐    │          │  │      ────┤
// │      │    │  ───  │                  │
// ├─    │    │          │  ───────  │
// │      │    │          │                  │
// │  ──┴──┴─────┴─────────┘

var mapData = [
    "50_10","49_10","48_10","47_10","46_10","45_10","44_10","43_10","42_10",
    "41_10","40_10","39_10","38_10","37_10","36_10","35_10","34_10","33_10",
    "32_10","31_10","30_10","29_10","28_10","27_10","26_10","25_10","24_10",
    "23_10","22_10","21_10","20_10","19_10","18_10","17_10","16_10","15_10",
    "14_10","13_10","12_10","11_10","10_10","50_11","33_11","25_11","14_11",
    "50_12","33_12","25_12","14_12","50_13","33_13","25_13","14_13","50_14",
    "46_14","45_14","44_14","43_14","42_14","41_14","40_14","39_14","38_14",
    "37_14","33_14","29_14","25_14","21_14","20_14","19_14","18_14","10_14",
    "50_15","46_15","33_15","29_15","21_15","18_15","10_15","50_16","46_16",
    "33_16","29_16","21_16","18_16","10_16","50_17","46_17","45_17","44_17",
    "43_17","42_17","41_17","40_17","39_17","38_17","37_17","33_17","29_17",
    "21_17","18_17","17_17","16_17","15_17","14_17","13_17","12_17","11_17",
    "10_17","50_18","37_18","29_18","21_18","10_18","50_19","37_19","29_19",
    "10_19","50_20","37_20","29_20","10_20","50_21","49_21","48_21","47_21",
    "46_21","45_21","44_21","43_21","42_21","41_21","37_21","36_21","35_21",
    "34_21","33_21","29_21","28_21","27_21","26_21","25_21","10_21","50_22",
    "37_22","25_22","21_22","20_22","19_22","18_22","17_22","16_22","15_22",
    "14_22","13_22","12_22","11_22","10_22","50_23","37_23","25_23","10_23",
    "50_24","37_24","25_24","10_24","50_25","46_25","41_25","37_25","25_25",
    "10_25","50_26","46_26","41_26","40_26","39_26","38_26","37_26","36_26",
    "35_26","34_26","30_26","29_26","28_26","27_26","26_26","25_26","24_26",
    "23_26","22_26","21_26","10_26","50_27","46_27","15_27","10_27","50_28",
    "46_28","15_28","10_28","50_29","46_29","15_29","10_29","50_30","46_30",
    "45_30","44_30","43_30","42_30","38_30","34_30","33_30","32_30","31_30",
    "30_30","29_30","28_30","27_30","21_30","15_30","10_30","50_31","38_31",
    "27_31","21_31","10_31","50_32","38_32","27_32","21_32","10_32","50_33",
    "38_33","27_33","21_33","10_33","50_34","49_34","48_34","47_34","46_34",
    "45_34","41_34","40_34","39_34","38_34","34_34","30_34","29_34","28_34",
    "27_34","26_34","25_34","21_34","20_34","19_34","18_34","17_34","16_34",
    "15_34","14_34","13_34","12_34","11_34","10_34","50_35","34_35","10_35",
    "50_36","34_36","10_36","50_37","34_37","10_37","50_38","46_38","45_38",
    "44_38","43_38","42_38","41_38","40_38","39_38","38_38","34_38","33_38",
    "32_38","31_38","27_38","26_38","25_38","24_38","23_38","22_38","21_38",
    "20_38","19_38","10_38","50_39","38_39","27_39","15_39","10_39","50_40",
    "38_40","27_40","15_40","10_40","50_41","38_41","27_41","15_41","10_41",
    "50_42","49_42","48_42","47_42","46_42","42_42","38_42","34_42","33_42",
    "32_42","31_42","27_42","15_42","10_42","50_43","42_43","38_43","31_43",
    "27_43","23_43","19_43","15_43","10_43","50_44","42_44","38_44","31_44",
    "27_44","23_44","19_44","15_44","10_44","50_45","42_45","38_45","31_45",
    "27_45","23_45","19_45","15_45","10_45","50_46","46_46","42_46","38_46",
    "37_46","36_46","35_46","31_46","27_46","26_46","25_46","24_46","23_46",
    "19_46","15_46","10_46","50_47","46_47","31_47","19_47","10_47","50_48",
    "46_48","31_48","19_48","10_48","50_49","46_49","31_49","19_49","10_49",
    "50_50","49_50","48_50","47_50","46_50","45_50","44_50","43_50","42_50",
    "41_50","40_50","39_50","38_50","37_50","36_50","35_50","34_50","33_50",
    "32_50","31_50","30_50","29_50","28_50","27_50","26_50","25_50","24_50",
    "23_50","22_50","21_50","20_50","19_50","18_50","17_50","16_50","15_50",
    "14_50","13_50","12_50","11_50","10_50"];

//描画sizeは、ブラウザ全体
var wSizeWidth  = window.innerWidth;
var wSizeHeight = window.innerHeight;

//迷路内を移動する自分自身
var selfData = new Object();
selfData.angle  = 270;
selfData.height = 2;
selfData.speed  = 5;

//スタート位置
var startPos = "48_48";
var sPos = startPos.split("_");
selfData.posX   = Number(sPos[0]);
selfData.posY   = Number(sPos[1]);

//地図データ
var boxObjArray = [];

for(var i = 0; i < mapData.length; i++){
    var wall = mapData[i].split("_"); //XとZのデータを分割
    
    boxObjArray[i] = createBoxObject(
        [Number(wall[0]), 1, Number(wall[1]) ]
    );
}

var thrBoxArray = [];
var selfObj;

//Texture---------------------------------------------------------------
var ground_texture = THREE.ImageUtils.loadTexture( 'img/ground.jpg' );
ground_texture.wrapS = ground_texture.wrapT = THREE.RepeatWrapping;
ground_texture.repeat.set( 64, 64 );

var wall_texture = THREE.ImageUtils.loadTexture( 'img/wall.jpg' );

var world = setPhysics(); //物理情報設定

var ret_vals = setView();
var renderer = ret_vals[0];
var scene    = ret_vals[1];
var camera   = ret_vals[2];

animate();

function setPhysics() {
    var world = new CANNON.World();
    world.gravity.set(0, -9.82, 0);   //重力設定
    
    //衝突している剛体の判定.
    //物理演算は処理の重いものなので、全てを計算するのではなく、
    //ぶつかっている可能性のあるものをピックアップし、
    //その後実際に計算.
    // https://qiita.com/o_tyazuke/items/3481ef1a31b2a4888f5d
    world.broadphase = new CANNON.NaiveBroadphase();
    world.solver.iterations = 10;     //反復計算回数
    world.solver.tolerance = 0.1;     //許容値
    
    //地面を作成
    var groundMat = new CANNON.Material('groundMat');
    groundMat.friction    = 0.3;  //摩擦係数
    groundMat.restitution = 0.5;  //反発係数
    
    var phyPlane = new CANNON.Body({mass: 0});
    phyPlane.material = groundMat;
    phyPlane.addShape(new CANNON.Plane());
    //回転
    phyPlane.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2 );
    
    world.add(phyPlane); //物理世界に追加
    
    //Cannon Box--------------------------------------------------------------
    //壁オブジェクト
    var canBoxArray = [];
    for( cnt=0; cnt<boxObjArray.length; cnt++){
        canBoxArray[cnt] = makeCannonBox(
            [boxObjArray[cnt].posX,
             boxObjArray[cnt].posY,
             boxObjArray[cnt].posZ],
            
            [boxObjArray[cnt].sizeX,
             boxObjArray[cnt].sizeY,
             boxObjArray[cnt].sizeZ],
            
            [boxObjArray[cnt].velocX,
             boxObjArray[cnt].velocY,
             boxObjArray[cnt].velocZ],
            
            [boxObjArray[cnt].angVelocX,
             boxObjArray[cnt].angVelocY,
             boxObjArray[cnt].angVelocZ],
            
            boxObjArray[cnt].mass,
            boxObjArray[cnt].dampVal);
        
        world.add(canBoxArray[cnt]);
    }
    
    //自分自身を表す球体オブジェクトを作成
    sphereShape = new CANNON.Sphere(1); //半径1の球体を作成
    
    var sphereMat = new CANNON.Material('sphereMat');
    sphereMat.friction = 0.8;       //摩擦係数
    sphereMat.restitution = 0.5;    //反発係数
    
    selfObj = new CANNON.Body({mass: 1});      //ボディを作成
    selfObj.material = sphereMat;              //ボディにマテリアルを設定
          
    selfObj.addShape(sphereShape);         //球体を作成
    selfObj.position.x = selfData.posX;    //初期位置を設定
    selfObj.position.y = selfData.height;  //初期位置を設定
    selfObj.position.z = selfData.posY;    //初期位置を設定
    world.add(selfObj); //物理世界に追加
    
    return world;
}

function setView() {
    var scene = new THREE.Scene();                //Three.jsの世界(シーン)を作成
    scene.fog = new THREE.Fog(0x000000, 1, 100);  //フォグ(黒色)を作成
    
    //カメラ
    var camera = new THREE.PerspectiveCamera(90, 800 / 600, 0.1, 10000);
    //カメラの位置を設定
    camera.position.set(Math.cos(Math.PI / 5) * 30, 5, Math.sin(Math.PI / 5) * 80);
    changeLookAt( camera );  //カメラの注視点を設定
          
    scene.add(camera);
          
    //ライト
    var light = new THREE.DirectionalLight(0xffffff, 0.5);
    light.position.set(10, 10, -10);      //光源位置
    light.castShadow = true;              //影を作る
    light.shadowMapWidth     = 2024;      //影の精細さ(解像度)
    light.shadowMapHeight    = 2024;
    light.shadowCameraLeft   = -50;       //ライト視点方向の影の表示度合い
    light.shadowCameraRight  = 50;
    light.shadowCameraTop    = 50;
    light.shadowCameraBottom = -50;
    light.shadowCameraFar = 100;      //影の範囲
    light.shadowCameraNear = 0;
    light.shadowDarkness = 0.5;       //影の透明度
    scene.add(light);
    
    var amb   = new THREE.AmbientLight(0xffffff);  //全体に光を当てる光源
    scene.add(amb);
          
    //壁オブジェクト作成
    for( cnt2=0; cnt2<boxObjArray.length; cnt2++){

        thrBoxArray[cnt2] = makeThreeBox(
            [boxObjArray[cnt2].posX,
             boxObjArray[cnt2].posY,
             boxObjArray[cnt2].posZ ],
            [boxObjArray[cnt2].sizeX,
             boxObjArray[cnt2].sizeY,
             boxObjArray[cnt2].sizeZ] );
        
        scene.add(thrBoxArray[cnt2]);
    }
    
    //地面の形状
    var graMeshGeometry = new THREE.PlaneGeometry(300, 300);
    var graMaterial = new THREE.MeshBasicMaterial({
        map: ground_texture
    });
    
    var viewPlane = new THREE.Mesh(graMeshGeometry, graMaterial);
    viewPlane.rotation.x = -Math.PI / 2;  //地面を回転
    viewPlane.position.y = 1 / 2;         //地面の位置を設定
    viewPlane.receiveShadow = true;       //地面に影を表示する
    scene.add(viewPlane);
    
    //レンダラー
    var renderer = new THREE.WebGLRenderer({antialias: true});
    //描画sizeは、ブラウザ全体
    renderer.setSize(wSizeWidth, wSizeHeight);
    
    renderer.setClearColor(0xffffff, 1);
    renderer.shadowMapEnabled = true;
    document.body.appendChild(renderer.domElement);
    
    renderer.render(scene, camera);
    return [renderer, scene, camera];
}

function animate() {
    requestAnimationFrame(animate);
    // 物理エンジンの時間を進行
    world.step(1 / 60);
    
    //カメラ位置の設定
    camera.position.set(selfObj.position.x, selfObj.position.y + 1.6, selfObj.position.z);
    // レンダリング
    renderer.render(scene, camera);
}

//操作ボタン
changeBtnPos(wSizeWidth,wSizeHeight);  //ボタン位置の変更

$('#forward').click(function(e) { forward(); });
$('#stop').click(function(e) { stop(); });
$('#back').click(function(e) { back(); });
$('#jump').click(function(e) { 
    stop();
    selfObj.velocity.y = 10; //ジャンプ時の上方向加速度
});
$('#turn_right').click(function(e) {
    selfData.angle += 5;
    stop();
    changeLookAt(camera);
});
$('#turn_left').click(function(e) {
    selfData.angle -= 5;
    stop();
    changeLookAt(camera);
});

//前進
function forward(){
    var theta = selfData.angle / 180 * Math.PI;
    selfObj.velocity.x = Math.cos(theta) * selfData.speed;
    selfObj.velocity.z = Math.sin(theta) * selfData.speed;
}

//停止
function stop(){
    selfObj.velocity.x = 0;
    selfObj.velocity.z = 0;
}

//後進
function back(){
    var theta = selfData.angle / 180 * Math.PI;
    selfObj.velocity.x = -1 * Math.cos(theta);
    selfObj.velocity.z = -1 * Math.sin(theta);
}

//注視点を設定
function changeLookAt(camera){
    var theta = selfData.angle / 180 * Math.PI;
    var posX = selfData.posX + Math.cos(theta) * 10000;
    var posY = selfData.posY + Math.sin(theta) * 10000;
    camera.lookAt(new THREE.Vector3(posX, 0, posY));
}

//ボタン位置の変更
function changeBtnPos(wSizeWidth,wSizeHeight){
    var btn_size = 100;
    
    //set button position
    $('#turn_right').css('top', wSizeHeight - (btn_size + 5));
    $('#turn_right').css('left', (btn_size + 10));
    $('#turn_left').css('top', wSizeHeight - (btn_size + 5));
    $('#turn_left').css('left', 5);
    $('#back').css('top', (wSizeHeight - btn_size) - 5);
    $('#back').css('left', (wSizeWidth - btn_size) - 5);
    $('#stop').css('top', (wSizeHeight - (btn_size * 2)) - (5 * 2));
    $('#stop').css('left', (wSizeWidth - btn_size) - 5);
    $('#forward').css('top', (wSizeHeight - (btn_size * 3)) - (5 * 3));
    $('#forward').css('left', (wSizeWidth - btn_size) - 5);
    $('#jump').css('top', (wSizeHeight - (btn_size * 4)) - (5 * 4));
    $('#jump').css('left', (wSizeWidth - btn_size) - 5);
}

// mode==0
function makeThreeBox( pos_xyz, size_xyz ){
    
    var retObj = null;
    var thrBox = null;
    var canBox = null;
    
    //Three.jsのオブジェクトを作成
    thrBox = new THREE.Mesh(
        new THREE.BoxGeometry(size_xyz[0],size_xyz[1],size_xyz[2], 10, 10),
        new THREE.MeshBasicMaterial( {map: wall_texture,
                                      //color: color
                                     })
    );
    
    thrBox.castShadow = true;
    thrBox.receiveShadow = true;
    thrBox.position.x = pos_xyz[0];
    thrBox.position.y = pos_xyz[1] + ( pos_xyz[1] /2 );
    thrBox.position.z = pos_xyz[2];
    
    return thrBox;
}

function makeCannonBox(
    pos_xyz,
    size_xyz,
    veloc_xyz,
    angVeloc_xyz,
    mass,
    dampVal){
    
    //cannon.jsのオブジェクトを作成
    var canBox = new CANNON.Body({mass: mass});
    canBox.addShape(
        new CANNON.Box(
            new CANNON.Vec3(size_xyz[0]/2, size_xyz[1]/2, size_xyz[2]/2)
        )
    );
    
    canBox.position.set(pos_xyz[0],pos_xyz[1],pos_xyz[2]);
    canBox.velocity.set(veloc_xyz[0],veloc_xyz[1],veloc_xyz[2]);
    canBox.angularVelocity.set(
        angVeloc_xyz[0],angVeloc_xyz[1],angVeloc_xyz[2] );
    
    canBox.angularDamping = dampVal;
    return canBox;
}

function createBoxObject( pos_xyz ){
    
    var box = {};
    box.posX = pos_xyz[0];
    box.posY = pos_xyz[1];
    box.posZ = pos_xyz[2];
    box.sizeX = 1;
    box.sizeY = 3;
    box.sizeZ = 1;
    box.velocX    = box.velocY    = box.velocZ    = 0;
    box.angVelocX = box.angVelocY = box.angVelocZ = 0;
    box.mass    = 0;
    box.dampVal = 0;
    box.color   = 0x000000;
    
    return box;
}

aws s3 の 静的ウェブサイトホスティング に 独自domain + via cloud front

独自domain & aws s3 の 静的ウェブサイトホスティング に対し、 httpsを有効にするには、cloud front 経由とする必要がありますが、 cloud frontの構成は、自身で構築実績がなかった為、hands-on 。

▲以下に順を記載していますが、細かい点で理解できていない部分もあります。

▲httpへの接続requestを強制的?に、https化したかったのですが、実施方法不明

目次

全体構成

cloud front 経由の構築に慣れていませんので、以下のステップで進めます。

※太線部は、前STEPからの変更箇所です

【STEP 1】AWS S3 による直接、serve

┌──────────────────────────────────┐
│【AWS S3】                                                          │
│bucket name: s3webtest.end0tknr.com                                 │
│url; s3webtest.end0tknr.com.s3-website-ap-northeast-1.amazonaws.com │
└─┬────────────────────────────────┘
    │http
┌─┴────────────────────────────────┐
│【USER】                                                            │
└──────────────────────────────────┘

【STEP 2】独自domainで AWS S3 による直接、serve

┌──────────────────────────────────┐
│【AWS S3】                                                          │
│bucket name: s3webtest.end0tknr.com                                 │
│url; s3webtest.end0tknr.com.s3-website-ap-northeast-1.amazonaws.com │
└─┬────────────────────────────────┘
    │http
┏━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃【Route 53】DNS                                                     ┃
┃s3webtest.end0tknr.com                                              ┃
┃ -(CNAME)-> s3-website-ap-northeast-1.amazonaws.com                 ┃
┗━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
    │http
┌─┴────────────────────────────────┐
│【USER】                                                            │
└──────────────────────────────────┘

【STEP 3】Cloud Front経由で AWS S3 による https、serve

┌──────────────────────────────────┐
│【AWS S3】                                                          │
│bucket name: s3webtest.end0tknr.com                                 │
│url; s3webtest.end0tknr.com.s3-website-ap-northeast-1.amazonaws.com │
└─┬────────────────────────────────┘
    │http
┏━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃【Cloud Front】CDN                                                  ┃
┃Origin domain:                                                      ┃
┃  s3webtest.end0tknr.com.s3.ap-northeast-1.amazonaws.com            ┃
┗━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
    │http(s)
┌─┴────────────────────────────────┐
│【USER】                                                            │
└──────────────────────────────────┘

【STEP 4】独自domain & https & Cloud Front経由で AWS S3 による serve

┌──────────────────────────────────┐
│【AWS S3】                                                          │
│bucket name: s3webtest.end0tknr.com                                 │
│url; s3webtest.end0tknr.com.s3-website-ap-northeast-1.amazonaws.com │
└─┬────────────────────────────────┘
    │http
┌──────────────────────────────────┐    
│【Cloud Front】CDN                                                  │
│Origin domain:                                                      │
│  s3webtest.end0tknr.com.s3.ap-northeast-1.amazonaws.com            │
└─┬────────────────────────────────┘
    │http(s)
┏━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃【Route 53】DNS                                                     ┃
┃s3webtest.end0tknr.com                                              ┃
┃ -(CNAME)-> s3-website-ap-northeast-1.amazonaws.com                 ┃
┗━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
┏━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃【 Certificate Manager】SSL                                         ┃
┃for s3webtest.end0tknr.com at N. Virginia , us-east-1               ┃
┗━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
    │http(s)
┌─┴────────────────────────────────┐
│【USER】                                                            │
└──────────────────────────────────┘

【STEP 1】AWS S3 による直接、serve

【STEP 1.1】独自domainと同名称で aws s3 bucket作成

します。

今回は、「独自domain」=「バケット名」=「s3webtest.end0tknr.com」で進めます。

バケット名以外の「プロパティ」や「アクセス許可」は、 この後、設定変更しますので、全てdefaultで作成してかまいません。

f:id:end0tknr:20210811162220p:plain

【STEP 1.2】バケットプロパティで、静的ウェブサイトホスティング有効化

します。

バケットプロパティ画面の下部に、静的ウェブサイトホスティングの欄がありますので、 以下の内容で設定してください。

項目 内容
静的ウェブサイトホスティング 有効にする
ホスティングタイプ 静的ウェブサイトをホストする
インデックスドキュメント index.html error.html
リダイレクトルール オプション ※ 以下、参照

今回の場合、リダイレクトルール オプションにおいて ファイルが見つからない404エラー時に、error.html へredirectさせています。

[
  { "Condition": {"HttpErrorCodeReturnedEquals": "404"},
    "Redirect" : {"ReplaceKeyWith": "error.html"      } }
]

その他のS3によるリダイレクトルールは、 例えば、以下のurlが分かりやすいです。

s3でURLリダイレクトするときの設定まとめ - Qiita

f:id:end0tknr:20210811162905p:plain

↑この画面を下へスクロースすると、↓この設定欄が現れます。

f:id:end0tknr:20210811162954p:plain

f:id:end0tknr:20210811163025p:plain

【STEP 1.3】バケットアクセス許可 設定

「このバケットのブロックパブリックアクセス設定」では 【内容を理解していませんが】全て OFFとしています。

f:id:end0tknr:20210811163106p:plain

また「バケットポリシー」では、以下を設定します。

※ Resource にあるバケット名以外は、他でも流用可能なルールです。

{ "Version":"2012-10-17",
  "Statement":[
    { "Sid":"TestAwsS3WebHosting",
      "Effect":"Allow",
      "Principal": "*",
      "Action":["s3:GetObject"],
      "Resource":["arn:aws:s3:::s3webtest.end0tknr.com/*"] }
  ]
}

f:id:end0tknr:20210811163129p:plain

バケットプロパティにおける静的ウェブサイトホスティング設定が完了すると、 以下のようなurlが発行されます。

http://s3webtest.end0tknr.com.s3-website-ap-northeast-1.amazonaws.com

f:id:end0tknr:20210811163140p:plain

【STEP 1.4】index.html , error.html をupload し、接続テスト

先程のSTEP1.3までで、s3 のみの web hosting設定は完了していますので、 index.html , error.html をupload し、ブラウザで http://s3webtest.end0tknr.com.s3-website-ap-northeast-1.amazonaws.com へ、アクセスし、index.html , error.htmlが閲覧できることを確認してください。

f:id:end0tknr:20210811163609p:plain

【STEP 2】独自domainで AWS S3 による直接、serve

STEP1で作成した S3 に独自domainでアクセスする為、 Route53で以下のように設定するだけです。

設定完了後、独自ドメインである http://s3webtest.end0tknr.com へ、 アクセスできることを確認してください。

項目 内容
レコード名 s3webtest.end0tknr.com
レコードタイプ CNAME
s3-website-ap-northeast-1.amazonaws.com

f:id:end0tknr:20210811163639p:plain

【STEP 3】Cloud Front経由で AWS S3 による https、serve

このステップでは、Cloud Front → s3 連携を有効にし、 Cloud Front独自のurlで s3へ、アクセスできる状態にします。

【STEP 3.1】Create distribution

Cloud Frontの管理画面にある「Create distribution」ボタンをクリックし、 以下の内容で設定します。

※ 実際の設定画面には、以下の他に様々な項目がありますが、 以下に記載がないものは、デフォルト値で問題ないと思います。

Origin

項目
Origin domain s3webtest.end0tknr.com.s3.ap-northeast-1.amazonaws.com
Origin path null
Name s3webtest.end0tknr.com.s3.ap-northeast-1.amazonaws.com
S3 bucket access No
Enable Origin Shield No

f:id:end0tknr:20210811163925p:plain

Default cache behavior

項目
Path pattern Default (*)
Viewer protocol policy HTTP and HTTPS
Allowed HTTP methods GET, HEAD

f:id:end0tknr:20210811163935p:plain

Settings

項目
Price class Use all edge locations
AWS WAF web ACL none
Supported HTTP versions HTTP/2 On
Default root object index.html

f:id:end0tknr:20210811163948p:plain

【STEP 3.2】~.cloudfront.net への接続テスト

上記 STEP 3.1 により、Cloud Frontによるurlが発行されますので、 http , https のそれぞれで接続テストを行って下さい。以下、url例

【STEP 4】独自domain & https & Cloud Front経由で AWS S3 による serve

最後に、Cloud Frontに対し、独自domainや、ssl証明書を紐づけます。

【STEP 4.1】dns設定変更

まず、Route53で作成済の s3web.end0tknr.com に関する dnsレコードを変更します。

項目
レコード名 s3webtest.end0tknr.com
レコードタイプ CNAME
値 【old】 s3-website-ap-northeast-1.amazonaws.com
〃 【new】 d1ytryj2mno8x5.cloudfront.net

【STEP 4.2】SSL証明書の発行 @ US East (N. Virginia , us-east-1)

cloud frontで独自ドメインを利用するには SSL証明書が必要ですので、 Croud Frontの設定画面の Custom SSL certificate 直下にある 「Request certificate」リンクをクリックし、 以下の画面のようにSSL証明書DNS検証を行ってください。

▲ 画面の注釈の通り、証明書は、 US East (N. Virginia , us-east-1).にある必要があります。

f:id:end0tknr:20210811164049p:plain

s3web.end0tknr.com を追加し、次の画面で「DNSの検証」を実行。

DNSの検証」完了後、「Custom SSL certificate」にて取得できたssl証明書を選択.

f:id:end0tknr:20210811164101p:plain ▼▼▼▼ f:id:end0tknr:20210811164111p:plain ▼▼▼▼ f:id:end0tknr:20210811164120p:plain ▼▼▼▼ f:id:end0tknr:20210811164128p:plain ▼▼▼▼ f:id:end0tknr:20210811164137p:plain ▼▼▼▼ f:id:end0tknr:20210811164158p:plain

【STEP 4.3】cloud front ( Edit distribution )

SSL証明書が発行されば、Croud Frontの設定画面で、 CNAME & SSL証明書を登録し、全ての作業が完了です。

f:id:end0tknr:20210811164210p:plain

【STEP 4.4】s3webtest.end0tknr.com への接続テスト

最後に http , https のそれぞれで接続テストを行って下さい。以下、url例

lenovo thinkpad x1 carbon のタッチパッド ゼスチャにブラウザの戻るを割り当てる

設定 > デバイス から、タッチパッド の設定画面を表示し、 そのタッチパッド設定画面下部にある「ジェスチャの詳細な構成」で 「後方ナビゲーション」を選択すれば、OK

タッチパッド」設定画面下部にある「ジェスチャの詳細な構成」リンク

f:id:end0tknr:20210810065411p:plain

「ジェスチャの詳細な構成」画面にある「3本指ジェスチャの構成」

f:id:end0tknr:20210810065422p:plain

f:id:end0tknr:20210810065435p:plain

reset.css ≒ normalize.css ≒ sanitize.css

メモ。

この手のcssは、昔から、名前を変えながら、同様のものがいくつもあります。

CDNで利用できるもののうち、例えば、以下のようなものがあります。

cookieチケットを要するサイトに対しての wget による crawl

STEP 1 - cookie file の準備

wgetの --save-cookies オプション により、以下のようなタブ区切りファイルで cookieが保存されますが、同様の書式で cookies.txt を準備します。

# HTTP Cookie File
# Generated by Wget on 2021-07-29 13:22:10.
# Edit at your own risk.

.sexy.co.jp FALSE   /   FALSE   1830124283  neoxmileauthticket3766  hogehogeticket
.sexy.co.jp FALSE   /   FALSE   1830124283  neoxmilesearchinittype  user

STEP 2 - wgetの実行

以下の通りで、 cygwin に付属する wget でも同様に動作します。

$ wget
  --mirror \
  --keep-session-cookies \
  --load-cookies=cookies.txt \
  --secure-protocol tlsv1 \
  --no-check-certificate \
  --random-wait \
  --tries=5 \
  --timestamping \
  --convert-links \
  --convert-file-only \
  --page-requisites \
  --reject=pdf,zip,xls,xlsx,xlsm,ppt,pptx,doc,docx,mp3,mp4,wmv,wav,mov,mpg,tif,exe \
  --user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0" \
  http://www.jbnk.sexy.co.jp/jb/index_a.html

install mysql5.7 to rhel8

更?に、上記2 entryの続きです。

内容は、上記entryに似ていますが、osが前回のcent に対し、今回のrhelですので、 微妙に異なる点がありました。

Step 1 - 依存パッケージのインストール

以下を yum インストール。

$ sudo yum install gcc
$ sudo yum install git
$ sudo yum install openssl-devel
$ sudo yum install ncurses-devel
$ sudo yum install libtirpc-devel

Step 2 - 依存パッケージのインストール (その2)

cmakeは、新しいverが欲しかった為、srcより、インストール。

$ wget https://github.com/Kitware/CMake/releases/download/v3.21.0/cmake-3.21.0.tar.gz
$ tar -xvf cmake-3.21.0.tar.gz
$ cd cmake-3.21.0
$ ./configure
$ make
$ make test
$ sudo make install

centosにおいて rpcgen ( rpcsvc-proto )は、devtoolset に含まれるようですが、 rhelにおける devtoolset のインストールが不明でしたので、srcよりインストール。

$ sudo yum install automake
$ sudo yum install gettext*

$ wget https://github.com/thkukuk/rpcsvc-proto/archive/refs/tags/v1.4.2.tar.gz
$ tar -xvf v1.4.2.tar.gz
$ cd rpcsvc-proto-1.4.2
$ ./autogen.sh
$ ./configure
$ make
$ make test
$ sudo make install

Step 3 - mysql5.7のインストール

$ tar -xvf mysql-boost-5.7.34.tar.gz
$ cd /home/end0tknr/tmp/mysql-5.7.34
$ /usr/local/bin/cmake . \
   -DCMAKE_INSTALL_PREFIX=/usr/local/mysql \
   -DDEFAULT_CHARSET=utf8 \
   -DDEFAULT_COLLATION=utf8_general_ci \
   -DENABLED_LOCAL_INFILE=true \
   -DWITH_INNOBASE_STORAGE_ENGINE=1 \
   -DWITH_EXTRA_CHARSETS=all \
   -DWITH_READLINE=ON \
   -DDOWNLOAD_BOOST=ON \
   -DWITH_BOOST=/home/end0tknr/tmp/mysql-5.7.34/boost \
   -DFORCE_INSOURCE_BUILD=1 \
   -DWITH_SYSTEMD=ON

$ make
$ make test
$ sudo make install

Step 4 - my.cnf の作成

mysql5.7 をsrcからinstall - end0tknr's kipple - web写経開発

上記エントリと同様ですので、省略します。

Step 5 - user & group 追加

$ sudo groupadd mysql
$ sudo useradd -r -g mysql mysql

Step 6 - ディレクトリ作成

以前、行った際のosと異なることの影響でしょうか、 いくつか mkdir しました。

# su - mysql
$ mkdir /home/mysql/data
# mkdir /var/run/mysqld
# chown mysql:mysql /var/run/mysqld

Step 7 - データベースの初期化

$ sudo su - mysql
$ /usr/local/mysql/bin/mysqld --initialize

Step 8 - 自動起動の設定と、データベースの起動

# cd /etc/systemd/system
# cp /home/end0tknr/tmp/mysql-5.7.34/scripts/mysqld.service .

# systemctl enable mysqld
# systemctl start  mysqld

Step 9 - 接続テスト

mysql8と異なり、pwなしで、すぐに接続できます。

$ /usr/local/mysql/bin/mysql

mysql> create database test;
Query OK, 1 row affected (0.00 sec)

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| test               |
+--------------------+
5 rows in set (0.01 sec)

install tomcat6 to rhel8

install java6 to rhel8 , use via alternatimes - end0tknr's kipple - web写経開発

上記エントリの続きです。

Step1 - インストール

と言っても、apache-tomcat-6.0.53.tar.gz のダウンロード & 解凍のみ。

$ sudo su -
# cd /usr/local
# mkdir tomcat
# cd tomcat

# wget https://archive.apache.org/dist/tomcat/tomcat-6/v6.0.53/bin/apache-tomcat-6.0.53.tar.gz
# tar -xvf apache-tomcat-6.0.53.tar.gz

Step 2 - alternatives にtomcatを登録

# alternatives --install /usr/local/tomcat_home tomcat_home /usr/local/tomcat/apache-tomcat-6.0.53 1

# alternatives --config tomcat_home

Step 3 - tomcat環境変数

特にJAVA_HOMEを必要としますので、setenv.sh を作成

# /usr/local/tomcat_home/bin/setenv.sh
export JAVA_HOME=/usr/local/java_home
export PATH=$JAVA_HOME/bin:$PATH
export CATALINA_HOME=/usr/local/tomcat_home/

# chmod 755 /usr/local/tomcat_home/bin/setenv.sh

Step 4 - 実行ユーザであるtomcatを追加し、自動起動設定

# useradd -s /sbin/nologin tomcat
# chown -R tomcat:tomcat /usr/local/tomcat
# vi /etc/systemd/system/tomcat.service

[Unit]
Description=Apache Tomcat 6
After=network.target

[Service]
User=tomcat
Group=tomcat
Type=oneshot
PIDFile=/usr/local/tomcat_home/tomcat.pid
RemainAfterExit=yes

ExecStart=/usr/local/tomcat_home/bin/startup.sh
ExecStop=/usr/local/tomcat_home/bin/shutdown.sh

[Install]
WantedBy=multi-user.target


# chmod 755 /etc/systemd/system/tomcat.service

# systemctl enable tomcat
# systemctl start  tomcat

Step 5 - manager-guiを使用する場合、tomcat-users.xml を編集

# vi /usr/local/tomcat_home/conf/tomcat-users.xml

以下、2行を追加

<role rolename="manager-gui"/>
<user username="admin" password="ないしょ" roles="manager-gui"/>

# systemctl restart tomcat

以上

install java6 to rhel8 , use via alternatimes

Step 0 - インストール先

$ cat /etc/redhat-release 
Red Hat Enterprise Linux release 8.4 (Ootpa)
$ uname --all
Linux rhel8.a5.jp 4.18.0-305.el8.x86_64 #1 SMP
Thu Apr 29 08:54:30 EDT 2021 x86_64 x86_64 x86_64 GNU/Linux

Step 1 - ダウンロード

download java6 from https://www.oracle.com/java/technologies/oracle-java-archive-downloads.html

Step 2 - javaをインストール

と言っても、自己解凍ファイルの jdk-6u45-linux-x64.bin を実行するだけ。

$ sudo su -
# cd /usr/local
# mkdir java
# cd java

# cp ~end0tknr/tmp/jdk-6u45-linux-x64.bin .
# ./jdk-6u45-linux-x64.bin 
Unpacking...
Checksumming...
Extracting...
UnZipSFX 5.50 of 17 February 2002, by Info-ZIP (Zip-Bugs@lists.wku.edu).
   creating: jdk1.6.0_45/
   creating: jdk1.6.0_45/db/
   creating: jdk1.6.0_45/db/bin/
  inflating: jdk1.6.0_45/db/bin/ij   
  inflating: jdk1.6.0_45/db/bin/NetworkServerControl  
  inflating: jdk1.6.0_45/db/bin/setNetworkClientCP.bat  
  inflating: jdk1.6.0_45/db/bin/setEmbeddedCP.bat
  <略>
   creating: jdk1.6.0_45/include/linux/
  inflating: jdk1.6.0_45/include/linux/jawt_md.h  
  inflating: jdk1.6.0_45/include/linux/jni_md.h  
  inflating: jdk1.6.0_45/include/jvmti.h  
  inflating: jdk1.6.0_45/include/jawt.h  
  inflating: jdk1.6.0_45/include/jdwpTransport.h  
  inflating: jdk1.6.0_45/include/classfile_constants.h  
  inflating: jdk1.6.0_45/COPYRIGHT   
Creating jdk1.6.0_45/jre/lib/rt.jar
Creating jdk1.6.0_45/jre/lib/jsse.jar
Creating jdk1.6.0_45/jre/lib/charsets.jar
Creating jdk1.6.0_45/lib/tools.jar
Creating jdk1.6.0_45/jre/lib/ext/localedata.jar
Creating jdk1.6.0_45/jre/lib/plugin.jar
Creating jdk1.6.0_45/jre/lib/javaws.jar
Creating jdk1.6.0_45/jre/lib/deploy.jar
 
Done.
#

Step 3 - libnsl.so.1 を追加インストール

rhel8では、libnsl.so が含まれなくなったらしく、 以下を参考に yum で、これをインストール。

RHEL 8へのインストール中に「libnsl.so.1 が必要ですが、見つかりません」メッセージが表示される

$ /usr/local/java/jdk1.6.0_45/bin/java -version
Error occurred during initialization of VM
Unable to load native library: libnsl.so.1:
  cannot open shared object file: No such file or directory


$ sudo yum install /lib64/libnsl.so.1

$ /usr/local/java/jdk1.6.0_45/bin/java -version
java version "1.6.0_45"
Java(TM) SE Runtime Environment (build 1.6.0_45-b06)
Java HotSpot(TM) 64-Bit Server VM (build 20.45-b01, mixed mode)

Step 4 - alternatives にjavaを登録

# alternatives --install /usr/local/java_home java_home /usr/local/java/jdk1.6.0_45 1

# alternatives --config java_home
There is 1 program that provides 'java_home'.
  Selection    Command
-----------------------------------------------
*+ 1           /usr/local/java/jdk1.6.0_45


# vi /etc/bashrc
 
export JAVA_HOME=/usr/local/java_home
export PATH=$PATH:$JAVA_HOME/bin
export CLASSPATH=.:$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/dt.jar


$ which java
/usr/local/java_home/bin/java

rhel8試用に伴う Red Hat サブスクリプションの有効化

Red Hat Enterprise Linux 8 を試用しますが、今後もきっと、忘れるので、メモ

rhel8の初期状態では、repositoryを参照できず、yum を使用できません

$ cat /etc/redhat-release 
Red Hat Enterprise Linux release 8.4 (Ootpa)

$ sudo yum install samba

Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered to Red Hat Subscription Management. You can use subscription-manager to register.

Error: There are no enabled repositories in "/etc/yum.repos.d", "/etc/yum/repos.d", "/etc/distro.repos.d".

以下のコマンドにより、Red Hat の サブスクリプション & repository の有効化

ブラウザで redhatへ、アクセスすることでも可能なようですが、試していません

$ sudo subscription-manager register
[sudo] password for end0tknr: 
Registering to: subscription.rhsm.redhat.com:443/subscription
Username: end0tknr
Password: 
The system has been registered with ID: 74cc7463-bffe-4e49-b8c6-f97088a30def
The registered system name is: rhel8.a5.jp
[end0tknr@rhel8 tmp]$ sudo subscription-manager list
+-------------------------------------------+
    Installed Product Status
+-------------------------------------------+
Product Name:   Red Hat Enterprise Linux for x86_64
Product ID:     479
Version:        8.4
Arch:           x86_64
Status:         Not Subscribed
Status Details: Not supported by a valid subscription.
Starts:         
Ends:           

$ sudo subscription-manager list --available
+-------------------------------------------+
    Available Subscriptions
+-------------------------------------------+
Subscription Name:   Red Hat Developer Subscription for Individuals
Provides:            Red Hat Developer Tools (for RHEL Server for ARM)
                     Red Hat Software Collections (for RHEL Server for ARM)
                     Red Hat Ansible Engine
                     JBoss Enterprise Application Platform from RHUI
                     Red Hat CodeReady Linux Builder for IBM z Systems - Extended Update
                     Support
                     Red Hat JBoss AMQ Interconnect
                     JBoss Enterprise Application Platform
                     JBoss Enterprise Web Server from RHUI
                     Red Hat Enterprise Linux for IBM z Systems - Extended Update Support
                     Red Hat Container Development Kit
                     Red Hat Beta
                     RHEL for SAP (for IBM Power LE) - Update Services for SAP Solutions
                     Red Hat OpenShift Container Platform
                     MRG Realtime
                     Red Hat JBoss Data Grid
                     dotNET on RHEL (for RHEL Server)
                     Red Hat CodeReady Linux Builder for x86_64 - Extended Update Support
                     Red Hat OpenShift Enterprise JBoss A-MQ add-on
                     Red Hat OpenShift Application Runtimes Beta
                     Red Hat Enterprise Linux High Availability - Update Services for SAP
                     Solutions
                     Oracle Java (for RHEL Server)
                     Red Hat Enterprise Linux Resilient Storage for x86_64
                     Red Hat Software Collections (for RHEL Server)
                     Red Hat Enterprise Linux for ARM 64
                     JBoss Enterprise Web Platform
                     Red Hat Enterprise Linux for Real Time
                     Red Hat CodeReady Linux Builder for ARM 64
                     Red Hat Developer Tools (for RHEL Server)
                     Red Hat Developer Tools Beta (for RHEL Server)
                     Red Hat Enterprise Linux for x86_64
                     Red Hat Enterprise Linux Resilient Storage for IBM z Systems -
                     Extended Update Support
                     Red Hat Enterprise Linux High Performance Networking (for RHEL
                     Compute Node)
                     Red Hat S-JIS Support (for RHEL Server) - Extended Update Support
                     dotNET on RHEL Beta (for RHEL Server)
                     Red Hat CodeReady Linux Builder for x86_64
                     Red Hat Enterprise Linux for SAP HANA for x86_64
                     RHEL for SAP HANA - Update Services for SAP Solutions
                     Red Hat Enterprise Linux Scalable File System (for RHEL Server) -
                     Extended Update Support
                     RHEL for SAP HANA - Extended Update Support
                     Red Hat Container Images Beta
                     Red Hat Ansible Automation Platform
                     Red Hat Enterprise Linux Atomic Host Beta
                     Red Hat JBoss Core Services
                     Red Hat Container Images
                     Red Hat Enterprise Linux Load Balancer (for RHEL Server)
                     Red Hat Developer Suite v.3
                     Red Hat CodeReady Workspaces for OpenShift
                     Red Hat OpenShift Enterprise JBoss EAP add-on
                     Red Hat Openshift Application Runtimes for IBM Power LE
                     Red Hat EUCJP Support (for RHEL Server) - Extended Update Support
                     Red Hat Enterprise Linux High Availability for x86_64
                     Red Hat Enterprise Linux Load Balancer (for RHEL Server) - Extended
                     Update Support
                     Red Hat Openshift Application Runtimes
                     Red Hat Enterprise Linux High Availability (for IBM z Systems) -
                     Extended Update Support
                     Red Hat Enterprise Linux Resilient Storage for x86_64 - Extended
                     Update Support
                     Red Hat Enterprise Linux High Availability for x86_64 - Extended
                     Update Support
                     Red Hat Enterprise Linux Server - Update Services for SAP Solutions
                     Red Hat JBoss Middleware
                     Red Hat Migration Toolkit
                     Red Hat Enterprise Linux High Performance Networking (for RHEL
                     Server)
                     Red Hat Enterprise Linux Scalable File System (for RHEL Server)
                     Red Hat Enterprise Linux High Performance Networking (for RHEL
                     Server) - Extended Update Support
                     Red Hat Enterprise Linux EUS Compute Node
                     RHEL for SAP - Update Services for SAP Solutions
                     Oracle Java (for RHEL Server) - Extended Update Support
                     Red Hat Enterprise Linux Atomic Host
                     OpenJDK Java (for Middleware)
                     JBoss Operations Network from RHUI
                     Red Hat JBoss AMQ Clients
                     Red Hat 3scale API Management Platform
                     Red Hat Software Collections Beta (for RHEL Server)
                     Red Hat Enterprise Linux Server
                     Red Hat Enterprise Linux for SAP Applications for x86_64
                     Red Hat Enterprise Linux for x86_64 - Extended Update Support
                     RHEL for SAP - Extended Update Support
                     Red Hat Developer Toolset (for RHEL Server)
                     Red Hat Software Collections Beta (for RHEL Server for ARM)
                     Red Hat JBoss Core Services from RHUI
                     Red Hat Developer Tools Beta (for RHEL Server for ARM)
                     Red Hat OpenShift Enterprise JBoss FUSE add-on
                     Red Hat Build of Quarkus
                     Red Hat OpenShift Enterprise JBoss EAP add-on Beta
SKU:                 RH00798
Contract:            
Pool ID:             2c9280817a7d8db6017ab9bdb6281e5b
Provides Management: No
Available:           16
Suggested:           1
Service Type:        
Roles:               Red Hat Enterprise Linux Server
                     Red Hat Enterprise Linux Workstation
                     Red Hat Enterprise Linux Compute Node
Service Level:       Self-Support
Usage:               Development/Test
Add-ons:             
Subscription Type:   Standard
Starts:              07/18/2021
Ends:                07/18/2022
Entitlement Type:    Physical

Subscription Name:   60 Day Product Trial of Red Hat Enterprise Linux Server with Smart
                     Management, Monitoring, and all Add-Ons, Self-Supported (Physical or
                     Virtual Nodes)
Provides:            dotNET on RHEL Beta (for RHEL Server)
                     Red Hat Satellite
                     Red Hat CodeReady Linux Builder for x86_64
                     Red Hat Ansible Engine
                     Red Hat Enterprise Linux Scalable File System (for RHEL Server) -
                     Extended Update Support
                     Red Hat Container Images Beta
                     Red Hat Enterprise Linux Atomic Host Beta
                     Red Hat Container Images
                     Red Hat Enterprise Linux Load Balancer (for RHEL Server)
                     Red Hat Beta
                     Red Hat Enterprise Linux High Availability for x86_64
                     Red Hat Enterprise Linux Load Balancer (for RHEL Server) - Extended
                     Update Support
                     dotNET on RHEL (for RHEL Server)
                     Red Hat CodeReady Linux Builder for x86_64 - Extended Update Support
                     Red Hat Enterprise Linux Resilient Storage for x86_64 - Extended
                     Update Support
                     Red Hat Enterprise Linux High Availability for x86_64 - Extended
                     Update Support
                     Oracle Java (for RHEL Server)
                     Red Hat Enterprise Linux Resilient Storage for x86_64
                     Red Hat Software Collections (for RHEL Server)
                     Red Hat Satellite Capsule
                     Red Hat Enterprise Linux High Performance Networking (for RHEL
                     Server)
                     Red Hat Enterprise Linux Scalable File System (for RHEL Server)
                     Red Hat Enterprise Linux High Performance Networking (for RHEL
                     Server) - Extended Update Support
                     Oracle Java (for RHEL Server) - Extended Update Support
                     Red Hat Enterprise Linux Atomic Host
                     Red Hat Developer Tools (for RHEL Server)
                     Red Hat Software Collections Beta (for RHEL Server)
                     Red Hat Enterprise Linux Server
                     Red Hat Developer Tools Beta (for RHEL Server)
                     Red Hat Enterprise Linux for x86_64
                     Red Hat Enterprise Linux for x86_64 - Extended Update Support
                     Red Hat Developer Toolset (for RHEL Server)
SKU:                 RH00066
Contract:            12738638
Pool ID:             8a85f99f7aaf7dc9017ab861e8f33915
Provides Management: Yes
Available:           2
Suggested:           1
Service Type:        
Roles:               Red Hat Enterprise Linux Server
Service Level:       Self-Support
Usage:               Development/Test
Add-ons:             
Subscription Type:   Instance Based
Starts:              07/18/2021
Ends:                09/16/2021
Entitlement Type:    Physical

$ sudo subscription-manager subscribe --pool=8a85f99f7aaf7dc9017ab861e8f33915
※ subscribeは attach だったかも
Successfully attached a subscription for: 60 Day Product Trial of Red Hat Enterprise Linux Server with Smart Management, Monitoring, and all Add-Ons, Self-Supported (Physical or Virtual Nodes)

$ sudo subscription-manager list
+-------------------------------------------+
    Installed Product Status
+-------------------------------------------+
Product Name:   Red Hat Enterprise Linux for x86_64
Product ID:     479
Version:        8.4
Arch:           x86_64
Status:         Subscribed
Status Details: 
Starts:         07/18/2021
Ends:           09/16/2021

以上により、yum が可能になります

$ sudo yum install samba
Updating Subscription Management repositories.
Red Hat Enterprise Linux 8 for x86_64 - BaseOS (RPMs)      17 MB/s |  34 MB     00:02    
Red Hat Enterprise Linux 8 for x86_64 - AppStream (RPMs)   16 MB/s |  31 MB     00:01    
Dependencies resolved.
==========================================================================================
 Package               Arch      Version           Repository                        Size
==========================================================================================
Installing:
 samba                 x86_64    4.13.3-3.el8      rhel-8-for-x86_64-baseos-rpms    846 k
Installing dependencies:
 avahi-libs            x86_64    0.7-20.el8        rhel-8-for-x86_64-baseos-rpms     62 k
 cups-libs             x86_64    1:2.2.6-38.el8    rhel-8-for-x86_64-baseos-rpms    433 k
 libicu                x86_64    60.3-2.el8_1      rhel-8-for-x86_64-baseos-rpms    8.8 M
 libwbclient           x86_64    4.13.3-3.el8      rhel-8-for-x86_64-baseos-rpms    119 k
 samba-client-libs     x86_64    4.13.3-3.el8      rhel-8-for-x86_64-baseos-rpms    5.4 M
 samba-common          noarch    4.13.3-3.el8      rhel-8-for-x86_64-baseos-rpms    218 k
 samba-common-libs     x86_64    4.13.3-3.el8      rhel-8-for-x86_64-baseos-rpms    171 k
 samba-common-tools    x86_64    4.13.3-3.el8      rhel-8-for-x86_64-baseos-rpms    498 k
 samba-libs            x86_64    4.13.3-3.el8      rhel-8-for-x86_64-baseos-rpms    168 k

Transaction Summary
==========================================================================================
Install  10 Packages

Total download size: 17 M
Installed size: 58 M
Is this ok [y/N]: y
Downloading Packages:
<以降、省略>

urlの変化なしに画面遷移する場合において google analytics の UA(Universal Analytics) で Conversion通知

世の中、GA4への移行が進みつつありますが、 以下は前バージョンである UA(Universal Analytics analytics.js)の 内容です。

高度な設定 - ウェブ トラッキング(analytics.js)  |  Analytics for Web (analytics.js)  |  Google Developers

google analyticsUA(Universal Analytics) において コンバージョンの目標(到達ページ)を次のように指定した場合、 ブラウザで指定urlにアクセスすることで、GAにコンバージョンが通知されます。

ですが、入力→確認→完了のurlが変わらず、ページ遷移するような画面では、 以下のように ga('create',~); と ga('send','pageview',~); を呼ぶことで、 アクセスした(コンバージョンした)と見なしてくれるらしい。

<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
    
<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){
w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});
var f=d.getElementsByTagName(s)[0],
    j=d.createElement(s),dl=l!='dataLayer'?'&l='
    +l:'';j.async=true;j.src=
    'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-ないしょ');
</script><!-- End Google Tag Manager -->
</head>

<body>

<form>
  <div class="mb-3">
     <label for="exampleInputEmail1" class="form-label">Email address</label>
     <input type="email" class="form-control" id="exampleInputEmail1"
            aria-describedby="emailHelp"/>
     <div id="emailHelp" class="form-text">
       We'll never share your email with anyone else.</div>
  </div>

<button type="button" class="btn btn-primary"
        onClick="done_contact();">Submit</button>
</form>
          
<script>
function done_contact(){
  var dummy_path = location.pathname + '?status=comp';
  ga('create','UA-ないしょ-3','auto');
  ga('send', 'pageview', {'page': dummy_path});
}
</script>

</body>
</html>