move 3d clicking and touching for placing from renderer into controls
This commit is contained in:
parent
e74b3f923b
commit
1768dd88bf
|
@ -27,6 +27,13 @@ import {
|
|||
THREE_CANVAS_HEIGHT,
|
||||
VIEW_UPDATE_DELAY,
|
||||
} from '../core/constants';
|
||||
import {
|
||||
getDiff,
|
||||
getTapOrClickCenter,
|
||||
} from '../core/utils';
|
||||
import {
|
||||
selectHoverColor,
|
||||
} from '../store/actions';
|
||||
|
||||
const STORE_UPDATE_DELAY = VIEW_UPDATE_DELAY / 2;
|
||||
// Mouse buttons
|
||||
|
@ -126,6 +133,14 @@ class VoxelPainterControls {
|
|||
vec = new Vector3();
|
||||
// forcing next update
|
||||
forceNextUpdate = false;
|
||||
// start of touch or click
|
||||
clickTapStartTime = 0;
|
||||
// on touch: true if current tab was ever more than one figher at any time
|
||||
wasEverMultiTap = false;
|
||||
// screen coords of where a tap/click started
|
||||
clickTapStartCoords = [0, 0];
|
||||
// on touch: timeout to detect long-press
|
||||
tapTimeout = null;
|
||||
|
||||
constructor(renderer, camera, target, domElement, store) {
|
||||
this.renderer = renderer;
|
||||
|
@ -276,9 +291,6 @@ class VoxelPainterControls {
|
|||
panStart.copy(panEnd);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
handleMouseUp() {}
|
||||
|
||||
handleMouseWheel(event) {
|
||||
const scaleDelta = 0.95 ** zoomSpeed;
|
||||
if (event.deltaY < 0) {
|
||||
|
@ -394,8 +406,102 @@ class VoxelPainterControls {
|
|||
if (enableRotate) this.handleTouchMoveRotate(event);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
handleTouchEnd() {}
|
||||
placeVoxelOnScreen(screenCoords, allowedDistance) {
|
||||
const intersect = this.renderer.castRay(screenCoords);
|
||||
if (!intersect) {
|
||||
return;
|
||||
}
|
||||
const target = intersect.point.clone()
|
||||
.add(intersect.face.normal.multiplyScalar(0.5))
|
||||
.floor()
|
||||
.addScalar(0.5)
|
||||
.floor();
|
||||
if (target.clone().sub(this.camera.position).length() < allowedDistance) {
|
||||
const [x, y, z] = target.toArray();
|
||||
this.renderer.placeVoxel(x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
deleteVoxelOnScreen(screenCoords, allowedDistance) {
|
||||
const intersect = this.renderer.castRay(screenCoords);
|
||||
if (!intersect) {
|
||||
return;
|
||||
}
|
||||
const target = intersect.point.clone()
|
||||
.add(intersect.face.normal.multiplyScalar(-0.5))
|
||||
.floor()
|
||||
.addScalar(0.5)
|
||||
.floor();
|
||||
if (target.y < 0) {
|
||||
return;
|
||||
}
|
||||
if (target.clone().sub(this.camera.position).length() < allowedDistance) {
|
||||
const [x, y, z] = target.toArray();
|
||||
this.renderer.placeVoxel(x, y, z, 0);
|
||||
}
|
||||
}
|
||||
|
||||
selectColorOnScreen(screenCoords) {
|
||||
// selectHoverColor doesn't actually do anything with the coords
|
||||
this.store.dispatch(selectHoverColor(screenCoords));
|
||||
}
|
||||
|
||||
handleMouseUp(event) {
|
||||
if (!this.clickTapStartTime
|
||||
|| Date.now() - this.clickTapStartTime > 300
|
||||
|| this.store.getState().fetching.fetchingPixel
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const screenCoords = getTapOrClickCenter(event);
|
||||
if (getDiff(screenCoords, this.clickTapStartCoords) > 6) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.button) {
|
||||
case 0:
|
||||
// left
|
||||
this.placeVoxelOnScreen(screenCoords, 120);
|
||||
break;
|
||||
case 1:
|
||||
// middle
|
||||
this.selectColorOnScreen(screenCoords);
|
||||
break;
|
||||
case 2:
|
||||
// right
|
||||
this.deleteVoxelOnScreen(screenCoords, 120);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
handleTouchEnd(event) {
|
||||
if (event.touches.length
|
||||
|| !this.clickTapStartTime
|
||||
|| Date.now() - this.clickTapStartTime > 300
|
||||
|| this.store.getState().fetching.fetchingPixel
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const screenCoords = getTapOrClickCenter(event);
|
||||
if (getDiff(screenCoords, this.clickTapStartCoords) > 6) {
|
||||
return;
|
||||
}
|
||||
this.placeVoxelOnScreen(screenCoords, 90);
|
||||
}
|
||||
|
||||
onLongTap(event) {
|
||||
if (!this.clickTapStartTime
|
||||
|| this.store.getState().fetching.fetchingPixel
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const screenCoords = getTapOrClickCenter(event);
|
||||
if (getDiff(screenCoords, this.clickTapStartCoords) > 6) {
|
||||
return;
|
||||
}
|
||||
this.deleteVoxelOnScreen(screenCoords, 90);
|
||||
}
|
||||
|
||||
onMouseMove(event) {
|
||||
event.preventDefault();
|
||||
|
@ -417,6 +523,7 @@ class VoxelPainterControls {
|
|||
|
||||
onMouseUp(event) {
|
||||
this.handleMouseUp(event);
|
||||
this.clickTapStartTime = 0;
|
||||
document.removeEventListener('mousemove', this.onMouseMove, false);
|
||||
document.removeEventListener('mouseup', this.onMouseUp, false);
|
||||
this.state = STATE.NONE;
|
||||
|
@ -439,6 +546,17 @@ class VoxelPainterControls {
|
|||
onTouchStart(event) {
|
||||
event.preventDefault();
|
||||
|
||||
if (event.touches.length === 1) {
|
||||
this.clickTapStartTime = Date.now();
|
||||
this.clickTapStartCoords = getTapOrClickCenter(event);
|
||||
this.tapTimeout = setTimeout(() => {
|
||||
this.onLongTap(event);
|
||||
}, 600);
|
||||
} else {
|
||||
this.clickTapStartTime = 0;
|
||||
clearTimeout(this.tapTimeout);
|
||||
}
|
||||
|
||||
switch (event.touches.length) {
|
||||
case 1:
|
||||
switch (TOUCHES.ONE) {
|
||||
|
@ -489,6 +607,12 @@ class VoxelPainterControls {
|
|||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const screenCoords = getTapOrClickCenter(event);
|
||||
if (getDiff(screenCoords, this.clickTapStartCoords) > 6) {
|
||||
clearTimeout(this.tapTimeout);
|
||||
this.clickTapStartTime = 0;
|
||||
}
|
||||
|
||||
switch (this.state) {
|
||||
case STATE.TOUCH_ROTATE:
|
||||
if (!enableRotate) {
|
||||
|
@ -520,7 +644,12 @@ class VoxelPainterControls {
|
|||
}
|
||||
|
||||
onTouchEnd(event) {
|
||||
event.preventDefault();
|
||||
if (!event.touches.length) {
|
||||
clearTimeout(this.tapTimeout);
|
||||
this.handleTouchEnd(event);
|
||||
this.clickTapStartTime = 0;
|
||||
}
|
||||
this.state = STATE.NONE;
|
||||
}
|
||||
|
||||
|
@ -532,6 +661,8 @@ class VoxelPainterControls {
|
|||
onMouseDown(event) {
|
||||
// Prevent the browser from scrolling.
|
||||
event.preventDefault();
|
||||
this.clickTapStartTime = Date.now();
|
||||
this.clickTapStartCoords = getTapOrClickCenter(event);
|
||||
|
||||
// Manually set the focus since calling preventDefault above
|
||||
// prevents the browser from setting it automatically.
|
||||
|
|
|
@ -636,6 +636,20 @@ export function getDateKeyOfTs(ts) {
|
|||
return `${year}${month}${day}`;
|
||||
}
|
||||
|
||||
/*
|
||||
* get largest distance between axes of array
|
||||
*/
|
||||
export function getDiff(arr1, arr2) {
|
||||
let largest = 0;
|
||||
for (let i = 0; i < arr1.length; i += 1) {
|
||||
const length = Math.abs(arr1[i] - arr2[i]);
|
||||
if (length > largest) {
|
||||
largest = length;
|
||||
}
|
||||
}
|
||||
return largest;
|
||||
}
|
||||
|
||||
/*
|
||||
* get screen coords of touch / mouse event
|
||||
* @param event MouseEvent or TouchEvent
|
||||
|
|
|
@ -409,6 +409,7 @@ class Renderer2D extends Renderer {
|
|||
context.fillRect(x, y, TILE_SIZE, TILE_SIZE);
|
||||
} else {
|
||||
chunk = this.chunkLoader.getChunk(tiledZoom, cx, cy);
|
||||
// TODO is chunk nnow an object or image? it doesnt make any sense
|
||||
if (chunk) {
|
||||
context.drawImage(chunk, x, y);
|
||||
if (touch) {
|
||||
|
|
|
@ -28,7 +28,6 @@ import ChunkLoader from './ChunkLoader3D';
|
|||
import {
|
||||
getChunkOfPixel,
|
||||
getOffsetOfPixel,
|
||||
getTapOrClickCenter,
|
||||
} from '../core/utils';
|
||||
import {
|
||||
THREE_TILE_SIZE,
|
||||
|
@ -36,7 +35,6 @@ import {
|
|||
import {
|
||||
setHover,
|
||||
unsetHover,
|
||||
selectHoverColor,
|
||||
} from '../store/actions';
|
||||
import pixelTransferController from './PixelTransferController';
|
||||
|
||||
|
@ -64,14 +62,7 @@ class Renderer3D extends Renderer {
|
|||
// temp variables for mouse events
|
||||
mouse = new Vector2();
|
||||
raycaster = new Raycaster();
|
||||
pressTime;
|
||||
pressCdTime;
|
||||
multitap = 0;
|
||||
lastIntersect = 0;
|
||||
// on touch: true if current tab was ever more than one figher at any time
|
||||
wasEverMultiTap = false;
|
||||
// screen coords of where a tap/click started
|
||||
clickTapStartCoords = [0, 0];
|
||||
|
||||
constructor(store) {
|
||||
super(store);
|
||||
|
@ -194,26 +185,9 @@ class Renderer3D extends Renderer {
|
|||
|
||||
this.onDocumentMouseMove = this.onDocumentMouseMove.bind(this);
|
||||
this.onDocumentTouchMove = this.onDocumentTouchMove.bind(this);
|
||||
// eslint-disable-next-line max-len
|
||||
this.onDocumentMouseDownOrTouchStart = this.onDocumentMouseDownOrTouchStart.bind(this);
|
||||
this.onDocumentMouseUp = this.onDocumentMouseUp.bind(this);
|
||||
this.onWindowResize = this.onWindowResize.bind(this);
|
||||
this.onDocumentTouchEnd = this.onDocumentTouchEnd.bind(this);
|
||||
this.multiTapEnd = this.multiTapEnd.bind(this);
|
||||
domElement.addEventListener('mousemove', this.onDocumentMouseMove, false);
|
||||
domElement.addEventListener('touchmove', this.onDocumentTouchMove, false);
|
||||
domElement.addEventListener(
|
||||
'mousedown',
|
||||
this.onDocumentMouseDownOrTouchStart,
|
||||
false,
|
||||
);
|
||||
domElement.addEventListener(
|
||||
'touchstart',
|
||||
this.onDocumentMouseDownOrTouchStart,
|
||||
false,
|
||||
);
|
||||
domElement.addEventListener('touchend', this.onDocumentTouchEnd, false);
|
||||
domElement.addEventListener('mouseup', this.onDocumentMouseUp, false);
|
||||
window.addEventListener('resize', this.onWindowResize, false);
|
||||
|
||||
this.updateCanvasData(state);
|
||||
|
@ -511,25 +485,34 @@ class Renderer3D extends Renderer {
|
|||
}
|
||||
|
||||
onDocumentMouseMove(event) {
|
||||
event.preventDefault();
|
||||
|
||||
this.updateRollOverMesh(
|
||||
(event.clientX / window.innerWidth) * 2 - 1,
|
||||
-(event.clientY / window.innerHeight) * 2 + 1,
|
||||
);
|
||||
}
|
||||
|
||||
onDocumentMouseDownOrTouchStart(event) {
|
||||
this.pressTime = Date.now();
|
||||
this.clickTapStartCoords = getTapOrClickCenter(event);
|
||||
this.wasEverMultiTap = (event.touches?.length > 1);
|
||||
onDocumentTouchMove(event) {
|
||||
if (this.rollOverMesh.position.y !== -10) {
|
||||
console.log('unset hover');
|
||||
this.store.dispatch(unsetHover());
|
||||
this.rollOverMesh.position.y = -10;
|
||||
}
|
||||
}
|
||||
|
||||
onDocumentTouchMove(event) {
|
||||
if (event.touches?.length > 1) {
|
||||
this.wasEverMultiTap = true;
|
||||
castRay([clientX, clientY]) {
|
||||
const {
|
||||
mouse, camera, raycaster, objects,
|
||||
} = this;
|
||||
mouse.set(
|
||||
(clientX / window.innerWidth) * 2 - 1,
|
||||
-(clientY / window.innerHeight) * 2 + 1,
|
||||
);
|
||||
raycaster.setFromCamera(mouse, camera);
|
||||
const intersects = raycaster.intersectObjects(objects);
|
||||
if (intersects.length > 0) {
|
||||
return intersects[0];
|
||||
}
|
||||
this.updateRollOverMesh(0, 0);
|
||||
return null;
|
||||
}
|
||||
|
||||
getPointedColor() {
|
||||
|
@ -540,7 +523,6 @@ class Renderer3D extends Renderer {
|
|||
camera,
|
||||
} = this;
|
||||
raycaster.setFromCamera(mouse, camera);
|
||||
|
||||
const intersects = raycaster.intersectObjects(objects);
|
||||
if (intersects.length <= 0) {
|
||||
return null;
|
||||
|
@ -586,188 +568,6 @@ class Renderer3D extends Renderer {
|
|||
curColor,
|
||||
);
|
||||
}
|
||||
|
||||
multiTapEnd(event) {
|
||||
const {
|
||||
store,
|
||||
multitap,
|
||||
} = this;
|
||||
this.multitap = 0;
|
||||
const state = store.getState();
|
||||
|
||||
if (!state.canvas.hover || this.wasEverMultiTap) {
|
||||
return;
|
||||
}
|
||||
const [clientX, clientY] = getTapOrClickCenter(event);
|
||||
const { clickTapStartCoords } = this;
|
||||
const coordsDiff = [
|
||||
clickTapStartCoords[0] - clientX,
|
||||
clickTapStartCoords[1] - clientY,
|
||||
].map(Math.abs);
|
||||
if (coordsDiff[0] > 5 || coordsDiff[1] > 5) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (multitap) {
|
||||
case 1: {
|
||||
// single tap
|
||||
// Place Voxel
|
||||
if (this.rollOverMesh.position.y < 0) {
|
||||
return;
|
||||
}
|
||||
this.placeVoxel(...state.canvas.hover);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
// double tap
|
||||
// Remove Voxel
|
||||
const {
|
||||
mouse,
|
||||
raycaster,
|
||||
camera,
|
||||
objects,
|
||||
} = this;
|
||||
mouse.set(0, 0);
|
||||
raycaster.setFromCamera(mouse, camera);
|
||||
const intersects = raycaster.intersectObjects(objects);
|
||||
if (intersects.length > 0) {
|
||||
const intersect = intersects[0];
|
||||
const target = intersect.point.clone()
|
||||
.add(intersect.face.normal.multiplyScalar(-0.5))
|
||||
.floor()
|
||||
.addScalar(0.5)
|
||||
.floor();
|
||||
if (target.y < 0) {
|
||||
return;
|
||||
}
|
||||
if (target.clone().sub(camera.position).length() <= 50) {
|
||||
const [x, y, z] = target.toArray();
|
||||
this.placeVoxel(x, y, z, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
onDocumentTouchEnd(event) {
|
||||
event.preventDefault();
|
||||
if (event.touches.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const curTime = Date.now();
|
||||
if (curTime - this.pressTime > 600) {
|
||||
this.multitap = 0;
|
||||
return;
|
||||
}
|
||||
// if we want to do something with triple tap,
|
||||
// we should reset on every tap
|
||||
// but we don't need that right now...
|
||||
if (this.multitap === 0) {
|
||||
setTimeout(() => this.multiTapEnd(event), 500);
|
||||
}
|
||||
this.multitap += 1;
|
||||
}
|
||||
|
||||
onDocumentMouseUp(event) {
|
||||
const curTime = Date.now();
|
||||
if (curTime - this.pressCdTime < 200) {
|
||||
return;
|
||||
}
|
||||
if (curTime - this.pressTime > 500) {
|
||||
this.pressCdTime = curTime;
|
||||
return;
|
||||
}
|
||||
|
||||
const state = this.store.getState();
|
||||
const {
|
||||
isOnMobile,
|
||||
} = state.user;
|
||||
const {
|
||||
fetchingPixel,
|
||||
} = state.fetching;
|
||||
if (fetchingPixel || isOnMobile) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!state.canvas.hover) {
|
||||
return;
|
||||
}
|
||||
const [clientX, clientY] = getTapOrClickCenter(event);
|
||||
const { clickTapStartCoords } = this;
|
||||
const coordsDiff = [
|
||||
clickTapStartCoords[0] - clientX,
|
||||
clickTapStartCoords[1] - clientY,
|
||||
].map(Math.abs);
|
||||
if (coordsDiff[0] > 5 || coordsDiff[1] > 5) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
const { button } = event;
|
||||
const {
|
||||
innerWidth,
|
||||
innerHeight,
|
||||
} = window;
|
||||
const {
|
||||
store,
|
||||
mouse,
|
||||
} = this;
|
||||
|
||||
mouse.set(
|
||||
(clientX / innerWidth) * 2 - 1,
|
||||
-(clientY / innerHeight) * 2 + 1,
|
||||
);
|
||||
|
||||
if (button === 1) {
|
||||
// middle mouse button
|
||||
store.dispatch(selectHoverColor());
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
camera,
|
||||
objects,
|
||||
raycaster,
|
||||
} = this;
|
||||
|
||||
raycaster.setFromCamera(mouse, camera);
|
||||
|
||||
const intersects = raycaster.intersectObjects(objects);
|
||||
if (intersects.length > 0) {
|
||||
const intersect = intersects[0];
|
||||
|
||||
if (button === 0) {
|
||||
// left mouse button
|
||||
const target = intersect.point.clone()
|
||||
.add(intersect.face.normal.multiplyScalar(0.5))
|
||||
.floor()
|
||||
.addScalar(0.5)
|
||||
.floor();
|
||||
if (target.clone().sub(camera.position).length() < 120) {
|
||||
const [x, y, z] = target.toArray();
|
||||
this.placeVoxel(x, y, z);
|
||||
}
|
||||
} else if (button === 2) {
|
||||
// right mouse button
|
||||
const target = intersect.point.clone()
|
||||
.add(intersect.face.normal.multiplyScalar(-0.5))
|
||||
.floor()
|
||||
.addScalar(0.5)
|
||||
.floor();
|
||||
if (target.y < 0) {
|
||||
return;
|
||||
}
|
||||
if (target.clone().sub(camera.position).length() < 120) {
|
||||
const [x, y, z] = target.toArray();
|
||||
this.placeVoxel(x, y, z, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Renderer3D;
|
||||
|
|
Loading…
Reference in New Issue
Block a user