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
And this is if I move camera a little in angle
This is from one side of the graph:
And this is when from other:
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)
}
}