lstcnvrstn
lstcnvrstn

Reputation: 61

.svg asset in three.js 3d space

I'm trying to load a .svg asset into my three.js scene, as a flat vector layer; I found this example with SVGLoader and SVGRenderer from another post, but I can't make it work.

The svg loaded is stuck in 2d space and not responding to camera movement, I can't access its position. I tried to switch to WebGLRenderer, but the svg doesn’t get loaded.

The option of loading it as sprite would be good, but I would want the sprite to not face the camera and stay still in 3d space.

var svgManager = new THREE.SVGLoader();
var url = 'https://upload.wikimedia.org/wikipedia/commons/f/f7/Europe_laea_location_map.svg';

function svg_loading_done_callback(doc) {
  init();
  svg(new THREE.SVGObject(doc));
  ico();
  animate();
};

svgManager.load(url,
  svg_loading_done_callback,
  function() {
    console.log("Loading SVG...");
  },
  function() {
    console.log("Error loading SVG!");
  });


var AMOUNT = 100;
var container, camera, scene, renderer;

function init() {
  scene = new THREE.Scene();
  renderer = new THREE.SVGRenderer();
  renderer.setClearColor(0x00ff00);
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(window.innerWidth, window.innerHeight);
  var container = document.getElementById('container');
  container.appendChild(renderer.domElement);

  camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000);
  camera.position.z = 1100;
  controls = new THREE.OrbitControls(camera, renderer.domElement);
  controls.enableZoom = true;
  window.addEventListener('resize', onWindowResize, false);
}

function svg(svgObject) {
  svgObject.position.x = 510;
  svgObject.position.y = -110;
  svgObject.position.z = 0;
  scene.add(svgObject);
}

function ico() {
  geometry = new THREE.IcosahedronGeometry(100, 1)
  material = new THREE.MeshNormalMaterial({});
  ico = new THREE.Mesh(geometry, material);
  ico.position.y = -300;
  scene.add(ico);
  ico2 = new THREE.Mesh(geometry, material);
  ico2.position.y = 500;
  ico2.position.x = -500;
  ico2.position.z = -50;
  scene.add(ico2);
}

function onWindowResize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
}

function animate() {
  requestAnimationFrame(animate);
  controls.update;
  render();
}

function render() {
  renderer.render(scene, camera);
}
body {
  margin: 0;
}

canvas {
  width: 100%;
  height: 100%
}
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">

<div id="container"></div>

<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/renderers/SVGRenderer.js"></script>
<script src="https://threejs.org/examples/js/renderers/Projector.js"></script>
<script src="https://threejs.org/examples/js/loaders/SVGLoader.js"></script>
<script src="https://threejs.org/examples/js/libs/stats.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

Upvotes: 2

Views: 1785

Answers (1)

elifer
elifer

Reputation: 31

The SVGLoader and SVGRenderer are two different things. The first loads an SVG file and converts it to three.js shapes (albeit with some limitations, i.e. can read very simple SVGs, does not render strokes but only filled objects, etc), while the latter renders three.js primitives using SVG elements instead of WebGL. In a sense, they are opposites of each other.

So, first of all, you'd need to use the WebGLRenderer for your case.

Then, you need to change the SVG loading callback. It receives an array of paths with which you can render the SVG.

See the changes in functions svg_loading_done_callback, init and svg, and run it in JSFiddle:

var svgManager = new THREE.SVGLoader();
var url = 'https://upload.wikimedia.org/wikipedia/commons/f/f7/Europe_laea_location_map.svg';

function svg_loading_done_callback(paths) {
  init();
  svg(paths);
  ico();
  animate();
};

svgManager.load(url,
  svg_loading_done_callback,
  function() {
    console.log("Loading SVG...");
  },
  function() {
    console.log("Error loading SVG!");
  });


var AMOUNT = 100;
var container, camera, scene, renderer;

function init() {
  scene = new THREE.Scene();
  renderer = new THREE.WebGLRenderer();
  renderer.setClearColor(0x00ff00);
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(window.innerWidth, window.innerHeight);
  var container = document.getElementById('container');
  container.appendChild(renderer.domElement);

  camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000);
  camera.position.z = 1100;
  controls = new THREE.OrbitControls(camera, renderer.domElement);
  controls.enableZoom = true;
  window.addEventListener('resize', onWindowResize, false);
}

function svg(paths) {

  var group = new THREE.Group();
  group.position.x = 510;
  group.position.y = -110;
  group.position.z = 0;

  for ( var i = 0; i < paths.length; i ++ ) {

    var path = paths[ i ];

    var material = new THREE.MeshBasicMaterial( {
      color: path.color,
      side: THREE.DoubleSide,
      depthWrite: false
    } );

    var shapes = path.toShapes( true );

    for ( var j = 0; j < shapes.length; j ++ ) {

      var shape = shapes[ j ];

      var geometry = new THREE.ShapeBufferGeometry( shape );
      var mesh = new THREE.Mesh( geometry, material );

      group.add( mesh );

    }

  }

  scene.add( group );
}

function ico() {
  geometry = new THREE.IcosahedronGeometry(100, 1)
  material = new THREE.MeshNormalMaterial({});
  ico = new THREE.Mesh(geometry, material);
  ico.position.y = -300;
  scene.add(ico);
  ico2 = new THREE.Mesh(geometry, material);
  ico2.position.y = 500;
  ico2.position.x = -500;
  ico2.position.z = -50;
  scene.add(ico2);
}

function onWindowResize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
}

function animate() {
  requestAnimationFrame(animate);
  controls.update;
  render();
}

function render() {
  renderer.render(scene, camera);
}

PS: Check the SVG Loader to see what it's able to parse

Upvotes: 3

Related Questions