Rendering various models in separate DIVs using Threejs

(Brand new to venturing into Three.js)


Overview

Currently, I am immersed in the development of a 3D Viewer that can showcase multiple stl models simultaneously. Each model is supposed to be rendered within its own separate div element, resulting in a grid layout displaying diverse models. These model filenames are conveniently stored in a JSON file. By utilizing a for loop, I iterate through the JSON data and save each filename as "var filename", which is then used to construct the complete path to the respective model.


Insights

var filename = data[i].Filename;

The above snippet illustrates how all filenames are extracted from the JSON file via a well-structured for loop.

loader.load('model/' + filename, function(geometry) {...

After obtaining the filename, it gets appended to finalize the model path.

modelContainer = document.createElement('div');
modelContainer.className = modelClass;
document.getElementById(modelID).appendChild(modelContainer);

Consequently, a div element with a designated classname is dynamically generated inside the div specified by the given ID. The objective is to render the model content within a canvas housed within this particular div element.


Dilemma Encountered

Check out this image. The current issue at hand pertains to the fact that despite having three distinct models, they all end up being placed within the last div element. Consequently, the first two div elements, which were intended for the initial two models, remain blank.

Despite numerous trials and analytical investigations, the solution to this problem remains elusive. Perhaps storing all the scenes in an array and subsequently iterating through these scenes within the render function might lead to a breakthrough?


Complete CSS and JavaScript Source Code

You can access the entire project's source code (excluding the .stl models) here: https://codepen.io/indoguy/project/editor/XqNKGQ

<div class="container">
    <div class="row" id="modelGrid">
        <!-- Models will be inserted into 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();

    // Extracting model names from JSON
    // Include URL where 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) {
        // Processing 3D Models
        if (WEBGL.isWebGLAvailable() === false) {
            document.body.appendChild(WEBGL.getWebGLErrorMessage());
        }

        // Editable Parameters
        var divWidth = 300;
        var divHeight = 300;
        var modelID = 'modelGrid';
        var modelClass = 'col-sm';

        // Constants
        var modelContainer;
        var camera, cameraTarget, scene, renderer;
        var cameraFOV = 45;


        init(data);
        animate();


        // Initialization
        function init(data) {

            // Loop through each model in the JSON file
            for (i = 0; i < data.length; i++) {

                var filename = data[i].Filename;

                // Scene Setup
                scene = new THREE.Scene();
                scene.background = new THREE.Color(0xffe5e5);

                // Camera Configuration
                camera = new THREE.PerspectiveCamera(cameraFOV, divWidth / divHeight, 1, 15);
                cameraTarget = new THREE.Vector3(0, -0.25, 0);

                // Create a div element based on the value of className within the modelGrid ID
                modelContainer = document.createElement('div');
                modelContainer.className = modelClass; // Add class to the div
                document.getElementById(modelID).appendChild(modelContainer);

                // STL Model 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); // Set Y-axis position
                    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
                });

                // Lighting Effects
                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 Configuration
                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);
            }
        }

        // Animation Play
        function animate() {
            render();
            requestAnimationFrame(animate);
        }

        // Render Function
        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>

Answer №1

The main issue here is that the three.js renderer you're using is being saved and rendered singularly. Within your for loop, the line of code below is continuously overwriting the global renderer variable, which is crucial for your render() function:

renderer = new THREE.WebGLRenderer({
    antialias: true
});

To address this issue, there are multiple solutions such as storing renderers and cameras in an array and iterating through them with a single render function or implementing a unique animate() loop for each renderer. A basic example of the latter option is provided below:

// Loop through every model in the json file
for (i = 0; i < data.length; i++) {

    var filename = data[i].Filename;

    // EDIT Fix3: Ensure these variables are defined within the scope of the for loop 
    // to prevent sharing among all iterations
    var modelContainer;
    var camera, cameraTarget, scene;

    // ...

    // Fix 1: Introduce var declaration for proper scoping within this block
    // avoiding global definition and overwriting in subsequent loops. This applies 
    // to all variables within this segment (scene, camera, etc)
    // renderer
    var renderer = new THREE.WebGLRenderer({
        antialias: true
    });

    // ...

    // Fix 2: Establish a unique animation loop for each renderer instead
    // of a generic 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();    

}

While cleaner solutions exist, this should provide a starting point! Feel free to reach out if you encounter any issues or need further clarification.

edit: Upon reviewing your image, it seems like all three models are overlapping. This occurs because the scene variable is declared globally and by the time the asynchronous load function triggers, it references the final scene object.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The offsetTop property of Angular's nativeElement always returns a value of 0

I am currently working on a menu that will automatically select the current section upon scrolling. However, I am running into an issue where I am consistently getting a value of 0 for the offsetTop of the elements. The parentElement returns a value for of ...

Error encountered with the NextGen (Mirth) Connect and MongoDB Java driver connection

