import { View } from './view.js';
import { Marker } from './marker.js';
import { Project } from '../project.js';
import { Timer } from "../../src/timer.js";
import { Scene, sRGBEncoding, LinearEncoding, LinearFilter, Vector3, WebGLRenderer, PCFSoftShadowMap, Euler, VSMShadowMap, LinearToneMapping, CineonToneMapping, RGBAFormat, FloatType, Raycaster, Vector2, Plane, UnsignedByteType, PMREMGenerator, Mesh, Quaternion, DirectionalLight, BoxGeometry, MeshBasicMaterial } from '../../node_modules/three/build/three.module.js';
import { Pass } from '../../node_modules/three/examples/jsm/postprocessing/Pass.js';
import { Reporter } from '../reporter/reporter.js';
import { checkPropTypes, UUIDRegex } from '../lib.js';
import { ContactShadow } from '../scene/contact_shadow.js'
import { ShaderPass } from '../../node_modules/three/examples/jsm/postprocessing/ShaderPass.js';
//import { GammaCorrectionShader } from '../../node_modules/three/examples/jsm/shaders/GammaCorrectionShader.js';
import { GammaCorrectionShader } from '../../test/resources/js/GammaCorrectionShader.js';
import { FXAAShader } from '../../node_modules/three/examples/jsm/shaders/FXAAShader.js';
import { CSS2DRenderer } from '../../node_modules/three/examples/jsm/renderers/CSS2DRenderer.js';
import { EXRLoader } from '../../node_modules/three/examples/jsm/loaders/EXRLoader.js';
import { KeyboardShortcuts } from '../../src/utilities/keyboardShortcuts.js';
import { EXR } from '../package/image/exr.js';
import { Component } from '../component/component.js';
import { Block } from '../package/block/block.js';
import { WrappedMaterial } from '../package/material/wrapped_material.js';
import { BlockInstance } from '../configurator/block_instance.js';
import { Configuration } from '../configurator/configuration.js';
import { Configurator } from '../configurator/configurator.js';
import { Actor } from '../actor/actor.js';
const fxaaShader = new ShaderPass(FXAAShader);
const gammaCorrectionShader = new ShaderPass(GammaCorrectionShader);
/**
* Default View
* @extends View
*/
class MoooiView extends View {
/**
* @param {Reporter} reporter
* @param {Object} settings
* @param {Scene} settings.scene
* @param {WebGLRenderer} settings.renderer
* @param {WebGLRenderer} settings.rendererOrtho
* @param {Timer} settings.timer
* @param {Object<string,any>} [settings.cameraSettings]
* @param {Array<Pass>} [settings.passes]
* @param {Function} [settings.findConfigurator]
* @param {Function} [settings.addConfigurator]
* @param {Function} [settings.findComponentById]
*/
constructor(reporter, settings) {
super(
reporter,
{
// scene: settings.scene,
// renderer: settings.renderer,
// rendererOrtho: settings.rendererOrtho,
// timer: settings.timer,
//findConfigurator: settings.findConfigurator,
//addConfigurator: settings.addConfigurator
...settings,
rendererSettings: {
'antialias': false,
'alpha': true,
'preserveDrawingBuffer': true,
'precision': "highp",
'premultipliedAlpha': true,
'stencil': false,
'logarithmicDepthBuffer': false,
'toneMapping': CineonToneMapping, //LinearToneMapping, //ReinhardToneMapping, //THREE.NoToneMapping //; // Met NoToneMapping werkt de exposure niet! //CineonToneMapping, //THREE.ACESFilmicToneMapping //THREE.
'toneMappingExposure': 1,
'toneMappingWhitePoint': 1,
'gammaFactor': 2.2,
'outputEncoding': CineonToneMapping, //sRGBEncoding, // LinearEncoding, // //When using post-processing or in general when rendering to a render target, WebGLRenderer.outputEncoding is not evaluated.
'physicallyCorrectLights': true,
'minFilter': LinearFilter,
'magFilter': LinearFilter,
'format': RGBAFormat,
'stencilBuffer': false,
'type': FloatType, // line not present in original code
'shadowMap.enabled': true
},
cameraSettings: {
fov: 30,
aspect: window.innerWidth / window.innerHeight,
near: 0.5,
far: 100,
maxPolarAngle: 90
},
passes: [
fxaaShader, //deze pass moet altijd als laatste komen (met uitzondering van de gamma correction shader) zodat de anti aliasing het beste werkt
gammaCorrectionShader
],
}
);
checkPropTypes(
settings,
{
scene: Scene,
renderer: WebGLRenderer,
timer: Timer
},
{
rendererSettings: Object,
cameraSettings: Object,
passes: val => {
if (!Array.isArray(val)) {
return 'Not an array';
}
for (let i = 0; i < val.length; i += 1) {
const entry = val[i];
if (!entry instanceof Pass) {
return `Entry ${i} is not a Pass but a ${entry.constructor.name}`;
}
}
return true;
}
}
);
//Settings / Variables
this.dragObject = null;
this.dragging = false;
this.allMeshes = [];
this.layerMap = Project.layerMap
this.onMouseDownActive = false;
this.shift = new Vector3();
this.intersects = new Vector3();
this.exrBackground = null;
//Composer Passes
this.pixelRatio = this.renderer.getPixelRatio();
//set the dimensions where needed
this.on(
'dimensionsupdate',
newDimensions => {
fxaaShader.uniforms.resolution.value.x = 1 / (newDimensions.width * this.pixelRatio);
fxaaShader.uniforms.resolution.value.y = 1 / (newDimensions.height * this.pixelRatio);
}
);
fxaaShader.name = "FXAA Pass";
gammaCorrectionShader.name = "Gamma Correction Pass";
//renderer
this.renderer.setClearColor(0xFFFFFF, 1); //deze is nodig om de FXAA goed te laten werken
this.renderer.shadowMap.enabled = true;
this.renderer.shadowMap.type = PCFSoftShadowMap; //VSMShadowMap;
this.renderer.shadowMap.needsUpdate = true
this.renderer.antialias = false,
this.renderer.gammaFactor = 1.8,
this.renderer.physicallyCorrectLights = true,
this.renderer.toneMapping = 4,
this.renderer.toneMappingExposure = 0.5,
this.renderer.logarithmicDepthBuffer = true
//Camera
this.perspectiveCamera.position.set(-2, 1, 5);
//this.perspectiveCamera.layers.disable(2);
//this.perspectiveCamera.layers.disable(3);
//Raycaster
//this.raycaster = new Raycaster(); //er zit al een raycaster in view
//front light
this.directionalLight = new DirectionalLight(0xffffff, 3);
this.directionalLight.position.set(-40, 65, 50);
this.directionalLight.shadow.mapSize.width = 1024; // default
this.directionalLight.shadow.mapSize.height = 1024; // default
this.directionalLight.shadow.camera.near = 0.5; // default
this.directionalLight.shadow.camera.far = 500; // default
this.directionalLight.shadow.bias = 0.0001 //deze setting is onder andere nodig om te zorgen dat faces smoothing hebben bij een normal mmaptoepassing
//this.directionalLight.shadowRadius = 2.5
//shadow intensity workaround -> https://github.com/mrdoob/three.js/pull/14087
const shadowIntensity = 2.2; // between 0 and 1
this.directionalLight2 = this.directionalLight.clone();
this.directionalLight.castShadow = true;
this.directionalLight2.castShadow = false;
this.directionalLight.intensity = shadowIntensity;
this.directionalLight2.intensity = 4 - shadowIntensity;
this.directionalLight2.shadow.bias = 0.0001 //deze setting is onder andere nodig om te zorgen dat faces smoothing hebben bij een normal mmaptoepassing
//fill light
this.directionalLightLeft = this.directionalLight.clone();
this.directionalLightLeft.position.set(50, 50, 50);
this.directionalLightLeft.castShadow = true;
this.directionalLightLeft.intensity = 1.8;
this.directionalLightLeft.shadow.bias = 0.0001
//back light
this.directionalLightBack = this.directionalLight.clone();
this.directionalLightBack.position.set(0, 50, -50);
this.directionalLightBack.castShadow = false;
this.directionalLightBack.intensity = 1.5;
this.directionalLightBack.shadow.bias = 0.0001
this.scene.add(this.directionalLight)
this.scene.add(this.directionalLight2)
this.scene.add(this.directionalLightBack)
this.scene.add(this.directionalLightLeft)
//Mouse
this.mouse = new Vector2();
//Mouse Intersection Plane
this.intersectionPlane = new Plane();
this.intersectionPlane.setFromCoplanarPoints(new Vector3(), new Vector3(1, 0, 0), new Vector3(0, 0, 1))
this.intersectionPlaneNormal = new Vector3(0, 1, 0);
this.pointOfIntersection = new Vector3();
//Drag Variables
this.dragPlane = new Plane(); // een plane om een intersectie mee te kunnen maken voor de drag functionaliteit in de scene
this.dragPlane.setFromCoplanarPoints(new Vector3(), new Vector3(1, 0, 0), new Vector3(0, 0, 1))
this.dragPlaneNormal = new Vector3(0, 1, 0);
this.shift = new Vector3();
this.dragging = false;
this.dragObject = null;
this.pointOfIntersection = new Vector3();
this.onPointerDownActive = false;
//Keyboard Shortcuts
// KeyboardShortcuts(
// this.scene,
// this.perspectiveCamera,
// this.renderer,
// this.shadow,
// this.dragObject,
// this.allMeshes,
// this.onSceneUpdate()
// )
//const marker = new Marker( reporter, { scene:this.scene } );
//marker.addMarker();
}
//Event Handlers
/**
* Touch events
*/
onTouchStart(event){
//super.onTouchStart()
// console.log( 'touch start')
// console.log( event.touches[0].clientX )
// console.log( event.touches[0].clientY )
if (this.dragState.active) {
//do nothing
}
else {
this.onPointerDownActive = false;
this.onTouchStartActive = true;
//?
this.lastPointerMove = {
x: event.touches[0].clientX,
y: event.touches[0].clientY,
timestamp: new Date().getTime()
};
//check of block
var data = this.findFirstIntersectedBlockInstance() || {};
console.log( data )
data.event = event;
this.emit('pointerdown', data);
//get point of intersection
const intersections = this.intersect(this.lastPointerMove.x, this.lastPointerMove.y);
console.log( intersections )
//if block then
if (data.blockInstance instanceof BlockInstance) {
this.selectionActive = true;
// console.log('selection activated')
this.cameraControls.enabled = false; //disable cameraControls
const selectedConfigurator = this.findConfigurator(data.blockInstance.tree) //get the configurator from the data
//this.showBoundingBoxes( selectedConfigurator.configuration ) //show bbox for reference
//set the draggable object
this.dragObject = selectedConfigurator //._content.main.medium
//set the dragPlane on the first intersection point
this.dragPlane.setFromNormalAndCoplanarPoint(this.dragPlaneNormal, intersections[0].point)
//set a shift of the object versus the dragPlane
this.shift.subVectors(this.dragObject.body.position, intersections[0].point); //sets this vector to a - b
}
else {
this.dragObject = null;
this.hideBoundingBoxes()
this.selectionActive = false;
this.cameraControls.enabled = true; //disable cameraControls
}
this.update()
}
}
onTouchStart(event){
// if (this.dragState.active) {
// //do nothing
// }
// else {
this.onPointerDownActive = false;
this.onTouchStartActive = true;
this.lastPointerMove = {
x: event.touches[0].clientX,
y: event.touches[0].clientY,
timestamp: new Date().getTime()
};
//check of block
var data = this.findFirstIntersectedBlockInstance() || {};
data.event = event;
this.emit('pointerdown', data);
//get point of intersection
const intersections = this.intersect(this.lastPointerDown.x, this.lastPointerDown.y);
// //if block then
// if (data.blockInstance instanceof BlockInstance) {
// this.selectionActive = true;
// console.log('selection activated')
// this.cameraControls.enabled = false; //disable cameraControls
// const selectedConfigurator = this.findConfigurator(data.blockInstance.tree) //get the configurator from the data
// //this.showBoundingBoxes( selectedConfigurator.configuration ) //show bbox for reference
// //set the draggable object
// this.dragObject = selectedConfigurator //._content.main.medium
// //set the dragPlane on the first intersection point
// this.dragPlane.setFromNormalAndCoplanarPoint(this.dragPlaneNormal, intersections[0].point)
// //set a shift of the object versus the dragPlane
// this.shift.subVectors(this.dragObject.body.position, intersections[0].point); //sets this vector to a - b
// }
// else {
// this.dragObject = null;
// this.hideBoundingBoxes()
// this.selectionActive = false;
// this.cameraControls.enabled = true; //disable cameraControls
// }
// this.update()
// }
}
onTouchMove(event){
console.log( 'touch move')
if (this.dragState.active) {
//do nothing
}
else {
this.lastPointerMove.x = event.touches[0].clientX;
this.lastPointerMove.y = event.touches[0].clientY;
if (this.domElementDimensions === undefined) {
this.updateDOMElementDimensions() //<- deze hier tijdelijk neergzet, anders heeft this.intersect een error - deze kan waarschijnlijk beter op een andere plaats worden aangeroepen als de app opstart
}
//calculate the intersection based on the mouse position
this.intersect(this.lastPointerMove.x, this.lastPointerMove.y)
//check if mouse pointer is down
if (this.onTouchStartActive) {
//check if dragObject is set
if (this.dragObject instanceof Configurator) {
//console.log( 'dragObject = configuration' )
if (this.hideShadowOnDrag === false) {
//console.log( ' hide shadow' )
//update the shadow without the selected configuration (exclude)
this.shadow._update([this.dragObject.body]);
this.hideShadowOnDrag = true
}
//get the point of intersection
this.rayCaster.ray.intersectPlane(this.dragPlane, this.pointOfIntersection);
//set the position of the dragobject based on the point of intersection
this.dragObject.body.position.copy(this.pointOfIntersection).add(this.shift); //<- de add shift nog ff netjes oplossen. Weet niet of dit de beste plek er voor is.
this.dragObject.body.position.setY(0) //force y = 0
this.update()
}
}
else {
this.hideShadowOnDrag = false
}
}
}
onTouchEnd(event){
console.log( 'touch end')
if (this.dragState.active) {
//do nothing
}
else {
this.hideShadowOnDrag = false
this.onPointerDownActive = false;
this.onTouchStartActive = false;
this.cameraControls.enabled = true; //re-enable cameraControls from down event
this.lastPointerUp = {
x: this.lastPointerMove.x,
y: this.lastPointerMove.y,
timestamp: new Date().getTime()
};
//set the end position of the configrator/configuration
if (this.dragObject instanceof Configurator) {
this.selectionActive = true;
const configurator = this.dragObject
configurator.position.copy(this.dragObject.body.position)
configurator.position.setY(0) //force y axis = 0
}
else {
// console.log('selection de-activated')
this.selectionActive = false;
this.dragObject = null;
}
this.dragObject = null;
console.log( this.dragObject )
this.shadow._update();
this.update()
}
}
//dit event is nog experimental!!
onTouchCancel(event){
console.log( 'touch cancel')
this.onTouchCancelActive = true;
}
/**
* Finds the block 'under' the current (last known) mouse coordinates
* and emits an event with that data, if anyone is listening
* @fires View#click
*/
onClick(event) {
console.log('click')
this.dragState.active = false;
this.onPointerDownActive = false;
//this.selectionActive = false;
super.onClick()
}
onDoubleClick(event) {
console.log('double click')
//control the state
this.dragState.active = false;
this.onPointerDownActive = false;
//this.selectionActive = false;
super.onDoubleClick()
}
//selectionActive = false; //deze wordt gebruikt om te bepalen of de selectie actief is of niet
/**
* Stores the pointer down coordinates and timestamp.
* Finds the block 'under' the current (last known) mouse coordinates
* and emits an event with that data, if anyone is listening
* @fires View#pointerdown
*/
onPointerDown(event) {
// if (this.dragState.active) {
// //do nothing
// }
// else {
// this.onPointerDownActive = true;
// this.lastPointerDown = {
// x: this.lastPointerMove.x,
// y: this.lastPointerMove.y,
// timestamp: new Date().getTime()
// };
//check of block
var data = this.findFirstIntersectedBlockInstance() || {};
data.event = event;
this.emit('pointerdown', data);
// //get point of intersection
// const intersections = this.intersect(this.lastPointerDown.x, this.lastPointerDown.y);
// //if block then
// if (data.blockInstance instanceof BlockInstance) {
// this.selectionActive = true;
// console.log('selection activated')
// this.cameraControls.enabled = false; //disable cameraControls
// const selectedConfigurator = this.findConfigurator(data.blockInstance.tree) //get the configurator from the data
// //this.showBoundingBoxes( selectedConfigurator.configuration ) //show bbox for reference
// //set the draggable object
// this.dragObject = selectedConfigurator //._content.main.medium
// //set the dragPlane on the first intersection point
// this.dragPlane.setFromNormalAndCoplanarPoint(this.dragPlaneNormal, intersections[0].point)
// //set a shift of the object versus the dragPlane
// this.shift.subVectors(this.dragObject.body.position, intersections[0].point); //sets this vector to a - b
// }
// else {
// this.dragObject = null;
// this.hideBoundingBoxes()
// this.selectionActive = false;
// this.cameraControls.enabled = true; //disable cameraControls
// }
// this.update()
// }
}
//hideShadowOnDrag = false; //deze wordt bij pointer move 1x op tur gezet zodat de shaduw uitgezet kan wordeen tijdens het onPointerMove event
onPointerMove(event) {
if (this.dragState.active) {
//do nothing
}
else {
this.lastPointerMove.x = event.clientX;
this.lastPointerMove.y = event.clientY;
if (this.domElementDimensions === undefined) {
this.updateDOMElementDimensions() //<- deze hier tijdelijk neergzet, anders heeft this.intersect een error - deze kan waarschijnlijk beter op een andere plaats worden aangeroepen als de app opstart
}
//calculate the intersection based on the mouse position
this.intersect(this.lastPointerMove.x, this.lastPointerMove.y)
//check if mouse pointer is down
if (this.onPointerDownActive) {
//check if dragObject is set
if (this.dragObject instanceof Configurator) {
//console.log( 'dragObject = configuration' )
if (this.hideShadowOnDrag === false) {
//console.log( ' hide shadow' )
//update the shadow without the selected configuration (exclude)
this.shadow._update([this.dragObject.body]);
this.hideShadowOnDrag = true
}
//get the point of intersection
this.rayCaster.ray.intersectPlane(this.dragPlane, this.pointOfIntersection);
//set the position of the dragobject based on the point of intersection
this.dragObject.body.position.copy(this.pointOfIntersection).add(this.shift); //<- de add shift nog ff netjes oplossen. Weet niet of dit de beste plek er voor is.
this.dragObject.body.position.setY(0) //force y = 0
this.update()
}
}
else {
this.hideShadowOnDrag = false
}
}
}
/**
* Stores the pointer up coordinates and timestamp.
* If the mouse hasnt move (a lot) since mouse down, it find the block
* 'under' the last known mouse coordinates and emits an event
* with that data, if anyone is listening
* @fires View#fastclick
*/
onPointerUp(event) {
if (this.dragState.active) {
//do nothing
}
else {
this.hideShadowOnDrag = false
this.onPointerDownActive = false;
this.cameraControls.enabled = true; //re-enable cameraControls from down event
this.lastPointerUp = {
x: this.lastPointerMove.x,
y: this.lastPointerMove.y,
timestamp: new Date().getTime()
};
// if (Object.keys(this._events['fastclick']).length > 0) {
// // check whether the distance between pointer down and up
// // is sufficiently small to be seen as a 'click' event
// if (
// Math.pow(
// (this.lastPointerDown.x - this.lastPointerUp.x) * (this.lastPointerDown.x - this.lastPointerUp.x) + (this.lastPointerDown.y - this.lastPointerUp.y) * (this.lastPointerDown.y - this.lastPointerUp.y),
// 0.5
// ) < 2
// ) {
// const data = this.findFirstIntersectedBlockInstance() || {};
// data.event = event;
// this.emit('fastclick', data);
// }
// }
//set the end position of the configrator/configuration
if (this.dragObject instanceof Configurator) {
this.selectionActive = true;
const configurator = this.dragObject
configurator.position.copy(this.dragObject.body.position)
configurator.position.setY(0) //force y axis = 0
}
else {
console.log('selection de-activated')
this.selectionActive = false;
this.dragObject = null;
}
this.shadow._update();
this.update()
}
}
/**
* All the different states to track and control the status of the drag events.
* These are needed to prevent conflicts with click / dblclick events (maybe )
* @type {Object}
*/
dragState = {
_active: false,
get active() {
return this._active
},
set active(state) {
console.log(`Setting dragState.active [${this._active}] => [${state}]`);
this._active = state;
},
_enter: false,
get enter() {
return this._enter
},
set enter(state) {
console.log(`Setting dragState.enter [${this._enter}] => [${state}]`);
this._enter = state;
},
_leave: false,
get leave() {
return this._leave
},
set leave(state) {
console.log(`Setting dragState.leave [${this._leave}] => [${state}]`);
this._leave = state;
},
_over: false,
get over() {
return this._over
},
set over(state) {
console.log(`Setting dragState.over [${this._over}] => [${state}]`);
this._over = state;
},
}
/** @type {Object} */
expectedComponentDrag = null;
/**
* @param {Component} component
* @returns {Promise<Configuration>}
*/
expectComponentDrag(component) {
this.dragState.active = true
this.dragState.leave = false
this.dragState.over = false;
console.assert(component instanceof Component, 'expectComponentDrag -> entered component needs to be an instance of the class Component!')
//check if true then reject current expectation
if (this.expectedComponentDrag) {
console.warn('Another component drag is still being expected... cancelling that one');
this.expectedComponentDrag.reject('A newer component expectation has made this one obsolete');
}
let resolve, reject = null;
const promise = new Promise((res, rej) => { resolve = res; reject = rej; });
// clean up expectedComponentDrag when done
promise
.catch(err => {
console.error('Error in expected component drag chain:', err)
})
.finally(() => {
console.log('Drag promise fulfilled');
this.expectedComponentDrag = null;
//this.dragObject = null;
});
// set expectation
this.expectedComponentDrag = { component, promise, resolve, reject };
console.log(this.expectedComponentDrag)
const view = this;
//our patience is limited
if (this.expectedComponentDrag) {
this.expectedComponentDrag.timeout = setTimeout(
function () {
console.log('Reject expected drag after timeout');
view.expectedComponentDrag.reject('Timeout');
},
2000
);
};
return promise;
}
checkDrag(event) {
//return event.dataTransfer.types.contains("application/x-moz-file");
}
/** @type {Array} */
matOptions = []
/**
* Can receive data in the form of a coomponent.
* Evaluates the data and makes distinction between a wrapped material or a block.
* @fires View#dragenter
*/
async onDragEnter(event) {
event.preventDefault();
this.hideDimensions()
if (this.dragState.active) {
if (this.dragState.leave) {
console.log('drag re-enter');
this.lastPointerMove.x = event.clientX;
this.lastPointerMove.y = event.clientY;
this.dragState.leave = false;
this.dragState.over = false;
}
else {
this.dragState.enter = true;
this.dragState.leave = false;
this.dragState.over = false;
console.log('drag enter')
this.onPointerDownActive === true;
this.cameraControls.enabled = false; //disable cameraControls
this.lastPointerMove.x = event.clientX;
this.lastPointerMove.y = event.clientY;
const component = this.expectedComponentDrag.component
if (component) {
component.build()
clearTimeout(this.expectedComponentDrag.timeout);
switch (component.constructor) {
case WrappedMaterial:
console.log("drag enter wrapped material")
const material = component
this.dragObject = material
break;
case Block:
console.log("drag enter block")
//Make new configuration/configurator
const configuration = new Configuration(new Reporter(), { pkg: component.tree, blockInstances: [new BlockInstance(new Reporter(), { block: component })] })
await configuration.build()
const newConfigurator = await this.addConfigurator({ pkg: component.tree, configuration });
this.dragObject = newConfigurator //set the draggable object
this.expectedComponentDrag.createdConfigurator = newConfigurator;
this.expectedComponentDrag.promise.catch(err => {
// deze fn bestaat nog niet
//Project.removeConfigurator(newConfigurator);
throw new Error(err);
});
//show bbox for reference
//this.showBoundingBoxes( configuration )
//get the inetrsection
const intersections = this.intersect(this.lastPointerMove.x, this.lastPointerMove.y);
//this.intersect(this.lastPointerMove.x, this.lastPointerMove.y);
this.dragPlane.setFromNormalAndCoplanarPoint(this.dragPlaneNormal, intersections[0].point) //set the dragPlane on the first intersection point
//this.shift.subVectors( this.dragObject.content.main.medium.position, intersections[0].point ); //set a shift of the object versus the dragPlane
//set a shift of the object versus the dragPlane
this.shift.subVectors(this.dragObject.body.position, intersections[0].point); //sets this vector to a - b
//get the point of intersection
this.rayCaster.ray.intersectPlane(this.dragPlane, this.pointOfIntersection);
console.log(this.pointOfIntersection)
//set the position of the dragobject based on the point of intersection
this.dragObject.body.position.copy(this.pointOfIntersection)//.add( this.shift );
this.dragObject.body.position.setY(0)
console.log(this.dragObject.body)
this.scene.add(this.dragObject.body)
//update the scene with the new position
this.shadow._update([this.dragObject.body]);
this.update()
break;
default:
this.expectedComponentDrag.reject(`Unknown onDragEnter expected component constructor ${this.expectedComponentDrag.component.constructor.name}`);
}
}
}
}
else {
console.warn(`DragEnter event received, but dragState = `, this.dragState)
}
}
/**
* Finds the block 'under' the current (last known) mouse coordinates
* when a drag is happening and emits an event with that data and the
* original dragover event
* @fires View#dragover
*/
async onDragOver(event) {
if (this.dragState.active) {
console.log('drag over')
event.preventDefault();
this.lastPointerMove.x = event.clientX;
this.lastPointerMove.y = event.clientY;
//set the drag state
if (this.dragState.over === false) {
//await this.dragObject.configuration.build()
if (this.dragState.enter) { //<- hier moet nog een betere oplossing voor komen
console.log('disable shadow')
console.log(this.dragObject.body)
this.shadow._update([this.dragObject.body]);
}
this.update()
this.dragState.over = true;
//this.dragState.enter = false;
}
//get the point of intersection
this.intersect(this.lastPointerMove.x, this.lastPointerMove.y);
this.rayCaster.ray.intersectPlane(this.dragPlane, this.pointOfIntersection);
//set the position of the dragobject based on the point of intersection
if (this.dragObject instanceof Configurator) {
this.dragObject.body.position.copy(this.pointOfIntersection)//.add( this.shift );
this.dragObject.body.position.setY(0)
}
this.update()
}
}
/**
* Finds the block 'under' the drop location
* and emits an event with that data and the drop event
* @fires View#drop
*/
async onDrop(event) {
if (this.dragState.active) {
console.log('drop')
event.preventDefault();
const dataTransfer = event.dataTransfer.getData("text"); //hiermee nog een check uitvoeren op de ID van het object?
this.onPointerDownActive = false;
this.cameraControls.enabled = true; //re-enable cameraControls from down event
if (this.dragObject) {
switch (this.dragObject.constructor) {
case WrappedMaterial:
console.log("drop wrapped material")
const data = this.findFirstIntersectedBlockInstance() || {};
data.event = event;
const configurator = this.findConfigurator(data.blockInstance.tree)
this.matOptions = configurator.configuration.options.material(this.dragObject); // check the possible material options
console.log(this.matOptions)
const assignable = data.intersection?.object?.userData?.PB?.assignable;
let selectedMatOption = null;
if (assignable) {
selectedMatOption = this.matOptions.find(opt => opt.assignable === assignable);
if (selectedMatOption) {
this.domElement.style.cursor = "cell";
}
else {
this.domElement.style.cursor = "no-drop";
}
}
else {
this.domElement.style.cursor = "no-drop";
}
const newConfiguration = configurator.configuration.assignMaterial(selectedMatOption);
await configurator.update(newConfiguration);
this.expectedComponentDrag.resolve(newConfiguration);
break;
case Configurator:
console.log('drop configurator')
//get the point of intersection and update
this.intersect(this.lastPointerMove.x, this.lastPointerMove.y);
this.rayCaster.ray.intersectPlane(this.dragPlane, this.pointOfIntersection);
this.dragObject.body.position.copy(this.pointOfIntersection)//.add( this.shift ); //set the position of the dragobject based on the point of intersection
//get & update the configurator position
this.dragObject.position.copy(this.dragObject.body.position) // in this case the dragObject is the configurator
this.dragObject.position.setY(0) // force y = 0
this.expectedComponentDrag.resolve(this.expectedComponentDrag.createdConfigurator.configuration);
break;
default:
this.expectedComponentDrag.reject(`Unknown onDrop expected component constructor ${this.expectedComponentDrag.component.constructor.name}`);
break;
}
}
//update the scene
this.shadow._update()
this.update()
this.dragState.active = false;
this.dragState.enter = false;
this.dragState.leave = false;
this.dragState.over = false;
this.selectionActive = true;
}
}
onDragLeave(event) {
if (this.dragState.active) {
event.preventDefault();
console.log('drag leave')
if (this.dragState.enter) {
this.dragState.enter = false;
this.dragState.leave = true;
this.dragState.over = false;
}
this.update()
}
}
onDragEnd() {
if (this.dragState.active) {
console.log('drag end')
this.cameraControls.enabled = true; //re-enable cameraControls from down event
if (this.dragObject) {
this.selectionActive = true;
}
else {
this.selectionActive = false;
this.dragObject = null;
}
if (this.dragState.leave) {
this.expectedComponentDrag.reject('The drag event has left the window with a drop event');
}
//reset drag states
this.dragState.active = false;
this.dragState.enter = false;
this.dragState.over = false;
this.dragState.leave = false;
//update scene
this.shadow._update();
this.update()
}
}
// onComponentDragEnter(){
// console.log( 'component drag enter' )
// // https://developer.mozilla.org/en-US/docs/Web/CSS/cursor
// this.domElement.style.cursor = "crosshair";
// const data = this.findFirstIntersectedBlockInstance() || {};
// data.event = event;
// const assignable = data.intersection?.object?.userData?.PB?.assignable;
// if ( assignable ) {
// const selectedMatOption = matOptions.find(opt => opt.assignable === assignable);
// if ( selectedMatOption) {
// this.domElement.style.cursor = "cell";
// }
// else {
// this.domElement.style.cursor = "no-drop";
// }
// }
// else {
// this.domElement.style.cursor = "no-drop";
// }
// }
onComponentDragLeave(event) {
if (Object.keys(listenerObject).length > 0) {
//voorlopig uitgezetomdat dit conflicteert met de drag functies
// this.domElement.addEventListener(
// 'dragover',
// viewBoundDragEmitter
// );
}
else {
// this.domElement.removeEventListener(
// 'dragover',
// viewBoundDragEmitter
// );
}
}
onKeyUp(event) {
let key = event.which || event.keyCode; // both these properties are deprecated...
// //Rotate selectin -> Shift C
if (event.shiftKey && key === 67) {
console.log('rotate selection')
if (this.dragObject) {
console.log('rotate selection 2')
this.rotateSelection(this.dragObject)
}
}
if ( key === 67 ) {
console.log('connectors')
this.toggleConnectorHelpers()
}
}
rotateSelection(selection) {
if (selection instanceof Configurator) {
const configurator = selection
configurator.move(configurator.body.position, configurator.quaternion.multiply(new Quaternion().setFromEuler(new Euler(0, Math.PI / 2, 0))));
this.shadow._update()
this.update()
}
else if (selection instanceof Block ) {
const configurator = this.findConfigurator( selection.tree)
configurator.move(configurator.body.position, configurator.quaternion.multiply(new Quaternion().setFromEuler(new Euler(0, Math.PI / 2, 0))));
this.shadow._update()
this.update()
}
}
//Update Functions
update() {
super.update();
}
//This function is called after a static update of the scene
onSceneUpdate(...args) {
if (this.dragState.active) {
//do nothing
}
else {
this.shadow._update();
}
super.onSceneUpdate(...args);
}
toggleConnectorHelpers(){
console.log('connector helpers')
console.log( this )
this.scene.traverse( function(child) {
if( child.name === 'Actor' ){
console.log( child.userData.origin.configuration.connectors )
for( let connector of child.userData.origin.configuration.connectors ){
const position = connector.connector._settings.position
console.log( position )
const box = new BoxGeometry(0.1, 0.1, 0.1)
const mat = new MeshBasicMaterial( )
const mesh = new Mesh( box, mat )
mesh.position.copy(position)
scene.add(mesh)
}
}
})
// const connectorGroup = new Group()
// connectorGroup.name = "connectors"
// const dir = new Vector3(0, 0, 0.1);
// dir.normalize(); //normalize the direction vector (convert to vector of length 1)
// const origin = new Vector3(0, 0, 0); // origin of the arrowHelper (not able to set oafter creation -> use position instead)
// const length = 0.15; // length of the arrowHelper
// const hex = 0xff0000; // color of the arrowHelper
// const headLength = 0.05 // The length of the head of the arrow. Default is 0.2 * length.
// const headWidth = 0.05 // The width of the head of the arrow. Default is 0.2 * headLength.
// const arrowHelper = new ArrowHelper(dir, origin, length, hex, 0.05, 0.05); // arrowhelper template
// //HELPER - ARROW -> place helpers in block group
// for (var i = 0; i < this.settings.connectors.length; i++) {
// //get the connector data
// //console.log( this.settings)
// const conn = this.settings.connectors[i].content.main[quality] //get the connector
// //console.log( conn )
// const newDir = new Vector3(0, 0, 2); //create new vector, deze moet groter zijn dan 1! zit anders een bug in dat die de vector niet negatief kan roteren!
// newDir.applyQuaternion(conn.quaternion) //apply connector rotation to vector
// //console.log( newDir )
// const newArrowHelper = arrowHelper.clone() //create copy of the arrow helper template
// newArrowHelper.position.copy(conn.position); //copy the position of the connector to the arrowhelper
// newArrowHelper.setDirection(newDir)
// //newArrowHelper.layers.set( layerMap.connectorHelpers )
// connectorGroup.add(newArrowHelper)
// //connectorGroup.layers.set( layerMap.connectorHelpers )
// //block.add(connectorGroup)
// }
}
};
export { MoooiView };