How to fix this rendering on label and textarea?


In the video I am showing my issue Label and textfield are not stable where is the issue. Below code I am using-

// create scene
const scene = new THREE.Scene();
const scene1 = new THREE.Scene();
// create axis helper
// let axisHelper = new THREE.AxesHelper(100);
// scene1.add(axisHelper);
// create camera
const camera = new THREE.PerspectiveCamera(
    75,
    window.innerWidth / window.innerHeight,
    0.0001,
    99999
)
camera.position.z = 5;
scene.background = new THREE.Color(0xffffff);

const canvas = document.querySelector('#c');
// create renderer
const renderer = new THREE.WebGLRenderer({ antialias: true, logarithmicDepthBuffer: true, canvas });
//renderer.shadowMap.enabled = true;
renderer.autoClear = false;
renderer.physicallyCorrectLights = false;
renderer.shadowMap.enabled = true;
renderer.shadowMap.needsUpdate = true;
renderer.shadowMap.autoUpdate = false;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.setPixelRatio(window.devicePixelRatio);
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.setSize(window.innerWidth, window.innerHeight);

const labelRenderer = new CSS2DRenderer();
labelRenderer.setSize(window.innerWidth, window.innerHeight);
labelRenderer.domElement.style.position = 'absolute';
labelRenderer.domElement.style.top = '0px';
labelRenderer.domElement.style.pointerEvents = 'none';
document.body.appendChild(labelRenderer.domElement);

