曲线上的Three.js getTangent应该在曲线上移动时正确旋转我的对象

时间:2017-11-01 10:07:40

标签: javascript three.js coffeescript transformation trigonometry

它确实旋转,使用:

t = 0
animate = ->
  requestAnimationFrame animate

  t += 0.001
  # ...

  position3 = curveSeaRoute.getPoint(t+.3)
  tangent3 = curveSeaRoute.getTangent(t)
  ship.position.copy(position3)
  ship.lookAt(tangent3)
  # console.log tangent3

  renderer.render scene, camera
animate()

用我的船+路线'定义如下:

# Ship
centerLength = 2
bodyLength = 3
bodyWidth = 2
deckHeight = 0.1
geometry = new THREE.Geometry() 
v1 = new THREE.Vector3(1, deckHeight,0)
v2 = new THREE.Vector3(-2, deckHeight,2)
v3 = new THREE.Vector3(1, deckHeight,2)
v4 = new THREE.Vector3(-2, deckHeight,0)
v5 = new THREE.Vector3(-3, deckHeight, 1)
geometry.vertices.push(v1)
geometry.vertices.push(v2)
geometry.vertices.push(v3)
geometry.vertices.push(v4)
geometry.vertices.push(v5)
geometry.faces.push( new THREE.Face3( 0, 1, 2 ) )
geometry.faces.push( new THREE.Face3( 3, 1, 0 ) )
geometry.faces.push( new THREE.Face3( 3, 4, 1 ) )
material = new THREE.MeshBasicMaterial( { color: 0x005b1e } )
ship = new THREE.Mesh( geometry, material )
ship.translateZ 20 
ship.translateX 14.5
scene.add( ship )

# Sea route
curveSeaRoute = new THREE.CatmullRomCurve3([
  new THREE.Vector3( 18, roadHeight, 31 ),
  new THREE.Vector3( 24, roadHeight, 23 ),
  new THREE.Vector3( 22, roadHeight, 20 ),
  new THREE.Vector3( 14, roadHeight, 19 ),
  new THREE.Vector3( 10, roadHeight, 20 ),
  new THREE.Vector3( 8, roadHeight, 23 ),
  new THREE.Vector3( 10, roadHeight, 31 ),
] )

geometry = new THREE.Geometry()
geometry.vertices = curveSeaRoute.getPoints( 200 )
material = new THREE.LineBasicMaterial( { color : 0x45607c } )
curveObject = new THREE.Line( geometry, material )
scene.add( curveObject )

但是旋转的问题不是很顺利:

http://jsfiddle.net/CoderX99/66b3j9wa/6/

getTangent确实为我提供了有价值的数据,这些数据看起来像是曲线上的方向向量(正切)。现在我当然可以使用三角法来获得x轴和z值之外的y轴旋转角度,但我希望有一种更简单的方法 - Three.js方式。

此外,我希望旋转中心位于船的中间。看起来它现在就在后方的某个地方。

ANSWER

来自囚犯849。加上我自己的代码整理。

setOutPath = (path, color, segments ) ->
    geometry = new THREE.Geometry()
    geometry.vertices = path.getPoints( segments )
    material = new THREE.LineBasicMaterial( { color : color } )
    curveObject = new THREE.Line( geometry, material )
    scene.add( curveObject )

setOutPath(curveRoadNo1, 0xa9c41e, 500)
setOutPath(curveRoadNo2, 0xa9c41e, 200)
setOutPath(curveSeaRoute, 0x45607c, 200)

# Moving objects
# Vehicle
geometry = new THREE.SphereBufferGeometry( 0.1, 32, 32 )
material = new THREE.MeshBasicMaterial( {color: 0xffff00} )
vehicle1 = new THREE.Mesh( geometry, material )
scene.add( vehicle1 )
vehicle2 = vehicle1.clone()
scene.add( vehicle2 )
vehicle3 = vehicle1.clone()
scene.add( vehicle3 )
# The ship
centerLength = 2
bodyLength = 3
bodyWidth = 2
deckHeight = 0.1
geometry = new THREE.PlaneGeometry(2,2,2)
geometry.vertices[4].y = -3
geometry.rotateX(-Math.PI * 0.5)
geometry.translate(0, deckHeight, 0)
material = new THREE.MeshBasicMaterial({
    color: 0x005b1e
  })