I'm currently in the process of setting up Mirth Connect Server 3.10.1 (Java version: 1.8.0_181) to write FHIR JSON documents to MongoDB. Following the instructions provided in this post, I have included the following drivers in my custom-lib/ directo ...

Node.js is unable to handle the contents of an SNS event message

I am encountering an issue while attempting to extract content from a Message in an SNS event within a Node.js Lambda project Below is the code snippet for processing the message: exports.handler = (event, context, callback) => { var message = event. ...

Hiding a Blogger section when its widget is not visible

Take a look at this code: <b:section id='page-list'> <b:widget id='PageList1' locked='false' type='PageList' version='2' visible='true'> </b:widget> </b:section> I wa ...

Navigating through object arrays using a loop

I have multiple arrays within an object that I would like to iterate through using numeric values. This is necessary in order to assign them to different slots in an accordion loop. The JSON file containing the data looks similar to this (Pokemon used as a ...

Determine the index of items within an array of objects in Lodash where a boolean property is set to true

I am new to using lodash after transitioning from C# where I occasionally used LINQ. I have discovered that lodash can be utilized for querying in a LINQ-style manner, but I'm struggling to retrieve the indexes of items in an array of objects with a b ...

retain the input data from the form by using the keyup event

Currently, I have a scenario where an input field is used to capture user input. Instead of displaying the entered value directly, I am looking to store it in a variable and subsequently use it to retrieve data from a database. Below is the code snippet I ...

Error: Unforeseen token encountered while attempting to import React in Javascript

Upon executing the command 'npm run start', I encountered this error: import React from 'react'; ^^^^^ SyntaxError: Unexpected identifier at Module._compile (internal/modules/cjs/loader.js:721:23) at Object.Module._exten ...

Does the syntax for the $.ajax() function appear to be incorrect in this instance?

I am attempting to use .ajax to retrieve the source HTML of a specific URL (in this case, www.wikipedia.org) and insert it into the body of a document. However, the code provided below is not producing the expected outcome. <!DOCTYPE html> < ...

Unlock the parent of an unnamed iframe

Here's a scenario: <div id="parent"> <iframe....></iframe> </div> If I had the above structure, I could use window.parent.document.getElementById('parent').innerHTML to access it. However, my current situation is di ...

Utilize jQuery to apply inline styles to dynamically created div elements

I am attempting to apply inline styles to a div using jQuery. The current state of the div, as seen in Firebug, is shown below: https://i.sstatic.net/rqhIc.jpg My goal is to retain the existing CSS while adding margin-left:0 and margin-right:0 to the cur ...

JavaScript allows for inserting one HTML tag into another by using the `appendChild()` method. This method

My goal is to insert a <div id="all_content"> element into the <sector id="all_field"> element using Javascript <section id="all_field"></section> <div id="all_content"> <h1>---&nbsp;&nbsp;Meeting Room Booki ...

meteor Error: IDENTIFIER is missing

I recently started following the Angular-Meteor framework tutorial () but I encountered an error towards the end that I'm struggling to resolve. Despite my efforts in looking for a solution, my limited understanding of the framework seems to be hinder ...

Discover the sub strings that fall between two specified regular expressions

I am looking for a way to extract substrings from a string that are between two specified regex patterns. Here are a few examples: My $$name$$ is John, my $$surname$$ is Doe -> should return [name, surname] My &&name&& is John, my & ...

Adding elements to a JSON array in Javascript

Seeking assistance on updating a JSON array. var updatedData = { updatedValues: [{a:0,b:0}]}; updatedData.updatedValues.push({c:0}); This will result in: {updatedValues: [{a: 0, b: 0}, {c: 0}]} How can I modify the code so that "c" becomes part of ...

Leverage the `dispatch` hook within a useEffect function

When it comes to triggering an action upon the React component unmounting, I faced a challenge due to hooks not allowing the use of componentWillUnmount. In order to address this, I turned to the useEffect hook: const dispatch = useDispatch(); useEffect(( ...

Set a checkbox to be pre-checked on page refresh using a PHP conditional statement

Thank you for your patience and understanding as I seek help with my issue. I am currently working on a form that consists of checkboxes which control the boolean values in a database. Upon submission, these values are updated in the database and the page ...

Error occurred due to an improperly formatted authorization header while attempting to upload an object to S3 using the JavaScript SDK

When attempting to upload an object to Amazon S3 using their JavaScript SDK, I encounter the following error message: <Error> <Code>AuthorizationHeaderMalformed</Code> <Message>The authorization header is malformed; the reg ...

Utilizing Vue.js to connect with a Node.js server

After setting up a basic Node.js server, the following code is running successfully: var http = require('http'); var server = http.createServer(); server.on('request', function(req, res) { res.writeHead(200, { 'content ...

What is the process of converting the timing from my stopwatch to a standard time format?

I am currently working on a stopwatch project where I log the time into an array. However, when I try to use Math.min(array) or Math.max(array), it returns NaN (not a number). The time format for the stopwatch is like 00:00:15.91 which is not recognized as ...