function hotspot() {
    $("canvas").one('click', function (event) {
        let mouse = new THREE.Vector2();
        mouse.x = (event.offsetX / window.innerWidth) * 2 - 1;
        mouse.y = -(event.offsetY / window.innerHeight) * 2 + 1;
        let [mouseX, mouseY] = d3.pointer(event);
        mouse_position = [mouseX, mouseY];
        mouse_position[0] = mouse_position[0] + 15;
        mouse_position[1] = mouse_position[1] - 15;
        const vector = new THREE.Vector3(mouse.x, mouse.y, 0.5).unproject(camera);
        const raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
        raycaster.setFromCamera(mouse, camera);
        const intersects = raycaster.intersectObjects(hotspots, false);

        if (intersects.length > 0) {
            let dot_count = $(".dot").length;
            let text_count = $(".textField").length;
            const numDiv = document.createElement('div');
            numDiv.className = 'dot';
            numDiv.id = `div_${dot_count}`;
            numDiv.textContent = `${dot_count + 1}`;
            numDiv.style.marginTop = '-1em';
            const Label = new CSS2DObject(numDiv);
            Label.position.set(intersects[0].point.x, intersects[0].point.y, 0.5);
            scene.add(Label);
            const textField = document.createElement('textarea');
            textField.className = 'textField';
            textField.id = `textField_${text_count}`
            const textFieldLabel = new CSS2DObject(textField);
            textFieldLabel.position.set(intersects[0].point.x, intersects[0].point.y, 0.5);
            scene.add(textFieldLabel);
            document.body.appendChild(numDiv);
            document.body.appendChild(textField)

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

function animate() {
    requestAnimationFrame(animate);
    controls.update();
    render();
    stats.update();
}

// function to render on a screen
function render() {
   
    renderer.render(scene, camera);
    renderer.render(scene1, camera);
    
    labelRenderer.render(scene, camera);
   }


Above is the whole code where is the issue is it in rendering or any parameter changed?

Please describe as concise as possible, how you want the label to behave instead.

Hint: Imagine you’re hiring a developer to implement this label and you’re writing into the contract what you expect the result to be like. Obviously the developer expects the agreed upon pay once he delivers exactly as you specified - nothing more and nothing less.

Sorry, for that. I just need where mouse click happens label and text field stick to that point only if we perform panning and rotating model, they will stick there

You are clicking a 2D pixel position on screen, but are referring to a 3D position of your mesh. The 3D position which you intended to click is likely off-center wrt. the axis of rotation. It’s likely to become hidden during rotation.

Mixing of 2D and 3D effects is more complicated than you seem to realize.

then how I can change this can you guide me I need to add hotspot when user click on GLB model

Maybe label move because you didnt click up because rotating scene or something else

click up means in which way?

Clicking a mouse button is inherently a 2D-operation and results in 2D pixel coordinates.
What you intend to do is, to identify a 3D-mesh. Raycasting can bridge between the two. See this example.

Once you’ve identified the mesh, …

… you’re dealing with its dimensions (length, width, height). So which 3D-point of the mesh to attach the label to? A natural choice would be (a fixed [pixel] distance from) the center coordinates of the mesh’s bounding box. See this example of the application of the CSS2DRenderer, which yields consistent results even during rotation and/or panning.

I already saw this examples main thing I wanna know like I am creating div and textarea and then add both into the scene is this good approach?

Sorry, that’s beyond my experience.

you ever worked on how we can add hotspots using raycaster?

If raycast object, then add 3d Label with coordinates of model, not intersection coordinates.

That’s the point i didn’t able to understand can you provide some examples or any other code snippet, i also checked model viewer repo but didn’t able to understand

dist_14.zip (34.4 KB)

image

bro it gives me an one error of havelabel does not exist of Object3DEvent and my output is shown like this if I comment two lines related to havelabel

and my code is below

import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import Stats from 'three/examples/jsm/libs/stats.module'
import { GUI } from 'dat.gui'
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader'
import * as validator from 'gltf-validator';
import { HorizontalBlurShader } from 'three/examples/jsm/shaders/HorizontalBlurShader';
import { VerticalBlurShader } from 'three/examples/jsm/shaders/VerticalBlurShader';
import SpriteText from 'three-spritetext';
import { BackSide, LoaderUtils, Matrix3, MeshBasicMaterial, TextureLoader, WebGLRenderer } from 'three'
import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader'
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader'
import { SimpleDropzone } from 'simple-dropzone';
import { CSS2DRenderer, CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer.js';
import { setupCanvas, clearCanvas, editHotspot } from 'hotspot-js';
import * as d3 from "d3";
// import * as CSM from 'three-csm';
import { CSM } from 'three/examples/jsm/csm/CSM'
import { CSMHelper } from 'three/examples/jsm/csm/CSMHelper'



var can_click = true;


var renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

const labelRenderer = new CSS2DRenderer();
labelRenderer.setSize(window.innerWidth, window.innerHeight);
labelRenderer.domElement.style.position = 'absolute';
labelRenderer.domElement.style.top = '0px';
document.body.appendChild(labelRenderer.domElement);


var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 200);
camera.position.set(10, 5, 20);
camera.layers.enableAll();
camera.layers.toggle(1);


const controls = new OrbitControls(camera, labelRenderer.domElement);
controls.minDistance = 5;
controls.maxDistance = 100;


const scene = new THREE.Scene();
const pl1 = new THREE.PointLight(0xFEE3B1, 2);
pl1.position.set(-20, 20, 20);
scene.add(pl1);
const clock = new THREE.Clock();


var vs = [];
var fs = [];
var mesh = [];
var tex = [];
var mat = [];
var bend = [];


mesh["bbb_1"] = new THREE.Mesh(new THREE.BoxBufferGeometry(2, 1, 2), new THREE.MeshStandardMaterial({ color: 0xff0000 }));
mesh["bbb_1"].position.set(0, 0, -4);
mesh["bbb_1"].haveLabel = false;
scene.add(mesh["bbb_1"]);

mesh["bbb_1"].layers.enableAll();


mesh["bbb_2"] = new THREE.Mesh(new THREE.BoxBufferGeometry(2, 1, 2), new THREE.MeshStandardMaterial({ color: 0xff0000 }));
mesh["bbb_2"].position.set(8, 0, -4);
mesh["bbb_1"].haveLabel = false;
scene.add(mesh["bbb_2"]);


mesh["bbb_2"].layers.enableAll();


var hotspots = [mesh["bbb_1"], mesh["bbb_2"]];
var text_count = 0;


const raycaster = new THREE.Raycaster();
const pointer = new THREE.Vector2();


document.addEventListener("mousedown", onDocumentMouseDown, false);


function aaa() {


    if (can_click) { can_click = false; }
    else { can_click = true; }


}


function onDocumentMouseDown(event) {
    console.log();
    if (!can_click) { return; }

    pointer.x = (event.clientX / renderer.domElement.clientWidth) * 2 - 1;
    pointer.y = - (event.clientY / renderer.domElement.clientHeight) * 2 + 1;
    raycaster.setFromCamera(pointer, camera);

    const intersects = raycaster.intersectObjects(hotspots);
    console.log(intersects);
    

    if (intersects.length > 0) {
        //if(intersects[0].object.haveLabel==true){ return; }
        text_count++;
        var textField = document.createElement('textarea');
        textField.className = 'textFieldNew';
        textField.id = "textField_" + text_count;
        textField.addEventListener("mousedown", aaa, false);
        var textFieldLabel = new CSS2DObject(textField);
        textFieldLabel.position.set(intersects[0].object.position.x, intersects[0].object.position.y, intersects[0].object.position.z);
        scene.add(textFieldLabel);
        //intersects[0].object.haveLabel=true;


    }


}




function loop() {


    requestAnimationFrame(loop);

    controls.update()
    renderer.render(scene, camera);
    labelRenderer.render(scene, camera);

}


loop();

where is the issue?

Maybe bad browser

I created one thing can you check recent post I uploaded

In your code only added imports and mine code. Textarea offset maybe because some imported code do this offset or browser

check this link how it can be solve?