When using a model as a source to an entity, say gltf, is there a way we know the original size? Since the scale
attribute works on relative size, it seems to be a trial an error to fit the model to our desired size. I tried using the geometry.getComputingBox()
of the mesh of the model but it returns null. Wondering if there is a component that is available that lets us specify the scale in absolute terms.
3 Answers
Ah, figured it out.
var model = this.el.object3D;
var box = new THREE.Box3().setFromObject( model );
var size = box.getSize();
gives you the size. then using the above any desired size can be set.
Created a simple component that can be conveniently used
AFRAME.registerComponent('resize', {
schema: {
axis: {
type: 'string',
default: 'x'
},
value: {
type: 'number',
default: 1
}
},
init: function() {
var el = this.el;
var data = this.data;
var model = el.object3D;
el.addEventListener('model-loaded', function(e) {
var box = new THREE.Box3().setFromObject( model );
var size = box.getSize();
var x = size.x;
var y = size.y;
var z = size.z;
if(data.axis === 'x') {
var scale = data.value / x;
}
else if(data.axis === 'y') {
var scale = data.value / y;
}
else {
var scale = data.value / z;
}
el.setAttribute('scale', scale + ' ' + scale + ' ' + scale);
});
}
});
And it can be used as to proportionately resize the model with x axis length as 0.5
<a-entity resize='axis:x; value:0.5' gltf-model='#model`></a-entity>
(This would have come as a comment but as I don't have enough rep points this is coming as an answer.)
I found that the model doesn't have a size directly after the model-loaded
event listener so I trigger the rescale from the update
method. Funnily enough though if you don't have the model-loaded
event listener then the size of the model will be 0 even after the first update
is fired.
This is my variant of the above code with the difference being that the dimension is set in meters:
const AFRAME = window.AFRAME;
/*
* Scales the object proportionally to a set value given in meters.
*/
AFRAME.registerComponent('natural-size', {
schema: {
width: {
type: "number",
default: undefined // meters
},
height: {
type: "number",
default: undefined // meters
},
depth: {
type: "number",
default: undefined // meters
}
},
init() {
this.el.addEventListener('model-loaded', this.rescale.bind(this));
},
update() {
this.rescale();
},
rescale() {
const el = this.el;
const data = this.data;
const model = el.object3D;
const box = new THREE.Box3().setFromObject(model);
const size = box.getSize();
if ( !size.x && !size.y && !size.z ) {
return;
}
let scale = 1;
if ( data.width ) {
scale = data.width / size.x;
} else if( data.height ) {
scale = data.height ( size.y);
} else if( data.depth ) {
scale = data.depth / size.y;
}
el.setAttribute('scale', `${scale} ${scale} ${scale}`);
},
remove() {
this.el.removeEventListener('model-loaded', this.rescale);
}
});
Then:
<a-entity natural-size='width:0.72' gltf-model='#model`></a-entity>
box.getSize
has changed, I combined what I found here with what I found in another answer and noticed in the console to produce a more minimalist answer just to determine the size itself of a model:
getDimensions(object3d) {
// e.g., object3d = document.querySelector('#goban').object3D
var box = new THREE.Box3().setFromObject( object3d );
var x = box.max.x - box.min.x
var y = box.max.y - box.min.y
var z = box.max.z - box.min.z
return {x,y,z}
}