tino415
tino415

Reputation: 109

Line disappear when camera move in three js

I'm trying to render d3 force graph in three.js, I'm using standard Line and BoxGeometry with photo texture on it. On force graph update I call draw function, which is also called on in

controls.addEventListener('change', () => {this.redraw()});

but when I move camera, some lines disappear, when I'm closer, it seems worst, besides it does not look like there is any rule, even when I'm close to graph, it look like lines to disappear are chosen at random.

There line is

enter image description here

And this is if I move camera a little in angle

enter image description here

This is from one side of the graph:

enter image description here

And this is when from other:

enter image description here

I tried to scale down units

And also frustum = false

Whole code:

import {
    WebGLRenderer,
    Scene,
    PerspectiveCamera,
    Texture,
    MeshBasicMaterial,
    SphereGeometry,
    Mesh,
    Geometry,
    Vector3,
    LineBasicMaterial,
    Line,
    LineSegments,
    BoxGeometry,
    TextureLoader
} from 'three';

import * as three from 'three';

import { ViewModel, Link, Node } from './Model';
import { event } from 'd3-selection';
import * as selection from 'd3-selection';
import { drag } from 'd3-drag';

// Old module syntax
declare function require(name:String);

let OrbitControls = require('./../../../node_modules/three-orbit-controls/index')(three);

interface IView {
    render():void;
}

class ViewNode {
    public vector:Vector3;
    public mesh:Mesh;
    public node:Node;
}

export class Full3DView implements IView {
    private canvas: Element;

    private renderer: WebGLRenderer;

    private scene: Scene;

    private lineMaterial: LineBasicMaterial;

    private camera: PerspectiveCamera;

    private controls: any;

    private nodes:ViewNode[] = [];

    private lines:Geometry[] = [];

    constructor(private model:ViewModel) {
        this.canvas = document.querySelector('#view3d2');
        this.model.onChange(() => {this.render()});
    }

    render(): void {
        this.buildScene();
        this.model.simulation.on('tick', () => this.redraw());
        this.model.linkForce.distance(40);
        this.model.collideForce.radius(30);
    }

    private buildScene() {
        this.scene = new Scene();
        this.camera = new PerspectiveCamera( 90, window.innerWidth/window.innerHeight, 1, 20000 );

        this.renderer = new WebGLRenderer();
        this.renderer.setSize( this.canvas.clientWidth, this.canvas.clientHeight );
        this.canvas.appendChild( this.renderer.domElement );

        this.controls = new OrbitControls( this.camera, this.renderer.domElement);
        this.controls.addEventListener('change', () => {this.redraw()});

        this.lineMaterial = new LineBasicMaterial({ color: 0xccff00, linewidth: 3});

        let vectorIndex:Map<String, Vector3> = new Map();
        let textureLoader = new TextureLoader();

        this.model.nodes.forEach((node:Node) => {
            this.buildNode(vectorIndex, textureLoader, node);
        });

        this.model.links.forEach((link:Link) => {
            this.buildEdge(vectorIndex, link);
        });

        this.camera.position.z = 5000;
    }

    private buildNode(vectorIndex:Map<String, Vector3>, textureLoader:TextureLoader, node:Node) {
        let material = new MeshBasicMaterial();
        let geometry = new BoxGeometry( 30, 30, 30);
        let mesh = new Mesh( geometry, material );

        mesh.lookAt(this.camera.position);

        this.scene.add( mesh );

        mesh.position.set(node.x, node.y, 0);

        mesh.rotation.x += 1;

        vectorIndex.set(node.index, mesh.position);

        this.nodes.push({
            vector: mesh.position,
            mesh: mesh,
            node: node
        });

        textureLoader.load('/data/images/' + node.id + '.jpg', (texture:Texture) => {
            material.map = texture;
            material.needsUpdate = true;
        });
    }

    private buildEdge(vectorIndex:Map<String, Vector3>, link:Link) {
        let geometry = new Geometry();

        geometry.vertices.push(
       vectorIndex.get(link.source.index).copy(vectorIndex.get(link.source.index).setZ(0)),
            vectorIndex.get(link.target.index).copy(vectorIndex.get(link.target.index).setZ(0))
        );

        geometry.computeLineDistances();

        this.lines.push(geometry);

        let line = new Line(geometry, this.lineMaterial);
        this.scene.add(line);
    }

    private redraw() {
        this.nodes.forEach((node:ViewNode) => {
            node.vector.setX(node.node.x * 10);
            node.vector.setY(node.node.y * 10);
            node.mesh.lookAt(this.camera.position);
            node.mesh.frustumCulled = false;
        });

        this.lines.forEach((line:Geometry) => {
            line.verticesNeedUpdate = true;
        });

        this.renderer.render(this.scene, this.camera)
    }
}

Upvotes: 0

Views: 2125

Answers (2)

Rhys van der Waerden
Rhys van der Waerden

Reputation: 3857

The actual answer I was looking for is in the comment above from WestLangley.

A possible explanation for your issue is that when you update the vertices of a geometry, you should call geometry.computeBoundingSphere(). The renderer calls it for you on the first render call, but after that, if you modify vertices, the bounding sphere is no longer correct, and you need to update it. Alternatively, you can set mesh.frustumCulled = false;

Upvotes: 7

tino415
tino415

Reputation: 109

I was unable to get it working using Line object, but if I use LineSegments and push all pairs of vertices to one Geometry, it is working well.

So in function buildScene I use instead of

this.lineMaterial = new LineBasicMaterial({ color: 0xccff00, linewidth: 3});

lines

this.linesGeometry = new Geometry();
this.scene.add(new LineSegments(this.linesGeometry, new LineBasicMaterial({ color: 0xccff00, linewidth: 3})));

and then content of buildEdge is

this.linesGeometry.vertices.push(
    vectorIndex.get(link.source.index).copy(vectorIndex.get(link.source.index).setZ(0)),
    vectorIndex.get(link.target.index).copy(vectorIndex.get(link.target.index).setZ(0))
);

and in redraw function I just do

this.linesGeometry.verticesNeedUpdate = true;

instead of

this.lines.forEach((line:Geometry) => {
    line.verticesNeedUpdate = true;
});

Upvotes: 1

Related Questions