Reputation: 43
(I'm new to Three.js)
I'm working on a 3D Viewer for multiple stl models. Every model should get rendered in a seperate div, so you'll get a grid with different models. The filenames are stored into a json file. With a for loop I loop through the json and store the filename in "var filename". I use that variable to complete the path to the model.
var filename = data[i].Filename;
With a for loop I am able to get all the filenames out of the json file.
loader.load('model/' + filename, function(geometry) {...
I then add filename to complete the model path.
modelContainer = document.createElement('div');
modelContainer.className = modelClass;
document.getElementById(modelId).appendChild(modelContainer);
A div with a classname gets created inside the div with the given ID. And the model should get rendered into a canvas inside the div (the div with the class).
See image What happens now is that the code puts all the models (3 models in this example) into the last div. The first two divs that were meant for model 1 and 2 are black.
After lots of experimenting and analysing I couldn't find the solution yet. Maybe it could be that I have to store all the scenes in an array, and loop all the scenes in the render function?
You can find the full project code (without the .stl models) here: https://codepen.io/indoguy/project/editor/XqNKGQ
<div class="container">
<div class="row" id="modelGrid">
<!-- The models will be uploaded within this grid -->
</div>
</div>
<script src="js/three.js"></script>
<script src="js/STLLoader.js"></script>
<script src="js/WebGL.js"></script>
<script>
var modelDiv = document.getElementById('modelGrid');
var ourRequest = new XMLHttpRequest();
// In this code snippit I will read the model filenames out of the json.
// Add URL where the json is stored
ourRequest.open('GET', 'http://localhost:8888/3D%20Modeling/3D%20Previewer/3D%20Previewer%20Library/models.json');
ourRequest.onload = function() {
var queueData = JSON.parse(ourRequest.responseText);
renderHTML(queueData);
};
ourRequest.send();
function renderHTML(data) {
// 3D Model Process
if (WEBGL.isWebGLAvailable() === false) {
document.body.appendChild(WEBGL.getWebGLErrorMessage());
}
// Editable
var divWidth = 300;
var divHeight = 300;
var modelId = 'modelGrid';
var modelClass = 'col-sm';
// Don't change
var modelContainer;
var camera, cameraTarget, scene, renderer;
var cameraFov = 45;
init(data);
animate();
// init
function init(data) {
// For loop for every model in the json file
for (i = 0; i < data.length; i++) {
var filename = data[i].Filename;
// Scene
scene = new THREE.Scene();
scene.background = new THREE.Color(0xffe5e5);
// Camera
camera = new THREE.PerspectiveCamera(cameraFov, divWidth / divHeight, 1, 15);
cameraTarget = new THREE.Vector3(0, -0.25, 0);
// Creates a div with the name of the value of var className, inside modelGrid id
modelContainer = document.createElement('div');
modelContainer.className = modelClass; // Add class to the div
document.getElementById(modelId).appendChild(modelContainer);
// STL Loader
var loader = new THREE.STLLoader();
loader.load('model/' + filename, function(geometry) {
var material = new THREE.MeshStandardMaterial({
color: 0xff1919,
roughness: 0.5,
metalness: 0
});
var mesh = new THREE.Mesh(geometry, material);
mesh.position.set(0, -0.5, 0); // Position with the Y value
mesh.rotation.set(0, -Math.PI / 2, 0);
mesh.scale.set(0.15, 0.15, 0.15);
scene.add(mesh); // add model to the scene
});
// Lights
scene.add(new THREE.HemisphereLight(0xaaaaaa, 0x444444));
var light = new THREE.DirectionalLight(0xffffff, 0.5);
light.position.set(1, 1, 1);
scene.add(light);
// renderer
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setClearColor(0xffffff, 1);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(divWidth, divHeight);
// renderer.setSize(window.innerWidth, window.innerHeight);
renderer.gammaInput = true;
renderer.gammaOutput = true;
renderer.shadowMap.enabled = true;
modelContainer.appendChild(renderer.domElement);
// window.addEventListener('resize', onWindowResize, false);
}
}
// animate
function animate() {
render();
requestAnimationFrame(animate);
}
// render
function render() {
renderer.setClearColor(0xffffff);
var timer = Date.now() * 0.0005;
camera.position.x = Math.cos(timer) * 3;
camera.position.z = Math.sin(timer) * 3;
camera.lookAt(cameraTarget);
renderer.render(scene, camera);
}
};
</script>
Upvotes: 4
Views: 723
Reputation: 2534
The issue is that you're only saving and rendering a single three.js renderer. In your for loop this line is overwriting the global renderer
variable, which is referenced in your render()
function:
renderer = new THREE.WebGLRenderer({
antialias: true
});
There are a few ways to fix this including adding the renderers and cameras into an array and iterating over them in a single render function or creating a new animate()
loop for each renderer, etc. Here's a rough example of the second option:
// For loop for every model in the json file
for (i = 0; i < data.length; i++) {
var filename = data[i].Filename;
// EDIT Fix3: These variables must be defined inside of the for loop
// scope otherwise they will be shared by all loop iterations
var modelContainer;
var camera, cameraTarget, scene;
// ...
// Fix 1: Add var declaration so the variable is scoped to this
// block and variables are not defined globally and overwritten on
// subsequent iterations. This is needed for all the variables
// in this block (scene, camera, etc)
// renderer
var renderer = new THREE.WebGLRenderer({
antialias: true
});
// ...
// Fix 2: Create an animation loop for each renderer instead
// of a single global one
// animate
function animate() {
render();
requestAnimationFrame(animate);
}
// render
function render() {
renderer.setClearColor(0xffffff);
var timer = Date.now() * 0.0005;
camera.position.x = Math.cos(timer) * 3;
camera.position.z = Math.sin(timer) * 3;
camera.lookAt(cameraTarget);
renderer.render(scene, camera);
}
animate();
}
There are definitely cleaner approaches to this but hopefully this gets you started! Let me know if that works out or you need clarification.
edit: I just noticed that your image shows all three models displayed on top of each other. This is again because the scene
variable is declared globally and by the time the asynchronous load
function is fired it is set to the final scene object.
Upvotes: 2