holographix
holographix

Reputation: 2557

draw text from canvas into three js as a sprite

long story short, I'm making a live line chart and I'm using three.js due to the high number of lines per second that needs to be pushed inside this thing, so, now I'd need to draw the text values for the x and y axis, after a pretty long struggle ( because I'm a total noob @ threejs ) I figured out the wonderful technique of using the canvas as a sprite ( which is darn rad ). This is the code that I use

/// 2D REALM

var canvas = document.createElement('canvas'),
        context = canvas.getContext('2d'),
        metrics = null,
        textHeight = 100,
        textWidth = 0,
        actualFontSize = 2;

        context.fillStyle = '#FF0000';
        var size = 250;
          canvas.width = size;
          canvas.height = size;

function addText(position, text) {
  context.textAlign = 'center';
  context.font = '24px Arial';
  context.fillText("Game Over", size/2, size/2);

  var texture = new THREE.Texture(canvas);
  texture.needsUpdate = true;
  var material = new THREE.SpriteMaterial( { map: texture, color: 0x333333, fog: false } );
  var sprite = new THREE.Sprite( material );
  sprite.position.set(0,100,0);

  return sprite;
}

. . .

 $(document).ready(function() {
   h = $(window).height() - $('.speed-graph').outerHeight() - $('.speed-toolbar').outerHeight() - $('.channel-toolbar').outerHeight() - $('.status_bar').outerHeight() + 1;
      w = $(window).width() - $('.palette').outerWidth();


      camera = new THREE.PerspectiveCamera( 75, parseFloat(w) / parseFloat(h), 0.1, 5000 );


    //renderer.setSize( window.innerWidth, window.innerHeight );
      console.log("w: " + w + " | h: " + h);
      renderer.setSize(w, h);

      $('body').append( renderer.domElement );

      var material = new THREE.LineBasicMaterial({ color: 0x000000, linewidth: 1 });
      var geometry = new THREE.Geometry();

      geometry.vertices.push( new THREE.Vector3(-10 , 0, 0 ) );
      geometry.vertices.push( new THREE.Vector3(8 , 0, 0 ) );

      lines['line'].push(  new THREE.Line( geometry, material ))
      text = addText(new Array(0, 9), "lorem ipsum");
      //lines['label'].push( addText(new Array(0, 9), "0") );
      scene.add( lines['line'][0] );
      scene.add( text );


      camera.position.z = 5;
      render(); 
});

now, the lines get drawn as they should, but I had no luck at all with the text. I can't understand the reason why I can't see the text, because I don't get any compiler error.

Upvotes: 3

Views: 7107

Answers (2)

holographix
holographix

Reputation: 2557

in the end, I figured it out by myself, problem was that the canvas didn't had a proper size, plus a bunch of other stuff.

for the sake of the solution, and hoping to help others in the future, here's the working code along with a codepen to see it live in action.

/// 2D REALM

//var canvas = document.createElement('canvas'),
var canvas,
  context,
  metrics = null,
  textHeight = 10,
  textWidth = 0,
  actualFontSize = 0.8;

canvas = document.createElement('canvas'),
  context = canvas.getContext('2d');
context.fillStyle = '#FF0000';


function addText(position, text) {
  // 2d duty
  metrics = context.measureText(text);
  var textWidth = metrics.width;

  canvas.width = textWidth;
  canvas.height = textHeight;
  context.font = "normal " + textHeight + "px Arial";
  context.textAlign = "center";
  context.textBaseline = "middle";
  context.fillStyle = "#ff0000";
  context.fillText(text, textWidth / 2, textHeight / 2);

  var texture = new THREE.Texture(canvas);
  texture.needsUpdate = true;
  var material = new THREE.SpriteMaterial({
    map: texture,
    useScreenCoordinates: false
  });
  var sprite = new THREE.Sprite(material);

  var textObject = new THREE.Object3D();
  // var sprite = new THREE.Sprite(texture);
  textObject.textHeight = actualFontSize;
  textObject.textWidth = (textWidth / textHeight) * textObject.textHeight;
  sprite.scale.set(textWidth / textHeight * actualFontSize, actualFontSize, 1);

  //  sprite.position.set(10,10,0);

  textObject.add(sprite);
  return textObject;
}

var scene = new THREE.Scene();
var camera;
var renderer = new THREE.WebGLRenderer({
  alpha: 1,
  antialias: true,
  clearColor: 0xffffff
});


$(document).ready(function() {
  h = $(window).height();
  w = $(window).width();


  camera = new THREE.PerspectiveCamera(75, parseFloat(w) / parseFloat(h), 0.1, 5000);

  // console.log("w: " + w + " | h: " + h);
  renderer.setSize(w, h);

  $('body').append(renderer.domElement);

  text = addText(new Array(0, 9), "it fucking works!!");
  scene.add(text);
  // console.log(text.position);
  //      text.position.x = 3.0;

  camera.position.z = 5;
  render();
});

// render the whole things up
function render() {

  requestAnimationFrame(render);
  renderer.render(scene, camera);

}
body {
  margin: 0;
  padding: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/three.min.js"></script>

Upvotes: 8

Zen
Zen

Reputation: 1

const o=Object.assign({},defaultFont,conf)
    const canvas = document.createElement('canvas')
    canvasCache.add(canvas)
    canvas.height=o.size!
    canvas.width=text.length*o.size!
    const ctx = canvas.getContext('2d')!
    ctx.font = `Bold ${o.size!}px ${o.face!}`
    const metrics = ctx.measureText(text);
    const width = metrics.width;
    //  ctx.fillStyle = o.bg instanceof THREE.Color ? o.bg.getStyle() : RGBAToCss(o)
    //  canvas.width=width
    //  canvas.height=o.size!
    // ctx.lineWidth = 4
    ctx.textAlign = 'center'
    ctx.textBaseline = "middle"
    ctx.fillStyle =buildColor(o.color!)
    ctx.fillText(text, width/2, o.size!/2)

this same fine to me, I'd know why must set canvas width and height before do any

Upvotes: 0

Related Questions