From 539c5120e7bcbafebbb6cfc611ea3604d9e1e3fc Mon Sep 17 00:00:00 2001 From: HF Date: Sun, 21 Jan 2024 02:41:09 +0100 Subject: [PATCH] make view a protected member load view from store in child class renderers instead of parent --- src/controls/PixelPainterControls.js | 8 +-- src/controls/VoxelPainterControls.js | 23 ++++---- src/ui/Renderer.js | 21 +++++--- src/ui/Renderer2D.js | 50 +++++++----------- src/ui/Renderer3D.js | 79 +++++++++++++++++----------- 5 files changed, 95 insertions(+), 86 deletions(-) diff --git a/src/controls/PixelPainterControls.js b/src/controls/PixelPainterControls.js index b7686174..cba9fcec 100644 --- a/src/controls/PixelPainterControls.js +++ b/src/controls/PixelPainterControls.js @@ -107,7 +107,7 @@ class PixelPainterControls { const { clientX, clientY } = event; this.clickTapStartTime = Date.now(); this.clickTapStartCoords = [clientX, clientY]; - this.clickTapStartView = [...this.renderer.view]; + this.clickTapStartView = this.renderer.view; const { viewport } = this; setTimeout(() => { if (this.isClicking) { @@ -254,7 +254,7 @@ class PixelPainterControls { this.renderer.cancelStoreViewInState(); this.clickTapStartTime = Date.now(); this.clickTapStartCoords = PixelPainterControls.getTouchCenter(event); - this.clickTapStartView = [...this.renderer.view]; + this.clickTapStartView = this.renderer.view; if (event.touches.length > 1) { this.tapStartDist = PixelPainterControls.getMultiTouchDistance(event); @@ -321,7 +321,7 @@ class PixelPainterControls { // if one finger got lifted or added, reset clickTabStart this.isMultiTab = multiTouch; this.clickTapStartCoords = [clientX, clientY]; - this.clickTapStartView = [...this.renderer.view]; + this.clickTapStartView = this.renderer.view; this.tapStartDist = PixelPainterControls.getMultiTouchDistance(event); } else { // pan @@ -333,7 +333,7 @@ class PixelPainterControls { if (deltaX > 2 || deltaY > 2) { this.clearTabTimeout(); } - const { viewscale: scale } = this.renderer.view; + const { viewscale: scale } = this.renderer; this.renderer.updateView([ lastPosX - (deltaX / scale), lastPosY - (deltaY / scale), diff --git a/src/controls/VoxelPainterControls.js b/src/controls/VoxelPainterControls.js index 70b1c47b..8e6d170a 100644 --- a/src/controls/VoxelPainterControls.js +++ b/src/controls/VoxelPainterControls.js @@ -25,8 +25,10 @@ import { } from 'three'; import { THREE_CANVAS_HEIGHT, + VIEW_UPDATE_DELAY, } from '../core/constants'; +const STORE_UPDATE_DELAY = VIEW_UPDATE_DELAY / 2; // Mouse buttons const MOUSE_BUTTONS = { LEFT: MOUSE.ROTATE, @@ -127,7 +129,7 @@ class VoxelPainterControls { // temp for update quat; quatInverse; - updateTime = Date.now(); + storeViewInStateTime = Date.now(); prevTime = Date.now(); offset = new Vector3(); direction = new Vector3(); @@ -176,8 +178,6 @@ class VoxelPainterControls { this.quat = new Quaternion() .setFromUnitVectors(camera.up, new Vector3(0, 1, 0)); this.quatInverse = this.quat.clone().invert(); - - this.update(); } dispose() { @@ -261,7 +261,6 @@ class VoxelPainterControls { this.rotateLeft(Math.PI * rotateDelta.x / element.clientHeight); // yes, height this.rotateUp(Math.PI * rotateDelta.y / element.clientHeight); this.rotateStart.copy(rotateEnd); - this.update(); } handleMouseMoveDolly(event) { @@ -280,7 +279,6 @@ class VoxelPainterControls { this.scale *= scaleDelta; } dollyStart.copy(this.dollyEnd); - this.update(); } handleMouseMovePan(event) { @@ -294,7 +292,6 @@ class VoxelPainterControls { panDelta.subVectors(panEnd, panStart).multiplyScalar(panSpeed); this.pan(panDelta.x, panDelta.y); panStart.copy(panEnd); - this.update(); } // eslint-disable-next-line class-methods-use-this @@ -593,28 +590,24 @@ class VoxelPainterControls { return; } this.handleTouchMoveRotate(event); - this.update(); break; case STATE.TOUCH_PAN: if (!enablePan) { return; } this.handleTouchMovePan(event); - this.update(); break; case STATE.TOUCH_DOLLY_PAN: if (!enableZoom && !enablePan) { return; } this.handleTouchMoveDollyPan(event); - this.update(); break; case STATE.TOUCH_DOLLY_ROTATE: if (!enableZoom && !enableRotate) { return; } this.handleTouchMoveDollyRotate(event); - this.update(); break; default: this.state = STATE.NONE; @@ -713,11 +706,10 @@ class VoxelPainterControls { this.camera.zoom = this.zoom0; this.camera.updateProjectionMatrix(); - this.update(); this.state = STATE.NONE; } - update() { + update(force) { const { moveRight, moveLeft, @@ -727,7 +719,8 @@ class VoxelPainterControls { moveBackward, } = this; - if (!(this.state !== STATE.NONE + if (!(force + || this.state !== STATE.NONE || this.forceNextUpdate || moveRight || moveLeft || moveUp || moveDown @@ -852,6 +845,10 @@ class VoxelPainterControls { panOffset.set(0, 0, 0); this.scale = 1; + if (this.storeViewInStateTime + STORE_UPDATE_DELAY < time) { + this.renderer.storeViewInState(); + } + return true; } } diff --git a/src/ui/Renderer.js b/src/ui/Renderer.js index 70a92588..37f1f51b 100644 --- a/src/ui/Renderer.js +++ b/src/ui/Renderer.js @@ -1,6 +1,9 @@ /* * parent class for Renderer */ + +/* eslint-disable no-underscore-dangle */ + import { VIEW_UPDATE_DELAY, } from '../core/constants'; @@ -23,17 +26,19 @@ class Renderer { // to "subrender" known view next tick forceNextRender = true; forceNextSubrender = true; - // current position (subclass decies what it means), - // will be changed by controls - // TODO might be better as private and with a getter but no setter to avoid - // misconseption of it being writeable. Might have performance impact. - view = [0, 0, 0]; + // current position + // third value can be scale (2d) or z axis (3d) + _view = [0, 0, 0]; // #storeViewTimeout = null; constructor(store) { this.store = store; - this.loadViewFromState(); + //this.loadViewFromState(); + } + + get view() { + return [...this._view]; } get chunks() { @@ -52,7 +57,7 @@ class Renderer { updateView(view) { for (let i = 0; i < view.length; i += 1) { - this.view[i] = view[i]; + this._view[i] = view[i]; } } @@ -83,7 +88,7 @@ class Renderer { } render() { - return this.controls.update(); + return this.controls.update(this.forceNextRender); } renderPixel() {} diff --git a/src/ui/Renderer2D.js b/src/ui/Renderer2D.js index fac58f03..5e3fd9fc 100644 --- a/src/ui/Renderer2D.js +++ b/src/ui/Renderer2D.js @@ -3,6 +3,8 @@ * */ +/* eslint-disable no-underscore-dangle */ + import { TILE_ZOOM_LEVEL, TILE_SIZE, @@ -29,40 +31,28 @@ import pixelNotify from './PixelNotify'; class Renderer2D extends Renderer { canvasId = null; - viewscale; + viewscale = 0; //-- - centerChunk; - tiledScale; - tiledZoom; - hover; + centerChunk = [null, null]; + tiledScale = 0; + tiledZoom = 4; + hover = false; //-- viewport = null; //-- - forceNextRender; - forceNextSubrender; + lastFetch = 0; canvas; - lastFetch; //-- - oldHistoricalTime; + oldHistoricalTime = null; constructor(store) { super(store); this.is3D = false; - [,, this.viewscale] = this.view; - this.centerChunk = [null, null]; - this.tiledScale = 0; - this.tiledZoom = 4; this.canvasMaxTiledZoom = 0; this.historicalCanvasMaxTiledZoom = 0; - this.hover = false; this.scaleThreshold = 1; //-- - this.forceNextRender = true; - this.forceNextSubrender = true; - this.lastFetch = 0; - this.oldHistoricalTime = null; - //-- const viewport = document.createElement('canvas'); viewport.className = 'viewport'; const viewportCtx = viewport.getContext('2d', { alpha: false }); @@ -124,6 +114,7 @@ class Renderer2D extends Renderer { canvasId, } = state.canvas; if (canvasId !== this.canvasId) { + // TODO doesn't immediatelly reload when change from 3d to 2d this.canvasId = canvasId; if (canvasId !== null) { const { @@ -141,9 +132,8 @@ class Renderer2D extends Renderer { canvases[canvasId].historicalSizes, ); } - // scale of 0 is impossible, so it always updates - this.view[2] = 0; this.updateView(state.canvas.view); + this.forceNextRender = true; } } @@ -202,7 +192,7 @@ class Renderer2D extends Renderer { this.viewscale = scale; } } else { - [,, scale] = this.view; + [,, scale] = this._view; } // clamp coords const canvasMinXY = -canvasSize / 2; @@ -253,7 +243,6 @@ class Renderer2D extends Renderer { } } - renderPixel( i, j, @@ -450,8 +439,8 @@ class Renderer2D extends Renderer { state, ) { const { + _view, viewport, - view, viewscale, } = this; const { @@ -468,7 +457,7 @@ class Renderer2D extends Renderer { hover, } = state.canvas; - const [x, y] = view; + const [x, y] = _view; const [cx, cy] = this.centerChunk; // if we have to render pixelnotify @@ -553,18 +542,18 @@ class Renderer2D extends Renderer { } if (showGrid && viewscale >= 8) { - renderGrid(state, viewport, view, viewscale, isLightGrid); + renderGrid(state, viewport, _view, viewscale, isLightGrid); } if (doRenderPixelnotify) { - pixelNotify.render(state, viewport, view, viewscale); + pixelNotify.render(state, viewport, _view, viewscale); } if (hover && doRenderPlaceholder) { - renderPlaceholder(state, viewport, view, viewscale); + renderPlaceholder(state, viewport, _view, viewscale); } if (hover && doRenderPotatoPlaceholder) { - renderPotatoPlaceholder(state, viewport, view, viewscale); + renderPotatoPlaceholder(state, viewport, _view, viewscale); } } @@ -704,7 +693,6 @@ class Renderer2D extends Renderer { ) { const { viewport, - view, viewscale, } = this; const { @@ -715,7 +703,7 @@ class Renderer2D extends Renderer { historicalCanvasSize, } = state.canvas; - const [x, y] = view; + const [x, y] = this._view; const [cx, cy] = this.centerChunk; if (!this.forceNextRender && !this.forceNextSubrender) { diff --git a/src/ui/Renderer3D.js b/src/ui/Renderer3D.js index e7c61ee6..925f4210 100644 --- a/src/ui/Renderer3D.js +++ b/src/ui/Renderer3D.js @@ -44,12 +44,17 @@ const renderDistance = 150; class Renderer3D extends Renderer { scene; camera; + // current chunk in center of view + centerChunk = [null, null]; // position camera is looking at target = new Vector3(); // red voxel cursor cube rollOverMesh; - objects; - loadedChunks; + // loaded objects in scene + objects = []; + // map of chunkId to chunk mesh + loadedChunks = new Map(); + // plane object plane; oobGeometry; oobMaterial; @@ -67,8 +72,8 @@ class Renderer3D extends Renderer { constructor(store) { super(store); this.is3D = true; + this.loadViewFromState(); const state = store.getState(); - this.objects = []; // camera const camera = new PerspectiveCamera( @@ -212,16 +217,14 @@ class Renderer3D extends Renderer { window.addEventListener('resize', this.onWindowResize, false); this.updateCanvasData(state); + // TODO REMOVE + window.renderer = this; } get view() { return this.target.toArray(); } - set view(view) { - this.target.set(...view); - } - destructor() { window.removeEventListener('resize', this.onWindowResize, false); this.threeRenderer.dispose(); @@ -236,7 +239,8 @@ class Renderer3D extends Renderer { if (view.length !== 3) { return; } - this.view = view; + console.log('go to', view); + this.target.set(...view); this.forceNextRender = true; } @@ -252,18 +256,18 @@ class Renderer3D extends Renderer { if (canvasId !== this.canvasId) { this.canvasId = canvasId; if (canvasId !== null) { - if (this.chunkLoader) { - // destroy old chunks, - // meshes need to get disposed - if (this.loadedChunks) { - this.loadedChunks.forEach((chunk) => { - this.scene.remove(chunk); - this.objects = [this.plane]; - }); - this.chunkLoader.destructor(); - } + // destroy old chunks, + // meshes need to get disposed + if (this.loadedChunks.length) { + this.loadedChunks.forEach((chunk) => { + this.scene.remove(chunk); + }); + this.loadedChunks = new Map(); } - this.loadedChunks = new Map(); + if (this.chunkLoader) { + this.chunkLoader.destructor(); + } + this.objects = [this.plane]; const { palette, canvasSize, @@ -275,9 +279,9 @@ class Renderer3D extends Renderer { canvasSize, ); } + this.updateView(view); + this.forceNextRender = true; } - this.updateView(view); - this.forceNextRender = true; } renderPixel( @@ -290,6 +294,7 @@ class Renderer3D extends Renderer { if (chunkLoader) { chunkLoader.getVoxelUpdate(i, j, offset, color); } + this.forceNextSubrender = true; } isChunkInView(yc, xc, zc) { @@ -304,18 +309,13 @@ class Renderer3D extends Renderer { if (!this.chunkLoader) { return; } - const state = this.store.getState(); - const { - canvasSize, - view, - } = state.canvas; - const x = view[0]; - const z = view[2] || 0; + const { canvasSize } = this.store.getState().canvas; const { scene, loadedChunks, chunkLoader, } = this; + const { x, z } = this.target; const [xcMin, zcMin] = getChunkOfPixel( canvasSize, x - renderDistance, @@ -374,13 +374,31 @@ class Renderer3D extends Renderer { return; } const controlUpdate = super.render(); - if (this.forceNextRender) { - this.reloadChunks(); + + // check if we enter new chunk + if (controlUpdate) { + const { canvasSize } = this.store.getState().canvas; + const prevCenterChunk = this.centerChunk; + const { x, z } = this.target; + const centerChunk = getChunkOfPixel(canvasSize, x, 0, z); + if (!prevCenterChunk + || prevCenterChunk[0] !== centerChunk[0] + || prevCenterChunk[1] !== centerChunk[1] + ) { + this.centerChunk = centerChunk; + this.forceNextRender = true; + console.log('new cc', this.centerChunk); + } } + if (this.forceNextRender || this.forceNextSubrender || controlUpdate ) { + if (this.forceNextRender) { + this.reloadChunks(); + } + if (this.forceNextRender) { if (this.lol !== 'force') { console.log(this.lol, this.lola); @@ -421,6 +439,7 @@ class Renderer3D extends Renderer { this.camera.aspect = window.innerWidth / window.innerHeight; this.camera.updateProjectionMatrix(); this.threeRenderer.setSize(window.innerWidth, window.innerHeight); + this.forceNextSubrender = true; } updateRollOverMesh(sx, sy) {