import { World } from "./world.js";
import { VRSPACE } from "../client/vrspace.js";
import { VRSPACEUI } from "../ui/vrspace-ui.js";
/**
* Helper class, provides mesh visibility methods.
*/
export class VisibilityHelper {
constructor(world = World.lastInstance) {
this.world = world;
this.scene = world.scene;
}
dispose() {
}
/**
* Returns true if target mesh is visible, performs least expensive checks first, then more expensive ones.
* It's supposed to test multiple points of the mesh, tests only one for the time being.
* Calls this.isClosestMesh to determine visibility.
* @param target babylonjs mesh to test for visibility
* @param {number} [confidence=1] minimum number of points required to be visible
* @param {Vector3} [offset=new BABYLON.Vector3(0,0,0)] offset to add to mesh position before testing
*/
isVisible( target, confidence=1, offset=new BABYLON.Vector3(0,0,0) ) {
//console.log("isVisible "+target.name+" confidence "+confidence+" offset "+offset);
let ret = 0;
let camera = this.scene.activeCamera;
if (camera.isInFrustum(target)) {
// center
if (this.isClosestMesh(camera, target, target.position.add(offset))) {
ret++;
}
if ( ret < confidence ) {
// bounding box - slow
let bBox = target.getHierarchyBoundingVectors();
let height = bBox.max.y - bBox.min.y;
let points = [
// TODO additional points to check
// we could use positions of head, shoulders, arms, legs etc here
// at the moment, just approximate the center
new BABYLON.Vector3(target.position.x, target.position.y+height*.5, target.position.z)
]
for ( let i = 0; ret<confidence && i < points.length; i++ ) {
if (this.isClosestMesh(camera, target, points[i])) {
ret++;
}
}
}
}
return ret>=confidence;
}
/**
* Casts a ray from the camera to the point, and returns true if the mesh is hit.
* Mesh may be root node of the scene, or any mesh in the scene.
*/
isClosestMesh(camera, mesh, point) {
let direction = point.subtract(camera.position).normalize();
let ray = new BABYLON.Ray(camera.position,direction,camera.maxZ);
let closest = this.scene.pickWithRay(ray, (test) => test.isVisible);
//if ( closest && closest.hit ) {
//console.log("Picked "+closest.pickedMesh.name+" root "+VRSPACEUI.findRootNode(closest.pickedMesh).name+ " at "+point+" distance "+closest.distance);
//}
return closest && closest.hit && (closest.pickedMesh === mesh || mesh === VRSPACEUI.findRootNode(closest.pickedMesh));
}
/**
* Traverses VRObject scene, and returns visible avatars.
*/
getVisibleAvatars(confidence=1) {
let ret = [];
for ( let vrObject of VRSPACE.getScene().values() ) {
if ( typeof vrObject.avatar != "undefined" && this.isVisible(vrObject.avatar.baseMesh(), confidence, new BABYLON.Vector3(0,vrObject.avatar.userHeight*.5,0))) {
//console.log( "Visible: "+vrObject.id+" "+vrObject.avatar.name);
ret.push(vrObject.avatar);
}
}
return ret;
}
/**
* Traverses scene root nodes, and returns visible objects.
*/
getVisibleObjects(confidence=1) {
let ret = [];
this.scene.rootNodes.forEach( (node) => {
if ( typeof node.vrObject != "undefined" && this.isVisible(node, confidence)) {
ret.push(node);
}
});
return ret;
}
/** Traverses give babyolonjs node array, and returns visible nodes */
getVisibleOf(nodeArray, confidence=1) {
let ret = [];
nodeArray.forEach( (node) => {
if ( this.isVisible(node, confidence)) {
ret.push(node);
}
});
return ret;
}
/** Traverses given User array, and returns array of visible User objects */
getVisibleUsers(userArray, confidence=1) {
let ret = [];
userArray.forEach( (user) => {
if ( typeof user.avatar != "undefined" && this.isVisible(user.avatar.baseMesh(), confidence)) {
ret.push(user);
}
});
return ret;
}
}