ship = new THREE.Mesh(geometry, material)
ship.translateZ(15)
ship.translateX(15)
scene.add(ship)
# vehicle1, vehicle2, vehicle3, ship

t1 = { value: 0 } # for RoadNo1
t2 = { value: 0 } # for RoadNo2
t3 = { value: 0 } # for SeaRoute

# Tween updates
updateT = -> 
    vehicle1.position.copy(curveRoadNo1.getPointAt(t1.value))
    vehicle2.position.copy(curveRoadNo1.getPointAt(1 - t1.value)) 
updateT2 = ->
    vehicle3.position.copy(curveRoadNo2.getPointAt(t2.value))
updateT3 = ->
    lookAt = (t3.value + 0.0001) % 1
    ship.position.copy(curveSeaRoute.getPointAt(t3.value))
    ship.lookAt(curveSeaRoute.getPointAt(lookAt))

# Tweens - for Inbetweens
tween11 = new TWEEN.Tween(t1).to({ value: .3 }, 7000).delay(500).onUpdate(updateT)
tween12 = new TWEEN.Tween(t1).to({ value: 1 }, 3000).delay(1000).onUpdate(updateT).onComplete( () ->
        t1.value = 0
    )
tween11.chain(tween12)
tween12.chain(tween11)
tween11.start()

tween21 = new TWEEN.Tween(t2).to({ value: 1 }, 10000).onUpdate(updateT2)
tween22 = new TWEEN.Tween(t2).to({ value: 0 }, 10000).onUpdate(updateT2)
tween21.chain(tween22)
tween22.chain(tween21)
tween21.start()

tween3 = new TWEEN.Tween(t3).to({ value: 1 }, 17000).delay(1500).onUpdate(updateT3).onComplete( () ->
        t3.value = 0
    )
tween3.chain(tween3)
tween3.start()

controls = new THREE.OrbitControls(camera, renderer.domElement)
camera.position.x = colms/2
camera.position.y = 13
camera.position.z = 25
camera.rotation.x = -40 * Math.PI / 180
controls.target = new THREE.Box3().setFromObject(scene).getCenter()
controls.update()

t = 0
animate = ->
    requestAnimationFrame animate
    TWEEN.update()
    renderer.render scene, camera
animate()

1 个答案:

答案 0 :(得分:1)

您可以使用.lookAt()的{​​{1}}方法。看一下ship函数。

我重新设计了你的jsfiddle(一点点):简化了船的创造;使用Tween.js进行动画制作;添加了updateT3以便更好地查看。

请参阅代码段。

