How can I manually include a triangle in BufferGeometry using Three.js?

Recently, I've been exploring the quickest method to change a mesh's vertices using three.js. Through my experimentation, I discovered that modifying parts of mesh.geometry.attributes.position.array and then setting mesh.geometry.attributes.position.needsUpdate=true effectively updates the vertices without the need to rebuild arrays or recreate opengl buffers. The needsUpdate=true property alters the version number of the attribute, prompting it to resend the attributes vertices array to the opengl buffer.

Curious to understand this process better, I attempted to manually perform the same task by calling gl.bindBuffer() and gl.bufferData(). However, after repeating this process multiple times within a loop, it eventually crashes when I call new Float32Array(). Surprisingly, the crash occurs even though my memory usage is only at 4MB right before the crash. I acknowledge that continuously deallocating/reallocating the array every loop may not be the most efficient approach, as doubling the array's size when it reaches capacity could be a better alternative. Nonetheless, I am eager to uncover why it crashes in this manner.

https://jsfiddle.net/q1txL19c/3/ - Crashes in 20 seconds. However, altering the if(0) condition to if(1) resolves the issue.

What sets three.js apart in preventing such crashes? Why does new Float32Array() fail despite the javascript memory usage being relatively low, as indicated by the profiler?

<!doctype html>
<html>
   <body style='margin:0;padding:0'>
        <script src="https://threejs.org/build/three.js"></script>
        <script>

var camera, scene, renderer, mesh
var triangles = 1
init()

function init()
{
    scene = new THREE.Scene()

    camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, .1, 10000)
    camera.position.z = 15
    scene.add(camera)

    var geometry = new THREE.BufferGeometry()

    var material = new THREE.MeshBasicMaterial( {side: THREE.FrontSide, transparent:false, vertexColors: THREE.VertexColors} )
    mesh = new THREE.Mesh(geometry, material)

    var positions = new Float32Array([1,1,0, 0,1,0, 0,0,0])
    geometry.addAttribute('position', new THREE.BufferAttribute(positions,3))

    var colors = new Float32Array([0,0,1, 0,0,0, 0,0,0])
    geometry.addAttribute('color', new THREE.BufferAttribute(colors,3))

    scene.add(mesh)

    renderer = new THREE.WebGLRenderer()
    renderer.setSize(window.innerWidth, window.innerHeight)
    renderer.setClearColor( 0x6699DD )

    document.body.appendChild(renderer.domElement)

    loop()
}

function addTriangle(geometry)
{
    // Make 3 new vertices, each with x,y,z. 9 total positions.
    var newVertices = []
    for(var i=0; i<9; i++)
        newVertices[i] = Math.random()*10-5

    appendArrayToAttribute(geometry.attributes.position, newVertices)


    // Make 3 new colors, 1 for each new vertex, each with r,g,b. 9 total slots.
    var newColors = []
    for(var i=0; i<9; i++)
        newColors[i] = Math.random()

    appendArrayToAttribute(geometry.attributes.color, newColors)
}

function appendArrayToAttribute(attribute, arrayToAppend)
{
    // Make a new array for the geometry to fit the 9 extra positions at the end, since you can't resize Float32Array
    try
    {
        var newArray = new Float32Array(attribute.array.length + arrayToAppend.length)
    }
    catch(e)
    {
        console.log(e)
        if(!window.alerted)
        {
            alert("out of memory!? can't allocate array size="+(attribute.array.length + arrayToAppend.length))
            window.alerted = true
        }
        return false
    }
    newArray.set(attribute.array)
    newArray.set(arrayToAppend, attribute.array.length)


    attribute.setArray(newArray)

    if(0)
    {
        attribute.needsUpdate = true
    }
    else
    {
        // Have the geometry use the new array and send it to opengl.
        var gl = renderer.context
        gl.bindBuffer(gl.ARRAY_BUFFER, renderer.properties.get(attribute).__webglBuffer)
        gl.bufferData(gl.ARRAY_BUFFER, attribute.array, gl.STATIC_DRAW)
    }

}

function loop()
{
    requestAnimationFrame(loop)

    mesh.rotation.x += 0.01
    mesh.rotation.y += 0.02

    renderer.render(scene, camera)

    for(var i=0;i<10;i++)
    {
        addTriangle(mesh.geometry)
        triangles++
    }
    if(Math.random()<.03)
    {
        console.log("triangles="+triangles)
        var gl = renderer.context
        console.log("gl buffer size="+gl.getBufferParameter(gl.ARRAY_BUFFER, gl.BUFFER_SIZE))
    }
}

      </script>

   </body>
</html>

Answer №1

If you want to incorporate new faces into a BufferGeometry post-initial render, remember to set your geometry attribute buffers to a sufficient size upfront since they cannot be resized.

Furthermore, make sure you update array values instead of creating new arrays each time.

To adjust the number of faces to be rendered, use the following syntax:

geometry.setDrawRange( 0, 3 * numberOfFaces ); // 3 vertices for each face

For more information, check out this related answer and demonstration.

Version: three.js r.92

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

OrbitControls in THREE.JS fail to function properly when a DOM Element is layered on top of the scene

