Three.js 3D Text Bending - javascript

Is this still available in r69? In the process of 'rolling' my own, but before moving forward, want to make sure I have not overlooked any vital documentation or useful libraries...

Try to make through this example. Look at messages in the console.
<script src="js/modifiers/BendModifier.js"></script>
var text = "THREE.BendModifier";
var textGeometry = new THREE.TextGeometry(text, {
size: 128,
height: 50,
curveSegments: 4,
font: "gentilis",
weight: "bold",
style: "normal",
bevelEnabled: true,
bevelThickness: 2,
bevelSize: 1,
});
var textMaterial = new THREE.MeshPhongMaterial({
color: 0x62254a
});
var text3D = new THREE.Mesh(textGeometry, textMaterial);
var direction = new THREE.Vector3(0, 0, -1);
var axis = new THREE.Vector3(0, 1, 0);
var angle = Math.PI / 6;
var modifier = new THREE.BendModifier();
modifier.set(direction, axis, angle).modify( text3D.geometry );
textGeometry.computeBoundingBox();
var textWidth = textGeometry.boundingBox.max.x - textGeometry.boundingBox.min.x;
text3D.position.set(-0.5 * textWidth, 500, 0);
scene.add(text3D);

Just wanted to mention that you need to change BendModifier.js according to here https://stackoverflow.com/a/40492948/7132939
to make it work with newer versions of three.js
BendModifier.js uses THREE.Matrix3.getInverse() which is not working any more.
So you need to change the BendModifier.js to use THREE.Matrix4.getInverse(..) and one other change as mentioned in the answer linked.
And generating text has changed - using a THREE.fontLoader()
Complete working example can be found following this link
Screenshot of web site - link
And full answer of the internal link:
There was a change in THREE.js but it is quite easy to adjust the BendModifier.js script. Just change the lines
var InverseP = new THREE.Matrix3().getInverse( P );
and
newVertices[i] = new THREE.Vector3(); newVertices[i].copy( geometry.vertices[i] ).applyMatrix3( InverseP );
to Matrix4 in both cases - so they will read:
var InverseP = new THREE.Matrix4().getInverse( P );
and
newVertices[i] = new THREE.Vector3(); newVertices[i].copy( geometry.vertices[i] ).applyMatrix4( InverseP );
Just tried it with three.min.81.js and it works fine.

Related

Showing highlighted silhouette of mesh ONLY when it is occluded