THREE.OrbitControls()
// Generated by CoffeeScript 2.0.1
(function() {
  var animate, bodyLength, bodyWidth, build_city, camera, centerLength, city_map, colms, curveObject, curveRoadNo1, curveRoadNo2, deckHeight, flora_density_map, geometry, height, heightBuilding, height_map, i, j, k, len, len1, material, plant_tree, raise_land, ref, renderer, roadHeight, rows, scene, ship, t, v1, v2, v3, v4, v5, vehicle;

  scene = new THREE.Scene;

  camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000);

  renderer = new THREE.WebGLRenderer({
    antialias: true
  });

  renderer.setSize(window.innerWidth, window.innerHeight);

  renderer.setPixelRatio((ref = window.devicePixelRatio) != null ? ref : window.devicePixelRatio || 1);

  document.body.appendChild(renderer.domElement);

  var controls = new THREE.OrbitControls(camera, renderer.domElement);

  //##########################################################################################
  colms = 30;

  rows = 14;

  height_map = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 3, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 4, 5, 5, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 1, 2, 1, 3, 4, 7, 5, 5, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 2, 2, 3, 3, 4, 5, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 5, 4, 5, 6, 6];

  flora_density_map = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0];

  city_map = [9, 9, 9, 9, 9, 0, 0, 9, 9, 0, 0, 9];

  plant_tree = function(x, y, z) {
    var cone, geometry, material;
    geometry = new THREE.ConeGeometry(0.2, 0.5);
    material = new THREE.MeshBasicMaterial({
      color: 0x20281e
    });
    cone = new THREE.Mesh(geometry, material);
    cone.translateX(x);
    cone.translateY(y + .25);
    cone.translateZ(z);
    return scene.add(cone);
  };

  build_city = function(start_c, start_r, colms, rows, i, heightBuilding) {
    var geometry, height, land, material, row, width;
    if (heightBuilding !== 0) {
      height = heightBuilding * 0.035;
      width = 0.5;
      geometry = new THREE.BoxGeometry(width, height, width);
      material = new THREE.MeshBasicMaterial({
        color: 0x8b0000
      });
      land = new THREE.Mesh(geometry, material);
      // Set box base equal to overall baselevel.
      land.translateY(height / 2);
      // Normal animation translation.
      row = (Math.floor(i / colms)) + 1;
      land.translateZ(row + start_r - 1);
      land.translateX(i - (colms * (row - 1)) + start_c);
      return scene.add(land);
    }
  };

  raise_land = function(colms, rows, i, height) {
    var color, geometry, land, material, row;
    color = new THREE.Color(`rgb(${0 + height * 12}, 153, ${51 + height * 30})`);
    height = height * 0.22;
    geometry = new THREE.BoxGeometry(1, height, 1);
    material = new THREE.MeshBasicMaterial({
      color: color
    });
    land = new THREE.Mesh(geometry, material);
    // Set box base equal to overall baselevel.
    land.translateY(height / 2);
    // Normal animation translation.
    row = (Math.floor(i / colms)) + 1;
    land.translateZ(row);
    land.translateX(i - (colms * (row - 1)));
    scene.add(land);
    if (flora_density_map[i] !== 0) {
      return plant_tree(i - (colms * (row - 1)), height, row);
    }
  };

  centerLength = 2;

  bodyLength = 3;

  bodyWidth = 2;

  deckHeight = 0.1;

  geometry = new THREE.PlaneGeometry(2, 2, 2);
  geometry.vertices[4].y = -3;
  geometry.rotateX(-Math.PI * .5);
  geometry.translate(0, .1, 0);
  material = new THREE.MeshBasicMaterial({
    color: 0x005b1e
  });

  ship = new THREE.Mesh(geometry, material);

  ship.translateZ(15);

  ship.translateX(15);

  scene.add(ship);

  // Roads around the main settlement.
  roadHeight = 0.1;

  curveRoadNo1 = new THREE.CatmullRomCurve3([
    new THREE.Vector3(13,
      roadHeight,
      13),
    new THREE.Vector3(12,
      roadHeight,
      14),
    new THREE.Vector3(10,
      roadHeight,
      14),
    new THREE.Vector3(10,
      roadHeight,
      13),
    new THREE.Vector3(11,
      roadHeight,
      12),
    new THREE.Vector3(11,
      roadHeight,
      11),
    // new THREE.Vector3( 9, roadHeight, 10),
    // new THREE.Vector3( 7, roadHeight, 9),
    new THREE.Vector3(4,
      roadHeight,
      10),
    new THREE.Vector3(4,
      roadHeight,
      7),
    new THREE.Vector3(1,
      roadHeight,
      3),
    new THREE.Vector3(5,
      roadHeight,
      2),
    new THREE.Vector3(10,
      roadHeight,
      4),
    new THREE.Vector3(15,
      roadHeight,
      3),
    new THREE.Vector3(18,
      roadHeight,
      6),
    new THREE.Vector3(22,
      roadHeight,
      2),
    new THREE.Vector3(25,
      roadHeight,
      5),
    new THREE.Vector3(25,
      roadHeight,
      7),
    new THREE.Vector3(21,
      roadHeight,
      11),
    new THREE.Vector3(19,
      roadHeight,
      9),
    new THREE.Vector3(17,
      roadHeight,
      11),
    new THREE.Vector3(19,
      roadHeight,
      12),
    new THREE.Vector3(18,
      roadHeight,
      13)
  ]);

  curveRoadNo2 = new THREE.CatmullRomCurve3([new THREE.Vector3(15, roadHeight, 11), new THREE.Vector3(12, roadHeight, 12), new THREE.Vector3(11, roadHeight, 11), new THREE.Vector3(11, roadHeight, 9), new THREE.Vector3(12, roadHeight, 8), new THREE.Vector3(12, roadHeight, 5), new THREE.Vector3(15, roadHeight, 3), new THREE.Vector3(17, roadHeight, 7), new THREE.Vector3(16, roadHeight, 8), new THREE.Vector3(16, roadHeight, 9), new THREE.Vector3(17, roadHeight, 11)]);

  curveRoadNo2.closed = true;

  geometry = new THREE.Geometry();

  geometry.vertices = curveRoadNo1.getPoints(300);

  material = new THREE.LineBasicMaterial({
    color: 0xa9c41e
  });

  curveObject = new THREE.Line(geometry, material);

  scene.add(curveObject);

  geometry = new THREE.Geometry();

  geometry.vertices = curveRoadNo2.getPoints(200);

  material = new THREE.LineBasicMaterial({
    color: 0xa9c41e
  });

  curveObject = new THREE.Line(geometry, material);

  scene.add(curveObject);

  geometry = new THREE.SphereBufferGeometry(0.2, 32, 32);

  material = new THREE.MeshBasicMaterial({
    color: 0xffff00
  });

  vehicle = new THREE.Mesh(geometry, material);

  scene.add(vehicle);

  for (i = j = 0, len = height_map.length; j < len; i = ++j) {
    height = height_map[i];
    // vehicle.position.x = 15
    // vehicle.position.z = 3

    // Fundaments of scene.
    raise_land(30, 14, i, height);
  }

  for (i = k = 0, len1 = city_map.length; k < len1; i = ++k) {
    heightBuilding = city_map[i];
    build_city(14, 12, 4, 3, i, heightBuilding);
  }

  camera.position.x = colms / 2;

  camera.position.y = 15;

  camera.position.z = 30;

  controls.target = new THREE.Box3().setFromObject(scene).getCenter();
  controls.update();

  t = {
    value: 0
  };

  vehicle2 = new THREE.Mesh(vehicle.geometry, vehicle.material);
  scene.add(vehicle2);

  vehicle3 = new THREE.Mesh(vehicle.geometry, vehicle.material);
  scene.add(vehicle3);

  var tween11 = new TWEEN.Tween(t).to({
    value: .3
  }, 7000).delay(500).onUpdate(updateT);
  var tween12 = new TWEEN.Tween(t).to({
    value: 1
  }, 3000).delay(1000).onUpdate(updateT).onComplete(function() {
    t.value = 0
  });
  tween11.chain(tween12);
  tween12.chain(tween11);
  tween11.start();

  function updateT() {
    vehicle.position.copy(curveRoadNo2.getPointAt(t.value));
    vehicle2.position.copy(curveRoadNo2.getPointAt(1 - t.value));
  }

  let t2 = {
    value: 0
  };
  var tween21 = new TWEEN.Tween(t2).to({
    value: 1
  }, 10000).onUpdate(updateT2);
  var tween22 = new TWEEN.Tween(t2).to({
    value: 0
  }, 10000).onUpdate(updateT2);
  tween21.chain(tween22);
  tween22.chain(tween21);
  tween21.start();

  function updateT2() {
    vehicle3.position.copy(curveRoadNo1.getPointAt(t2.value));
  }

  var shipCurve = new THREE.CatmullRomCurve3([
    new THREE.Vector3(15, 0, 15),
    new THREE.Vector3(0, 0, 25),
    new THREE.Vector3(30, 0, 25)
  ]);
  shipCurve.closed = true;

  var shipLineGeometry = new THREE.Geometry();
  shipLineGeometry.vertices = shipCurve.getPoints(200);
  var shipLine = new THREE.Line(shipLineGeometry, new THREE.LineBasicMaterial({
    color: "aqua"
  }));
  scene.add(shipLine);

  let t3 = {
    value: 0
  };
  var tween3 = new TWEEN.Tween(t3).to({
    value: 1
  }, 17000).delay(1500).onUpdate(updateT3).onComplete(function() {
    t3.value = 0;
  });
  tween3.chain(tween3);
  tween3.start();

  function updateT3() {
    let lookAt = (t3.value + 0.0001) % 1;
    ship.position.copy(shipCurve.getPointAt(t3.value));
    ship.lookAt(shipCurve.getPointAt(lookAt));
  }

  animate = function() {
    requestAnimationFrame(animate);
    TWEEN.update();
    renderer.render(scene, camera);
  };

  animate();

}).call(this);
body {
  overflow: hidden;
  margin: 0;
}

作为参考,请查看此example的源代码。