How to speed up the loading of a model using ldraw-loader and threeHS?

Hi!

I built a website that uses a dropdown to select and load different LDR models (some kind of 3D Lego Model Viewer).

The models are deployed in a folder within project.

Loading and switching between the models sometimes takes a while (few seconds).

Is there a way to make it faster?

Here is the code of my main.js file:

import * as THREE from 'three';
import { LDrawLoader } from 'three/addons/loaders/LDrawLoader.js';
import WebGL from 'three/addons/capabilities/WebGL.js';
import { OrbitControls } from 'https://threejs.org/examples/jsm/controls/OrbitControls.js';
import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js';

function init() {

  // Reset camera to initial position and rotation
  camera.position.copy(initialCameraPosition);
  camera.rotation.copy(initialCameraRotation);

  // Reset controls
  controls.reset();

  // Set camera position
  camera.position.x = 150;
  camera.position.y = 150;
  camera.position.z = 250;

  // Set background color
  renderer.setClearColor(0xb3b3b3);

  // Activate shadows
  renderer.shadowMap.enabled = true;

  // Initialize controls
  controls.enableZoom = true;
}

function animate() {
  requestAnimationFrame(animate);

  // Update controls
  controls.update();

  renderer.render(scene, camera);
}

function onWindowResize() {

  camera.aspect = ratio;
  camera.updateProjectionMatrix();

  renderer.setSize( container.innerWidth, container.innerHeight );
}

// Function to load a new model based on the selected option
async function loadModel(modelName) {
  // Assuming models are in the "models" folder
  const modelPath = `/models/${modelName}.ldr`;

  // Clear the existing scene
  scene.children = [];

  // Load the new model
  loader.load(
    modelPath,
    function (group) {
      legoModel = group;
      // Rotate the loaded model 180 degrees around the x-axis
      legoModel.rotation.x = Math.PI;
      legoModel.traverse(function (child) {
        if (child.isMesh) {
          // Set material to Phong material
          //child.material = new THREE.MeshPhongMaterial({ color: 0xaaaaaa, specular: 0x111111, shininess: 200 });
          // OR set material to Lambert material
          //child.material = new THREE.MeshLambertMaterial({ color: 0xaaaaaa });
        }
      });
      scene.add(legoModel);
    },
    function (xhr) {
      console.log((xhr.loaded / xhr.total) * 100 + '% loaded');
    },
    function (error) {
      console.log('An error occurred');
    }
  );
}

// Function to fetch the list of model names
async function fetchModelList() {
  try {
    // Assuming you have an endpoint that provides the list of models
    const response = await fetch('/models/modelList');
    const data = await response.json();

    // Populate the dropdown with model names
    const modelSelect = document.getElementById('modelSelect');
    data.models.forEach((modelName) => {
      const option = document.createElement('option');
      option.value = modelName;
      option.text = modelName;
      modelSelect.add(option);
    });

    // Trigger loading the default model
    const defaultModel = modelSelect.value;
    loadModel(defaultModel);
  } catch (error) {
    console.error('Error fetching model list:', error);
  }
}

// Call the function to fetch and populate the model list
fetchModelList();

// Renderer
const renderer = new THREE.WebGLRenderer();
const ratio = window.devicePixelRatio;
renderer.setPixelRatio(ratio);
// Desired height
const windowHeight = 469;
// Calculate the width to maintain the natural aspect ratio
const windowWidth = (windowHeight / window.innerHeight) * window.innerWidth;
// Set window size for viewer window
renderer.setSize(windowWidth, windowHeight);
renderer.toneMapping = THREE.ACESFilmicToneMapping;

const pmremGenerator = new THREE.PMREMGenerator( renderer );

// Scene
const scene = new THREE.Scene();
scene.environment = pmremGenerator.fromScene( new RoomEnvironment( renderer ) ).texture;
// Ambient light
const ambientLight = new THREE.AmbientLight(0xffffff, 1000);

// Add light
scene.add(ambientLight);

const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
// Store initial camera position and rotation
const initialCameraPosition = new THREE.Vector3(0, 0, 400);
const initialCameraRotation = new THREE.Euler(0, 0, 0);

const loader = new LDrawLoader();

// Path to the parts
var partsLibraryPath = "/models/ldraw/officialLibrary/";
loader.setPartsLibraryPath(partsLibraryPath);

// Load materials
await loader.preloadMaterials('models/ldraw/officialLibrary/LDCfgalt.ldr');

const shadingButtonActive = false;

const controls = new OrbitControls(camera, renderer.domElement);

let legoModel;

// Call init function
init();

// Get container for viewer window
const container = document.getElementById('webgl-container');

// Add renderer to container
container.appendChild(renderer.domElement);

// Error handling
if (WebGL.isWebGLAvailable()) {
  // Initiate functions or other initializations here
  animate();
} else {
  const warning = WebGL.getWebGLErrorMessage();
  container.appendChild(warning);
}

$(document).ready(function () {

  $("#resetBtn").click(function () {
    init();
    animate();
    return false;
  });

  $("#bkgColorBtn").click(function () {

    $('#model-bg-color').click();

  });

  $('#model-bg-color').on('change', function (e) {
    renderer.setClearColor(e.target.value);
    animate();
  });
});

// Event listener for mouse down (left-click)
let isMouseDown = false;
container.addEventListener('mousedown', (event) => {
  if (event.button === controls.mouseButtons.LEFT) {
    isMouseDown = true;
  }
});

// Event listener for mouse up
container.addEventListener('mouseup', (event) => {
  if (event.button === controls.mouseButtons.LEFT) {
    isMouseDown = false;
  }
});

// Event listener for mouse move
container.addEventListener('mousemove', (event) => {
  if (isMouseDown) {
    // Accumulate the rotation based on mouse movement
    camera.rotation.x -= event.movementY / window.innerHeight * 2 * Math.PI;

    // Restrict vertical rotation within a certain range (e.g., between -PI/2 and PI/2)
    camera.rotation.x = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, camera.rotation.x));

    // Rotate the model based on mouse movement
    camera.rotation.y -= event.movementX / window.innerWidth * 2 * Math.PI;
  }
});

// Event listener for mouse leave
container.addEventListener('mouseleave', () => {
  isMouseDown = false;
});

// Event listener for resize
window.addEventListener( 'resize', onWindowResize );

// Event listener for changes in the dropdown
document.getElementById('modelSelect').addEventListener('change', function () {
  const selectedModel = this.value;
  loadModel(selectedModel);
});

And this is the corresponding html code für the dropdown and the viewer window:

<div class="col-sm-8 text-left"> 
        <h2>Bots Viewer</h2>
        <hr>

        <form name="modelSelection" id="modelSelection" action="">
          <p>Modell Auswahl:</p>
          <select name="model" id="modelSelect">
          </select>
        </form> 
        <br><br>
        
        // buttons and other stuff
        
        <div id="webgl-container"></div>
        <script type="module" src="/views/main.js"></script>

        <hr>

      </div>