javascriptthree.js3dgeometry

Showing stats of vertices and triangle counts on editor


In the example editor how does Three.js rendering a legend showing the number of Vertices and Triangles and also a 3 axes helper legend, I added screen shot of the scene with these legends.

Three.js editor scene

I want to show in my scene but couldn't find how to add these. Is there an addon or a custom geometry for this kind of stuff?


Solution

  • Basically you can take a look at this thread, but some information there needs to be updated.

    You can use the .info property, but pay attention to:

    Moreover, as pailhead rightly points out, obtaining uniform values ​​can be difficult due to non-indexed and indexed geometries. In the first one, the vertices are not shared between the triangles, which means that there will be more of them, and in the second one, fewer because they share them.

    As for rStats, it will no longer work because the WebGL1 standard has been deprecated since r117/153, and completely removed in the r163. If you will be using, which is unlikely, much older versions with WebGL1 then you can use this, in the latest it will not work due to the use of WebGL2.

    Migration Guide

    You can create a quasi-rStat using TextGeometry:

    As for this camera switcher, you can animate this transition between views using some library (e.g. GSAP, TWEEN)

    body { margin: 0; }
    #cameraSwitcher {
            position: fixed;
            bottom: 10px;
            right: 10px;
            display: flex;
            flex-direction: column;
          }
    .camera-button {
            width: 40px;
            height: 40px;
            margin: 5px;
            background-color: #800080;
            color: white;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            font-size: 16px;
            display: flex;
            align-items: center;
            justify-content: center;
          }
    <script type="importmap">
      {
    "imports": {
      "three": "https://unpkg.com/three@0.166.0/build/three.module.js",
      "three/addons/": "https://unpkg.com/three@0.166.0/examples/jsm/"
    }
      }
    </script>
    
    <div id="cameraSwitcher">
    <button class="camera-button" id="camera1">1</button>
    <button class="camera-button" id="camera2">2</button>
    </div>
    
    <script type="module">
    import * as THREE from "https://esm.sh/three";
    import { OrbitControls } from "https://esm.sh/three/addons/controls/OrbitControls.js";
    import { FontLoader } from "https://esm.sh/three/addons/loaders/FontLoader.js";
    import { TextGeometry } from "https://esm.sh/three/addons/geometries/TextGeometry.js";
    
            const scene = new THREE.Scene();
            const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
            camera.position.z = 5;
    
            const camera2 = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
            camera2.position.set(5, 5, 5);
            camera2.lookAt(new THREE.Vector3(0, 0, 0));
    
            const renderer = new THREE.WebGLRenderer({ antialias: true });
            renderer.setSize(window.innerWidth, window.innerHeight);
            renderer.setPixelRatio(2);
            document.body.appendChild(renderer.domElement);
    
            const indexedGeometry = new THREE.ConeGeometry(1, 2, 32);
            const indexedMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true });
            const indexedCone = new THREE.Mesh(indexedGeometry, indexedMaterial);
            scene.add(indexedCone);
            indexedCone.position.x = 2;
    
            const nonIndexedGeometry = indexedGeometry.toNonIndexed();
            const nonIndexedMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: true });
            const nonIndexedCone = new THREE.Mesh(nonIndexedGeometry, nonIndexedMaterial);
            scene.add(nonIndexedCone);
            nonIndexedCone.position.x = -2;
    
            function getVertexCount(geometry) {
                return geometry.attributes.position.count;
            }
    
            function getTriangleCount(geometry) {
                if (geometry.index) {
                    return geometry.index.count / 3;
                } else {
                    return geometry.attributes.position.count / 3;
                }
            }
    
            const fontLoader = new FontLoader();
            fontLoader.load('https://threejs.org/examples/fonts/helvetiker_regular.typeface.json', (font) => {
                const indexedVertexCount = getVertexCount(indexedGeometry);
                const indexedTriangleCount = getTriangleCount(indexedGeometry);
                const indexedTextGeometry = new TextGeometry(`Indexed Cone: Vertices: ${indexedVertexCount} Triangles: ${indexedTriangleCount}`, {
                    font: font,
                    size: 0.2,
                    depth: 0.01
                });
                const indexedTextMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
                const indexedTextMesh = new THREE.Mesh(indexedTextGeometry, indexedTextMaterial);
                indexedTextMesh.position.set(-2, 3, 0);
                scene.add(indexedTextMesh);
    
                const nonIndexedVertexCount = getVertexCount(nonIndexedGeometry);
                const nonIndexedTriangleCount = getTriangleCount(nonIndexedGeometry);
                const nonIndexedTextGeometry = new TextGeometry(`Non-Indexed Cone: Vertices: ${nonIndexedVertexCount} Triangles: ${nonIndexedTriangleCount}`, {
                    font: font,
                    size: 0.2,
                    depth: 0.01
                });
                const nonIndexedTextMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
                const nonIndexedTextMesh = new THREE.Mesh(nonIndexedTextGeometry, nonIndexedTextMaterial);
                nonIndexedTextMesh.position.set(-2, 2, 0);
                scene.add(nonIndexedTextMesh);
            });
            
            //const controls = new OrbitControls( camera, renderer.domElement );
            //controls.enableDamping = true
    
            let currentCamera = camera;
    
            document.getElementById('camera1').addEventListener('click', () => {
                currentCamera = camera;
            });
    
            document.getElementById('camera2').addEventListener('click', () => {
                currentCamera = camera2;
            });
    
            function animate() {
                requestAnimationFrame(animate);
                indexedCone.rotation.x += 0.01;
                indexedCone.rotation.y += 0.01;
                nonIndexedCone.rotation.x += 0.01;
                nonIndexedCone.rotation.y += 0.01;
                //controls.update();
                renderer.render(scene, currentCamera);
            }
    
            animate();
    
            window.addEventListener('resize', () => {
                renderer.setSize(window.innerWidth, window.innerHeight);
                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();
                camera2.aspect = window.innerWidth / window.innerHeight;
                camera2.updateProjectionMatrix();
            });
    
    </script>

    Or you can use HTML layer

    body { margin: 0; }
            canvas { display: block; }
            #hud {
                position: absolute;
                top: 10px;
                left: 10px;
                color: white;
                font-family: Arial, sans-serif;
                font-size: 16px;
                z-index: 1;
            }
            button {
                position: absolute;
                top: 10px;
                background-color: #800080;
                color: white;
                border: none;
                border-radius: 5px;
                padding: 10px;
                font-size: 16px;
                cursor: pointer;
                z-index: 2;
            }
            #camera1 {
                right: 120px;
            }
            #camera2 {
                right: 10px;
            }
          
    <script type="importmap">
      {
    "imports": {
      "three": "https://unpkg.com/three@0.166.0/build/three.module.js",
      "three/addons/": "https://unpkg.com/three@0.166.0/examples/jsm/"
    }
      }
    </script>
    
    <div id="hud">
    <div id="indexed-info"></div>
    <div id="non-indexed-info"></div>
    </div>
    <button id="camera1">Camera 1</button>
    <button id="camera2">Camera 2</button>
    
    <script type="module">
    import * as THREE from "https://esm.sh/three";
    import { OrbitControls } from "https://esm.sh/three/addons/controls/OrbitControls.js";
    
    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
    camera.position.z = 5;
    
    const camera2 = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
    camera2.position.set(5, 5, 5);
    camera2.lookAt(new THREE.Vector3(0, 0, 0));
    
    const renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.setPixelRatio(2);
    document.body.appendChild(renderer.domElement);
    
    const indexedGeometry = new THREE.ConeGeometry(1, 2, 32);
    const indexedMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true });
    const indexedCone = new THREE.Mesh(indexedGeometry, indexedMaterial);
    scene.add(indexedCone);
    indexedCone.position.x = 2;
    
    const nonIndexedGeometry = indexedGeometry.toNonIndexed();
    const nonIndexedMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: true });
    const nonIndexedCone = new THREE.Mesh(nonIndexedGeometry, nonIndexedMaterial);
    scene.add(nonIndexedCone);
    nonIndexedCone.position.x = -2;
    
    function getVertexCount(geometry) {
        return geometry.attributes.position.count;
    }
    
    function getTriangleCount(geometry) {
        if (geometry.index) {
            return geometry.index.count / 3;
        } else {
            return geometry.attributes.position.count / 3;
        }
    }
    
    function updateHUD() {
        const indexedVertexCount = getVertexCount(indexedGeometry);
        const indexedTriangleCount = getTriangleCount(indexedGeometry);
        const nonIndexedVertexCount = getVertexCount(nonIndexedGeometry);
        const nonIndexedTriangleCount = getTriangleCount(nonIndexedGeometry);
    
        document.getElementById('indexed-info').textContent = `Indexed Cone: Vertices: ${indexedVertexCount} Triangles: ${indexedTriangleCount}`;
        document.getElementById('non-indexed-info').textContent = `Non-Indexed Cone: Vertices: ${nonIndexedVertexCount} Triangles: ${nonIndexedTriangleCount}`;
    }
    
    const controls = new OrbitControls(camera, renderer.domElement);
    controls.enableDamping = true;
    
    let currentCamera = camera;
    
    document.getElementById('camera1').addEventListener('click', () => {
        currentCamera = camera;
    });
    
    document.getElementById('camera2').addEventListener('click', () => {
        currentCamera = camera2;
    });
    
    function animate() {
        requestAnimationFrame(animate);
        indexedCone.rotation.x += 0.01;
        indexedCone.rotation.y += 0.01;
        nonIndexedCone.rotation.x += 0.01;
        nonIndexedCone.rotation.y += 0.01;
        controls.update();
        updateHUD();
        renderer.render(scene, currentCamera);
    }
    
    animate();
    
    window.addEventListener('resize', () => {
        renderer.setSize(window.innerWidth, window.innerHeight);
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        camera2.aspect = window.innerWidth / window.innerHeight;
        camera2.updateProjectionMatrix();
    });
    
    
    </script>