I am trying to make an app which adds models from Google poly in GLTF format.
I had the issue of some models being extremely large when added on the scene which I solved by computing their size with bounding box max and min values and setting the scale amount.
Now after adding the objects to the scene when I open the inspector and drag to scale objects, even with a small amount of drag the objects becomes very large.
If there is any way to reset the scale value of loaded objects so that default value can be the value which I computed and this can also solve the drag to scale issue.
Note: The computed scale factor for some elements goes to 0.00001 for x, y, z.
Use the three.js API within A-Frame components to compute a bounding box of the model, then scale it down to the size you prefer. Example:
AFRAME.registerComponent('autoscale', {
schema: {type: 'number', default: 1},
init: function () {
this.scale();
this.el.addEventListener('object3dset', () => this.scale());
},
scale: function () {
const el = this.el;
const span = this.data;
const mesh = el.getObject3D('mesh');
if (!mesh) return;
// Compute bounds.
const bbox = new THREE.Box3().setFromObject(mesh);
// Normalize scale.
const scale = span / bbox.getSize().length();
mesh.scale.set(scale, scale, scale);
// Recenter.
const offset = bbox.getCenter().multiplyScalar(scale);
mesh.position.sub(offset);
}
});
HTML:
<a-entity autoscale="2" gltf-model="stereo.gltf"></a-entity>
The code above will fit your model to a ~2m box, and re-center it. For more information see THREE.Object3D documentation.
three.js r89, A-Frame 0.8.0.
Related
I've been trying to import a gltf model from blender into three js, but I'm having trouble rotating it as whenever I try to set its quaternion to anything but (0,0,0,1), the model is deformed. I've included screenshots of how the model looks with the (0,0,0,1) quaternion and then what it looks like when I change the quaternion to (0,1,0,1).
Here's my code for importing the model
loader.load('./resources/Machina_Colored.gltf',
function(gltf){
//pos and quat are a THREE.Vector3 and THREE.Quaternion respectively
const mMass = 0;
const size = new THREE.Vector3(4,8,4); //this is the size of the model in blender
const mShape = new Ammo.btBoxShape(new Ammo.btVector3(size.x * 0.5, size.y * 0.5, size.z * 0.5));
mShape.setMargin(0.05);
const mObj = gltf.scene.children[0];
createRigidBody(mObj,mShape,mMass,pos,quat); //this seems to work with any quaternion it's just the model itself that is having problems
},
function(xhr){
console.log(((xhr.loaded/xhr.total) * 100) + "% loaded");
},
function(error){
console.log('An error occured');
});
Here is the model (working normally) with the (0,0,0,1) Quaternion
And then here's what happens when I use the (0,1,0,1) Quaternion
I've also included a screenshot of what my file looks like in blender just in case
Thanks to WestLangley's comment I added these lines of code to the top of my program
const eul = new THREE.Euler(0,0,0); //this now controls the rotation of the model
quat.setFromEuler(eul);
and the rotation is reflected without deformation
I am trying to apply an image dynamically on a gltf loaded mesh.
The code to load the model looks like:
const gltfLoader = new GLTFLoader();
const url = 'resources/models/mesh.gltf';
gltfLoader.load(url, (gltf) => {
const root = gltf.scene;
scene.add(root);
})
When looking from top the element looks like a rounded rect:
When inspecting the imported mesh I can see that the BufferGeometry has a count of 18.000 points:
Everything works fine however if I apply the texture like this:
const texture = new THREE.TextureLoader().load( 'textures/land_ocean_ice_cloud_2048.jpg' );
const material = new THREE.MeshBasicMaterial( { map: texture } );
root.children[0].material = material;
The image is not visible but the mesh is now colored in 1 color.
Is it possible to apply the image just on the top face of the rect?
Hard to tell what the problem is without seeing the resulting image. However, I would just assign a new texture like this: root.children[0].material.map = texture instead of creating a whole new material, since you don't want to lose all the material attributes that came in the GLTF.
Additionally, MeshBasicMaterial always looks flat because it is not affected by lights.
I have a working GLTF animation that automatically starts playing when the page loads, however whenever I try to add a material to it, the animation no longer plays but the material appears. How do I fix this or if there is an easier way just to add a block colour to a gltf model please let me know, thanks.
var loader = new THREE.GLTFLoader();
loader.setDRACOLoader( new THREE.DRACOLoader() );
// Load a glTF resource
loader.load(
// resource URL
'../models/fox3.gltf',
// called when the resource is loaded
function ( gltf ) {
gltf.animations; // Array<THREE.AnimationClip>
gltf.scene; // THREE.Scene
gltf.scenes; // Array<THREE.Scene>
gltf.cameras; // Array<THREE.Camera>
gltf.asset; // Object
//Loading in and positioning model
var object = gltf.scene;
object.scale.set(10,10,10);
object.position.set (-300, 20,-400);
object.rotation.y = 0.5;
//Playing Animation
mixer = new THREE.AnimationMixer(gltf.scene);
console.log(gltf.animations)
mixer.clipAction( gltf.animations[0] ).play();
//Adding texture/colour to model (causes animation to stop playing)
// materialObj = new THREE.MeshBasicMaterial( { color: "#9E4300"} );
// object.traverse(function(child){
// if (child instanceof THREE.Mesh){
// //child.material = materialObj;
// }
// });
console.log(object);
scene.add( object )
});
or if there is an easier way just to add a block colour to a gltf model please let me know, thanks.
I'll address this last part of your question. The MeshBasicMaterial has the lighting calculations turned off, and in glTF this is supported with an extension called KHR_materials_unlit.
Here's a sample model called BoxUnlit.gltf that shows this extension in action. Two key places to note are ExtensionsUsed at the top, and the material near the bottom.
One major gotcha here is that the material's BaseColorFactor is specified in linear colorspace, while textures are provided in sRGB colorspace. So you have to take your chosen color and convert it to linear, typically by mathematically raising each component to the power of 2.2.
For example, your question contains color value #9E4300, which in 0..255 scale is equal to (158, 67, 0). Divide each number by 255, then raise by 2.2:
Red == (158 / 255.0) ** 2.2 == 0.348864834
Green == ( 67 / 255.0) ** 2.2 == 0.052841625
Blue == ( 0 / 255.0) ** 2.2 == 0.0
Use those values as the RGB values of the glTF model's BaseColorFactor, along with an alpha value of 1.0, like so:
"materials": [
{
"pbrMetallicRoughness": {
"baseColorFactor": [
0.348864834,
0.052841625,
0.0,
1.0
]
},
"extensions": {
"KHR_materials_unlit": {}
}
}
],
With that, ThreeJS should automatically select the MeshBasicMaterial for the model.
Try changing the skinning to true on the material of gltb and it should work. I had the same problem earlier.
I've added text to my scene using the code from https://threejs.org/docs/#api/geometries/TextGeometry
I cannot figure out how to change the extrude depth, which is much "thicker" than I would like. Here is the exact code I am using:
var loader = new THREE.FontLoader();
loader.load( 'fonts/font.json', function ( font ) {
var textGeometry = new THREE.TextGeometry( 'WELCOME', {
font: font,
size: 4,
height: 8,
amount: 8,//attempt to change the depth but doesn't work
} );
Is it possible to change the extrude amount for a text geometry?
Applying height and amount at the same time does not work. amount is an option for ExtrudeGeometry whereas height is intended for TextGeometry.
TextGeometry uses internally ExtrudeGeometry for geometry generation. In this context, height is mapped to amount.
three.js R92
I do not want to change the camera to fit the model. I would prefer to know the correct dimensions given a location to make sure the model/geometry once scaled will be in the screen.
I don't want to move the camera since I might have many different objects.
Thoughts?
You probably just want to scale the model, with scale="0.1 0.1 0.1" or whatever. To do that automatically, consider writing a new component:
AFRAME.registerComponent('autoscale', {
init: function () {
var el = this.el;
el.addEventListener('model-loaded', function (e) {
var bbox = new THREE.Box3().setFromObject(e.detail.model);
var scale = 1 / (bbox.max.x - bbox.min.x);
el.setAttribute('scale', {
x: scale,
y: scale,
z: scale,
});
});
}
});
To be more robust, you should probably use the largest dimension (x vs y vs z) for scaling, but this is the gist of it.