so I'm trying to create an online game using Babylon.js but have run into a problem thats got me a little stumped so hoping someone here would be willing to help me out. Please bear with me on this one, i'm a complete newbie with babylon as i've only every worked with THREE.js. Right now my game consists of a scene compromising of multiple meshes with multiple users represented as avatars (created from basic circle geometry for the moment) loaded into an environment. What I want to do is highlight the outline of these avatars ONLY when they are occluded by any other object, meaning that when they are not occluded they look normal with no highlight but when behind an object their highlighted silhouette can be seen by others (including yourself as you can see your own avatar). This is very akin to effects used in many other video games (see example below).
Example of Effect
Thus far, based on some googling and forum browsing (Babylonjs outline through walls & https://forum.babylonjs.com/t/highlight-through-objects/8002/4) I've figured out how to highlight the outline of objects using Babylon.HighlighLayer and I know that i can render objects above others via RenderingGroups but I can't seem to figure out how to use them in conjunction to create the effect I want. The best i've managed to do is get the highlighted avatar render above everything but I need just the silhouette not the entire mesh. I'm also constrained by the fact that my scene has many meshes in it that are loaded dynamically and i'm also trying to keep things as optimal as possible. Can't afford to use very computationally expensive procedures.
Anybody know of the best way to approach this? Would greatly appreciate any advice or assistance you can provide.Thanks!
So I asked the same question on the babylon forums which helped me to find a solution. All credit goes to the guy's that helped me out over there but just in case someone else comes across this question seeking an answer, here is a link to that forum question https://forum.babylonjs.com/t/showing-highlighted-silhouette-of-mesh-only-when-it-is-occluded/27783/7
Edit:
Ok thought i'd include the two possible solutions here properly as well as their babylon playgrounds. All credit goes to roland & evgeni_popov who came up with these solutions on the forum linked above.
The first solution is easier to implement but slightly less performant than the second solution.
Clone Solution: https://playground.babylonjs.com/#JXYGLT%235
// roland#babylonjs.xyz, 2022
const createScene = function () {
const scene = new BABYLON.Scene(engine);
const camera = new BABYLON.ArcRotateCamera('camera', -Math.PI / 2, Math.PI / 2, 20, new BABYLON.Vector3(0, 0, 0), scene)
camera.attachControl(canvas, true);
const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
light.intensity = 0.7;
const wall = BABYLON.MeshBuilder.CreateBox('wall', { width: 5, height: 5, depth: 0.5 }, scene)
wall.position.y = 1
wall.position.z = -2
const sphere = BABYLON.MeshBuilder.CreateSphere('sphere', { diameter: 2, segments: 32 }, scene)
sphere.position.y = 1
const sphereClone = sphere.clone('sphereClone')
sphereClone.setEnabled(false)
const matc = new BABYLON.StandardMaterial("matc", scene);
matc.depthFunction = BABYLON.Constants.ALWAYS;
matc.disableColorWrite = true;
matc.disableDepthWrite = true;
sphereClone.material = matc;
sphere.occlusionQueryAlgorithmType = BABYLON.AbstractMesh.OCCLUSION_ALGORITHM_TYPE_ACCURATE
sphere.occlusionType = BABYLON.AbstractMesh.OCCLUSION_TYPE_STRICT
const hl = new BABYLON.HighlightLayer('hl1', scene, { camera: camera })
hl.addMesh(sphereClone, BABYLON.Color3.Green())
hl.addExcludedMesh(wall);
let t = 0;
scene.onBeforeRenderObservable.add(() => {
sphere.position.x = 10 * Math.cos(t);
sphere.position.z = 100 + 104 * Math.sin(t);
if (sphere.isOccluded) {
sphereClone.setEnabled(true)
sphereClone.position.copyFrom(sphere.position);
} else {
sphereClone.setEnabled(false)
}
t += 0.03;
})
return scene;
};
This second solution is slightly more performant than above as you don't need a clone but involves overriding the AbstactMesh._checkOcclusionQuery function which is the function that updates the isOccluded property for meshes such that the mesh is always rendered even when occluded. There’s no overhead if you are using the occlusion queries only for the purpose of drawing silhouettes however If you are also using them to avoid drawing occluded meshes then there’s an overhead because the meshes will be drawn even if they are occluded. In which case your probably best of going with the first solution
Non-Clone solution: https://playground.babylonjs.com/#JXYGLT#14
// roland#babylonjs.xyz, 2022
const createScene = function () {
const scene = new BABYLON.Scene(engine);
const camera = new BABYLON.ArcRotateCamera('camera', -Math.PI / 2, Math.PI / 2, 20, new BABYLON.Vector3(0, 0, 0), scene)
camera.attachControl(canvas, true);
const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
light.intensity = 0.7;
const wall = BABYLON.MeshBuilder.CreateBox('wall', { width: 5, height: 5, depth: 0.5 }, scene)
wall.position.y = 1
wall.position.z = -2
const sphere = BABYLON.MeshBuilder.CreateSphere('sphere', { diameter: 2, segments: 32 }, scene)
sphere.position.y = 1
sphere.occlusionQueryAlgorithmType = BABYLON.AbstractMesh.OCCLUSION_ALGORITHM_TYPE_ACCURATE
sphere.occlusionType = BABYLON.AbstractMesh.OCCLUSION_TYPE_STRICT
const mats = new BABYLON.StandardMaterial("mats", scene);
sphere.material = mats;
const hl = new BABYLON.HighlightLayer('hl1', scene, { camera: camera })
hl.addExcludedMesh(wall);
let t = 0;
const cur = BABYLON.AbstractMesh.prototype._checkOcclusionQuery;
scene.onDisposeObservable.add(() => {
BABYLON.AbstractMesh.prototype._checkOcclusionQuery = cur;
});
BABYLON.AbstractMesh.prototype._checkOcclusionQuery = function() {
cur.apply(this);
return false;
}
scene.onBeforeRenderObservable.add(() => {
sphere.position.x = 10 * Math.cos(t);
sphere.position.z = 100 + 104 * Math.sin(t);
if (sphere.isOccluded) {
hl.addMesh(sphere, BABYLON.Color3.Green())
mats.depthFunction = BABYLON.Constants.ALWAYS;
mats.disableColorWrite = true;
} else {
hl.removeMesh(sphere);
mats.depthFunction = BABYLON.Constants.LESS;
mats.disableColorWrite = false;
}
t += 0.03;
})
return scene;
};

Insert three.js independient scripts in reveal.js slides

I want to introduce several three.js 3D models within different reveal.js slides. So far I achieved it partially. In each slide I introduce:
<section>
<script src="three_r84/model1.js"></script>
<div id="model1" width="950" height="600" data-prevent-swipe></div>
</section>
And model1.js file is the following:
document.addEventListener('DOMContentLoaded', function(event) {
window.requestAnimationFrame = (function() {
return window.requestAnimationFrame;
})();
function animateScene() {
requestAnimationFrame(animateScene);
renderScene();
}
function startScene() {
var canvas = document.getElementById('model1');
render = new THREE.WebGLRenderer({
antialias: true,
alpha: true});
render.setClearColor(0x191919, 0);
var canvasWidth = canvas.getAttribute('width');
var canvasHeight = canvas.getAttribute('height');
render.setSize(canvasWidth, canvasHeight);
canvas.appendChild(render.domElement);
scene = new THREE.Scene();
var aspect = canvasWidth / canvasHeight;
camera = new THREE.PerspectiveCamera(45, aspect);
camera.position.set(15, 10, 15);
camera.lookAt(scene.position);
scene.add(camera);
controls = new THREE.OrbitControls(camera);
controls.enableDamping = true;
controls.dampingFactor = 0.25;
controls.enableZoom = true;
// concrete columns
// define columns geometry
var columnGeometry = new THREE.BoxGeometry(0.25, 3, 0.4);
// define columns colors
function hsl(h, s, l) {
return (new THREE.Color()).setHSL(h, s, l);
}
// define columns position & pass data (geometry, color, position) to create foundation function
{
makeInstanceColumn(columnGeometry, hsl(0 / 8, 1, .5), 0, -0, 0);
}
// create columns
function makeInstanceColumn(columnGeometry, color, x, z, y) {
[THREE.BackSide, THREE.FrontSide].forEach((side) => {
var columnMaterial = new THREE.MeshPhongMaterial({
color,
opacity: 0.5,
transparent: true,
side,
});
var column = new THREE.Mesh(columnGeometry, columnMaterial);
scene.add(column);
column.position.set(x, z, y);
});
}
// light
function addLight(x, z, y) {
var color = 0xFFFFFF;
var intensity = 1;
var light = new THREE.DirectionalLight(color, intensity);
light.position.set(x, z, y);
scene.add(light);
}
addLight(-1, 2, 4);
addLight( 1, -1, -2);
}
function renderScene() {
controls.update();
render.render(scene, camera);
}
startScene();
animateScene();
renderScene();
});
It works, although I had to insert another slide between to consecutive 3D models. Otherwise they appear in the same slide (as an overlay) even though the "canvas"es are created in different "section"s. And also it does not work more than two times. However, the main problem is that I am unable to interact with the first model. The OrbitControl, seems to be linked with the second last 3D model, which slide I have not yet visualize but rotates, pans and zooms it.
I have read that there is a "slidechanged" event listener. I think that I should insert it somewhere in the code but I do not know how and where. I tried subtituting document.addEventListener('DOMContentLoaded', function(event) ... from the beginning of the script with "document.addEventListener('slidechanged', function(event) ... but it did not work. Silly me, I suppose I should use Reveal.on ... but I do not figure how.
BTW I am using a quite old version of three.js (r84). I do not need the full potential of three.js neither I can use it, because I need to show it in an old iPad (iOS 9.3.5) I know there is an example of combining three.js and reveal.js. It uses and even older version (r59) but I can not simply swap my scripts for its scripts, because it does not recognize some of the commands, e.g. BoxGeometry and other stuff.
I would like to do something similar, but I seek for guidance and advice. Does someone face something similar? Any ideas?

ThreeJS ExtrudeGeometry HOLE from points

Gutentag, guys!
I am trying to make a hole in my ExtrudeGeometry but it does not work.
My code looks like this:
var shape = new THREE.Mesh(new THREE.ExtrudeGeometry(drawShape(), options), new THREE.MeshLambertMaterial({color: 0x593788}))
function drawShape() {
// create a basic shape
var shape = new THREE.Shape();
shape.moveTo(10, 10);
shape.lineTo(10, 40);
shape.lineTo(30, 40);
shape.lineTo(30, 10);
shape.lineTo(10, 10);
var hole1 = new THREE.Path();
hole1.moveTo(12,15);
hole1.lineTo(15,16);
hole1.lineTo(16,16);
hole1.lineTo(16,15);
hole1.lineTo(12,15);
shape.holes.push(hole1);
return shape;
}
What I am getting is a plain shape figure with no hole
(drew a white hole with Clipping Tool to show where it should be) --->
May I ask why is this happening?
Because when I add absellipse or absarc - the hole works fine.
var hole2 = new THREE.Path();
hole2.absellipse(23, 24, 2, 3, 0, Math.PI * 2, true);
shape.holes.push(hole2);
Is it impossible to push holes made from line points instead of ellipse or arc?
If you have any idea why this does not work or how to fix it, please help.

use TextGeometry.js in Three.js

I was wondering how you use the TextGeometry.js from the extras folder in three.js in your three.js scene. I was trying to add text to my scene. I found this file and have no idea how to implement/use it. Here's a link to the file:
https://github.com/mrdoob/three.js/blob/master/src/extras/geometries/TextGeometry.js
First create a JSON font here.
Then load it like this:
var loader = new THREE.FontLoader();
loader.load("fonts/your_font.json", function(font) {
var textGeo = new THREE.TextGeometry("This is a 3D text", {
font: font,
size: 50,
height: 10,
curveSegments: 12,
bevelThickness: 1,
bevelSize: 1,
bevelEnabled: true
});
var textMat = new THREE.MeshLambertMaterial({color: 0xFF00FF});
var textMesh = new THREE.Mesh(textGeo, textMat);
scene.add(textMesh);
});

Three.js - BufferGeometry warning Render count or primcount is 0 when no index is specify

I want to create a BufferGeometry without setting the indices.
(As written here, in this case the renderer assumes that each three contiguous positions represent a single triangle), but I get the warning Render count or primcount is 0 and no geometry is shown.
What am I doing wrong?
Here following the code to reproduce the issue.
var buffGeometry = new THREE.BufferGeometry();
buffGeometry.attributes =
{
position:
{
itemSize: 3, array: new Float32Array([10, 0, 0, 0, 10, 0, 0, 0, 0, 0, 10, 0, 10, 0, 0, 10, 10, 0]),
numItems: 18
}
};
indexArray = new Uint32Array([0, 1, 2, 3, 4, 5]);
< !--it works adding the index array with the following line-- >
// buffGeometry.setIndex(new THREE.BufferAttribute(indexArray, 1));
material = new THREE.MeshBasicMaterial( { color: 0xff0000 } );
var mesh = new THREE.Mesh(buffGeometry, material);
scene.add(mesh);
three.js r77
(Here the complete sample)
Documentation here:
http://threejs.org/docs/index.html#Reference/Core/BufferGeometry
In short, you're not supposed to set the attribute property directly.
Instead, you're supposed to create a THREE.BufferAttribute and then add it to the geometry by calling .addAttribute('position', bufferAttribute)
EDIT: Not sure how setIndex work, does it actually render anything or not crash?
In my case I was receiving this warning when using this code, which was working completely fine:
const curveMesh = new THREE.Mesh()
let curve
allCoords.forEach(coords => {
curve = new Curve(coords, material, step)
curveMesh.add(curve.mesh)
curveMesh.add(curve.meshOrigin)
curveMesh.add(curve.meshDestination)
})
rootMesh.add(curveMesh)
When I replaced it with this line, it started to not see the warning anymore [.WebGL-0x7fe61b026400]RENDER WARNING: Render count or primcount is 0.
// please notice here is now Group!
const curveMesh = new THREE.Group()
let curve
allCoords.forEach(coords => {
curve = new Curve(coords, material, step)
curveMesh.add(curve.mesh)
curveMesh.add(curve.meshOrigin)
curveMesh.add(curve.meshDestination)
})
rootMesh.add(curveMesh)

Categories

Resources