I am attempting to position labels as elements with position:absolute; over a THREEJS scene. The issue arises when the mouse hovers over one of the labels (the red box in the example below), causing the events that trigger OrbitControls to be "halted" by t ...

Click and keystroke fusion

Greetings everyone, I've been attempting to integrate both click and keypress functionalities into my function, but unfortunately, nothing I've attempted so far has yielded any success. Here's the code snippet: function victoryMessage() { ...

Attempting to alert a particular device using Flutter for notification delivery

Currently, I am developing a Chat app using Flutter and attempting to send notifications to specific devices through Firebase functions. Initially, I retrieve the device token and store it in Firebase. Now, my challenge lies in fetching the token and invok ...

Transferring data between actions following an AJAX request in Zend Framework

I am currently utilizing an ajax call to communicate with a controller in order to update the number of articles displayed on the webpage. I have established an action within the controller to handle this ajax request. Below is a snippet of the code: publ ...

typescript: best practices for typing key and value parameters in the forEach loop of Object.entries()

I have a specific object with key/value pairs that I need to iterate over using the entries() method of Object followed by a forEach() method of Array. However, I'm struggling to understand how to avoid a typescript error in this situation: type objTy ...

What could be the issue with my JSON file?

I am currently utilizing the jQuery function $.getJson. It is successfully sending the desired data, and the PHP script generating the JSON is functioning properly. However, I am encountering an issue at this stage. Within my $.getJSON code, my intention ...

struggling to send variables to jade templates with coffeescript and express.js

As a newcomer to node and express, I am currently building the front end of an application that utilizes jade as its templating engine. Despite extensive searching online and within this community, I have not been able to find a solution to a particular is ...

The Material UI date range picker fails to close once a selection has been made

I'm currently using the Material UI date range picker, but I've encountered an issue. After selecting the dates, the date picker does not close automatically. How can I make it close once the dates are selected? I couldn't find any specific ...

After the update to the page, the DOM retains the previous element

I'm currently developing a Chrome Extension (no prior knowledge needed for this inquiry...) and I have encountered an issue. Whenever I navigate to a page with a specific domain, a script is executed. This script simply retrieves the value of the attr ...

Customize Material-UI icons dynamically by changing their props in an array

I am looking to change props (color, size) for multiple icons in an array using Material-UI v4: const ICONS_ARRAY: React.ReactNode[] = [ <AlertCircleCheckOutline />, <AppleSafari />, <MotionPlay />, <AppleKeyboardCommand />, <Fil ...

Unable to locate module using absolute import in a Next.js + TypeScript + Jest setup

Currently in my NextJS project, I am utilizing absolute imports and testing a component with Context Provider. The setup follows the instructions provided in this jest setup guide TEST: import { render, screen } from 'test-util'; import { Sideb ...

Angular JS Form's Pristine feature is malfunctioning when attempting to reset

I implemented a login form on my website. After submitting the form, I clear it and set it to Pristine mode. However, the error message still persists. Below is the code for my form: <form name="loginForm" ng-submit="loginForm.$valid && login( ...

Is there a way to execute JavaScript tests by incorporating debugger statements?

There isn't a one-size-fits-all solution to this question, and it hasn't been addressed on Stack Overflow either. My background is in Python, where I can use import pdb; pdb.set_trace() to debug code step by step with a debugger. How can I achiev ...

Utilize React MaterialUI's ExpansionPanelSummary by incorporating a counter to a specific div id

I am in need of a feature that will automatically increment a counter in the div id every time the render function is invoked. The main goal is to ensure that each div has a unique identifier. Below is the current render function implementation - render ...

Setting up a project with Angular 2 and NodeJS

Hello there, I have some inquiries about organizing the project structure of a MEAN application with Angular2. Initially, I followed the introductory guide on angular.io to create a basic Angular2 app. Now, I am attempting to incorporate this app into a N ...

Customize cursor using CSS

I have a div with overflow: hidden that I am making scrollable by allowing the user to click and drag the background. There are links and buttons within this space. Here is the CSS I am using for this: #div-grabscroll { cursor: url(../img/op ...

Handlebars template engine does not support query parameters

Below is the code snippet I am working with: app.get("/editar-equipo?:id", (req, res) => { const equipos = obtenerEquipos() let equipoSeleccionado for(let i = 0; i < equipos.length; i++){ if(equipos[i].numeroId === ...

Attempting to maintain the main navigation highlighted while browsing through the secondary navigation

I am facing a small issue that seems like it should be an easy fix, but I can't seem to figure it out. While working on my site, I'm having trouble keeping the parent navigation highlighted when scrolling through the sub-menu. If you hover over ...

What is the best way to ensure my button displays the complete description of a single country after showcasing all the countries?

I have designed a search feature for countries and I am looking to add a button next to the country names that will trigger one of my components responsible for displaying detailed information about the country. Currently, the details are shown only when t ...

Ways to monitor and measure clicks effectively

Within my website, I have a table element with one column and numerous rows. Each row serves as a hyperlink to an external shared drive. <tr><td ><a href="file://xxx">Staff1</a></td></tr> <tr ><td ><a h ...