Source: draw.js

import * as THREE from "three";

/**
 * Draw a particle point cloud
 * @param {THREE.Vector3[]} positions List of positions for all particles
 * @param {THREE.Color[]} colors List of colours for all particles
 * @param {number} size Particle size
 * @param {boolean} sizeAttenuation If true, draw size relative to distance from camera
 * @param {string} texturePath Path to image used as texture
 * @returns {THREE.Points} Three.js Object containing the points
 */
function drawParticles(positions, colors, size=10, sizeAttenuation = true, texturePath = "resources/circle.png") {
    const loader = new THREE.TextureLoader();
    const texture = loader.load(texturePath);

    const geometry = new THREE.BufferGeometry();
    geometry.setAttribute("position", new THREE.Float32BufferAttribute(positions, 3));
    geometry.setAttribute("color", new THREE.Float32BufferAttribute(colors, 3));
    geometry.computeBoundingSphere();

    const material = new THREE.PointsMaterial({
        size: size,
        vertexColors: true,
        map: texture,
        sizeAttenuation: sizeAttenuation,
        alphaTest: 0.5
    });

    return new THREE.Points(geometry, material);
}

/**
 *
 * @param {THREE.BufferGeometry} geometry Base geometry to use
 * @param {any[]} elements
 * @param {function(new:THREE.Material)} material Constructor for a THREE.Material (default THREE.MeshLambertMaterial)
 * @returns {THREE.InstancedMesh} Three.js Object containing the instanced objects
 */
function drawInstances(geometry, elements, material) {
    const count = elements.length;
    const mesh = new THREE.InstancedMesh(geometry, material, count);
    const matrix = new THREE.Matrix4();
    for (let i=0; i < count; i++) {
        matrix.compose(
            elements[i].position,
            elements[i].quaternion,
            elements[i].scale
        );
        mesh.setMatrixAt(i, matrix);
        mesh.setColorAt(i, elements[i].color);
    }
    return mesh;
}

function updateAllInstances(mesh, elements) {
    const matrix = new THREE.Matrix4();
    const count = elements.length;
    for (let i=0; i < count; i++) {
        matrix.compose(
            elements[i].position,
            elements[i].quaternion,
            elements[i].scale
        );
        mesh.setMatrixAt(i, matrix);
        mesh.setColorAt(i, elements[i].color);
    }
    mesh.instanceMatrix.needsUpdate = true;
    mesh.instanceColor.needsUpdate = true;
}

function updateInstance(mesh, element, instanceId, matrix = new THREE.Matrix4()) {
    matrix.compose(
        element.position,
        element.quaternion,
        element.scale
    );
    mesh.setMatrixAt(instanceId, matrix);
    mesh.setColorAt(instanceId, element.color);
    mesh.instanceMatrix.needsUpdate = true;
    mesh.instanceColor.needsUpdate = true;
}


export {drawParticles, drawInstances, updateAllInstances, updateInstance};