i have a 3d model created with blender and i want to place 2d text over the various meshes of the imported model. i am successfull in changing colors of the meshes on the basis of their indices by diffuse.color but unable to drawtext over my mesh.
code used to change color:
scene.meshes[indexValue].material.diffuseColor = color;
allMeshChange =indexValue == scene.meshes.length - 1;
and for text i am doing this:
var impact = var impact =scene.meshes[1];
impact.material = new BABYLON.StandardMaterial("impactMat", scene);
impact.material.emissiveColor = new BABYLON.Color3(1, 1, 0.5);
impact.position = new BABYLON.Vector3(scene.meshes[1].position);
var backgroundTexture = new BABYLON.DynamicTexture("dynamic texture", 512, scene, true);
impact.material.diffuseTexture = backgroundTexture;
impact.material.specularColor = new BABYLON.Color3(1, 1, 1);
impact.material.backFaceCulling = false;
backgroundTexture.drawText("test", null, 80, "italic 80px Segoe UI", "white", "#555555");
i am new to babylon js . Kindly help in writing text over mesh.
What is the options argument meaning with the current value 512 on the BABYLON.DynamicTexture constructor ? Cause to determine the 2d context for printing the text it's looking for a getContext(:string) method. Instead, you should pass it your canvas known as a HTMLCanvasElement.
Related
I have an existing line initiated
// material
const material = new THREE.LineBasicMaterial({ color: 0xffffff });
// array of vertices
vertices.push(new THREE.Vector3(0, 0, 0));
vertices.push(new THREE.Vector3(0, 0, 5));
//
const geometry = new THREE.BufferGeometry().setFromPoints(vertices);
const line = new THREE.Line(geometry, material);
And what I want to do is extend this line following its initiation. I've read this page on how to update things and I don't think it fits this situation because instead of adding vertices to my shape, I want to move them. Then again, it's very likely I misunderstood. I've tried deleting the line and then redrawing it longer, but I can't get it to work without my browser crashing.
The BufferGeometry exposes its vertices through its positions BufferAttribute. To change the positions, you should do something like the following:
//
// Assuming we want to move your line segment (0, 0, 0)-(0, 0, 5) by
// one unit in the direction of positive x, to (1, 0, 0)-(1, 0, 5).
//
// Get a reference to the "position" buffer attribute
const pos = geometry.getAttribute("position");
// Set the new positions
pos.setXYZ(0, vertices[0].x + 1, vertices[0].y, vertices[0].z);
pos.setXYZ(1, vertices[1].x + 1, vertices[1].y, vertices[1].z);
// Update the vertex buffer in graphics memory
pos.needsUpdate = true;
// Update the bounds to support, e.g., frustum culling
geometry.computeBoundingBox();
geometry.computeBoundingSphere();
Other methods exist, such as modifying the attribute's backing array directly and copying in a new array, but the general process will be the same.
I've looked for resources online, but I have not seen a way to extrude a colored image in Three.js. I'm trying to create something like a Minecraft item where the image is used to then create an extruded geometry. An example would be: https://minecraft.gamepedia.com/File:BowSpinning3.gif
I've tried looking at this resource: https://muffinman.io/three-js-extrude-svg-path/ but this only extrudes uncolored SVGs.
loader.load('./textures/diamondbleu.svg', function (data) {
// Group we'll use for all SVG paths
const svgGroup = new THREE.Group();
// When importing SVGs paths are inverted on Y axis
// it happens in the process of mapping from 2d to 3d coordinate system
svgGroup.scale.y *= -1;
const material = new THREE.MeshLambertMaterial();
// Loop through all of the parsed paths
data.paths.forEach((path, i) => {
const shapes = path.toShapes(true);
// Each path has array of shapes
shapes.forEach((shape, j) => {
// Finally we can take each shape and extrude it
const geometry = new THREE.ExtrudeGeometry(shape, {
depth: 20,
bevelEnabled: false
});
// Create a mesh and add it to the group
const mesh = new THREE.Mesh(geometry, material);
svgGroup.add(mesh);
});
});
// Get group's size
const box = new THREE.Box3().setFromObject(svgGroup);
const size = new THREE.Vector3();
box.getSize(size);
const yOffset = size.y / -2;
const xOffset = size.x / -2;
// Offset all of group's elements, to center them
svgGroup.children.forEach(item => {
item.position.x = xOffset;
item.position.y = yOffset;
});
svgGroup.position.set(0, blockSize*75, 0);
// Finally we add svg group to the scene
scene.add(svgGroup);
})
Is there a way to modify the code to allow for colored SVGs? Thanks!
You can use the SVGLoader that's available in the "examples/jsm/loaders/" folder.
The docs have outlined how to generate SVGs in 3D space, it looks like your code sample is missing the part where the paths loop makes a new material and assigns a color for each path:
var material = new THREE.MeshBasicMaterial( {
color: path.color,
side: THREE.DoubleSide,
depthWrite: false
} );
Your code seems to create a single LambertMaterial with no colors assigned, and no lights. Lambert materials need lights to be illuminated, whereas BasicMaterial just shows the color without need of lights.
Look at the code in this demo for another example. Instead of using path.color, this demo finds the color by accessing path.userData.style.fill. I think you'll want the latter approach, depending on your SVG file.
I am creating a simple "Hello World' Three.js application and I am curious to know why this works.
Firstly, I create and show a centered "Hello World" from the code snippet below. This code snippet is responsible for centering the text and moving it back 20 units.
/* Create the scene Text */
let loader = new THREE.FontLoader();
loader.load( 'fonts/helvetiker_regular.typeface.json', function (font) {
/* Create the geometry */
let geometry_text = new THREE.TextGeometry( "Hello World", {
font: font,
size: 5,
height: 1,
});
/* Create a bounding box in order to calculate the center position of the created text */
geometry_text.computeBoundingBox();
let x_mid = geometry_text.boundingBox.max.x - geometry_text.boundingBox.min.x;
geometry_text.translate(-0.5 * x_mid, 0, 0); // Center the text by offsetting half the width
/* Currently using basic material because I do not have a light, Phong will be black */
let material_text = new THREE.MeshBasicMaterial({
color: new THREE.Color( 0x006699 )
});
let textMesh = new THREE.Mesh(geometry_text, material_text);
textMesh.position.set(0, 0, -20);
//debugger;
scene.add(textMesh);
console.log('added mesh')
} );
Now notice here that I perform the translation first
geometry_text.computeBoundingBox();
let x_mid = geometry_text.boundingBox.max.x - geometry_text.boundingBox.min.x;
geometry_text.translate(-0.5 * x_mid, 0, 0);
and then the position is performed to move the mesh
let textMesh = new THREE.Mesh(geometry_text, material_text);
textMesh.position.set(0, 0, -20);
Now my confusion comes from that fact that if I remove my translation, then my "Hello World" text is not centered. However after my translation is completed, I am setting the position on my mesh to (0, 0, -20), shouldn't this set_position call overwrite my previous translation and move the object to the position (0, 0, -20), why is my text still centered eventhough my set_position is called after my translation?
This is because the call to THREE.TextGeometry.translate() ends up calling THREE.Geometry.applyMatrix() with the corresponding translation matrix, which bakes the transformation by directly modifying the vertex coordinates. See Geometry.js#L149 for the source.
In other words, before the call
textMesh.position.set(0, 0, -20);
the mesh transformation matrix was still the identity matrix. Mesh transformation differs from geometry transformation in that it only updates the matrix that is passed into the shader, instead of recomputing every vertex. For which one you would want to use: transforming the geometry is more expensive, but you can do it once and avoid it in the render loop (See the explanation here).
I want to make a dynamic render of user inputted text using three.js and dat.gui, so far i've made this to render out the text:
var displayGui = function(){
var gui = new dat.GUI();
var parameters = {
message:"sample",
spinVelocity: 0
}
//Adds Text controls
var myText = gui.add(parameters, 'message').name('Text');
myText.onChange(function () {
//adds text
var loader = new THREE.FontLoader();
loader.load('fonts/OpenSansBold.json', function(font) {
console.log(myText);
var textGeo = new THREE.TextGeometry(myText, {
font: font,
size: 200,
height: 50,
curveSegments: 12,
position: 3,
bevelThickness: 2,
bevelSize: 5,
bevelEnabled: true,
});
var textMaterial = new THREE.MeshPhongMaterial({ color: 0xff0000 });
var mesh = new THREE.Mesh(textGeo, textMaterial);
mesh.position.set(100, 100, 100);
scene.add(mesh);
});
});
gui.add(parameters, 'spinVelocity').name('Spin');
gui.open();
};
However, as you can see on here, it just renders out a big red 3D text that says [object Object] , i have suspected that this may be because var myText is an object and not a string, so i tried to String(myText) however, it did not change much and it still did not work.
Is this not working because the text is not a string or is this because three.js is not recognizing the text inputted by the user on the dat.gui interface?
You should not be trying to load the font each time your dat.gui fires. Your code has a horrible performance problem, and it's likely that you would run out of memory after fiddling with the gui for a while.
My understanding is that this code creates a new instance of geometry each time you change the value in the gui, and never disposes of them. You're filling up your gpu with copies of this mesh.
Specific to your question, you're using datgui wrong:
console.log(myText); //logs the intance of a gui object (a JS object with methods and such)
change to:
console.log(parameters.message);
To fix the reloading issue, cache your font
var myFont
loader.load('fonts/OpenSansBold.json', function(font) {
myFont = font
//your gui is not ready until the font comes so, for example you could instantiate it here
gui.add(...).onChange(function(){..})
})
Change line
myText.onChange(function () {
to
myText.onChange(function (value) {
Then value will contain the new value of the input.
I'm experimenting with Bjørn Sandvik's really great process for importing terrain data into a scene.
Check it out:
http://blog.thematicmapping.org/2013/10/terrain-building-with-threejs.html
var terrainLoader = new THREE.TerrainLoader();
terrainLoader.load('../assets/jotunheimen.bin', function(data) {
var geometry = new THREE.PlaneGeometry(60, 60, 199, 199);
for (var i = 0, l = geometry.vertices.length; i < l; i++) {
geometry.vertices[i].z = data[i] / 65535 * 10;
}
var material = new THREE.MeshPhongMaterial({
color: 0xdddddd,
wireframe: true
});
var plane = new THREE.Mesh(geometry, material);
scene.add(plane);
});
My intent is to use this to display elevation data from a time series, so multiple .bin files will be loaded to provide data representing a period of several years to show change over time.
I am having difficulties updating the geometry with new data. I think that my difficulties stem from the plane and geometry variables being defined inside of a function, meaning that they are undefined in the global context. So later when I call those variables they don't have any value associated with them.
Does anyone have an idea of how I can update this geometry with new data loaded using the TerrainLoader?
anything you .add() to the scene object is visible as an element of the scene.children array -- to you can still reference your plane and the geometry of it as plane.geometry -- the the plane is the only object in the scene, it will probably be scene.children[0].geometry
See this page: https://github.com/mrdoob/three.js/wiki/Updates for hints on how to let THREE know the geometry is changing