diff --git a/frontend/javascripts/oxalis/controller/scene_controller.ts b/frontend/javascripts/oxalis/controller/scene_controller.ts index 956b4b86618..43fb0ce0be5 100644 --- a/frontend/javascripts/oxalis/controller/scene_controller.ts +++ b/frontend/javascripts/oxalis/controller/scene_controller.ts @@ -70,6 +70,13 @@ THREE.Mesh.prototype.raycast = acceleratedRaycast; const CUBE_COLOR = 0x999999; const LAYER_CUBE_COLOR = 0xffff99; +export const OrthoBaseRotations = { + [OrthoViews.PLANE_XY]: new THREE.Euler(0, Math.PI, 0), + [OrthoViews.PLANE_YZ]: new THREE.Euler(Math.PI, (1 / 2) * Math.PI, 0), + [OrthoViews.PLANE_XZ]: new THREE.Euler((-1 / 2) * Math.PI, 0, 0), + [OrthoViews.TDView]: new THREE.Euler(Math.PI / 4, Math.PI / 4, Math.PI / 4), +}; + const getVisibleSegmentationLayerNames = reuseInstanceOnEquality((storeState: OxalisState) => getVisibleSegmentationLayers(storeState).map((l) => l.name), ); @@ -243,9 +250,9 @@ class SceneController { [OrthoViews.PLANE_YZ]: new Plane(OrthoViews.PLANE_YZ), [OrthoViews.PLANE_XZ]: new Plane(OrthoViews.PLANE_XZ), }; - this.planes[OrthoViews.PLANE_XY].setRotation(new THREE.Euler(Math.PI, 0, 0)); - this.planes[OrthoViews.PLANE_YZ].setRotation(new THREE.Euler(Math.PI, (1 / 2) * Math.PI, 0)); - this.planes[OrthoViews.PLANE_XZ].setRotation(new THREE.Euler((-1 / 2) * Math.PI, 0, 0)); + this.planes[OrthoViews.PLANE_XY].setBaseRotation(OrthoBaseRotations[OrthoViews.PLANE_XY]); + this.planes[OrthoViews.PLANE_YZ].setBaseRotation(OrthoBaseRotations[OrthoViews.PLANE_YZ]); + this.planes[OrthoViews.PLANE_XZ].setBaseRotation(OrthoBaseRotations[OrthoViews.PLANE_XZ]); const planeMeshes = _.values(this.planes).flatMap((plane) => plane.getMeshes()); this.rootNode = new THREE.Group().add( @@ -386,13 +393,17 @@ class SceneController { this.planes[planeId].setOriginalCrosshairColor(); this.planes[planeId].setVisible(!hidePlanes); - const pos = _.clone(originalPosition); - const ind = Dimensions.getIndices(planeId); // Offset the plane so the user can see the skeletonTracing behind the plane - pos[ind[2]] += + const positionOffset: [number, number, number] = [0, 0, 0]; + positionOffset[ind[2]] += planeId === OrthoViews.PLANE_XY ? this.planeShift[ind[2]] : -this.planeShift[ind[2]]; - this.planes[planeId].setPosition(pos, originalPosition); + //TODOM: adjust rotation of the plane + + this.planes[planeId].offsetForRenderingOrthoView( + new THREE.Vector3(...positionOffset), + originalPosition, + ); this.quickSelectGeometry.adaptVisibilityForRendering(originalPosition, ind[2]); } else { @@ -402,12 +413,21 @@ class SceneController { } } else { for (const planeId of OrthoViewValuesWithoutTDView) { - this.planes[planeId].setPosition(originalPosition); + // this.planes[planeId].setPosition(originalPosition); this.planes[planeId].setGrayCrosshairColor(); this.planes[planeId].setVisible( tdViewDisplayPlanes !== TDViewDisplayModeEnum.NONE, this.isPlaneVisible[planeId] && tdViewDisplayPlanes === TDViewDisplayModeEnum.DATA, ); + const ind = Dimensions.getIndices(planeId); + // Offset the plane so the user can see the skeletonTracing behind the plane + const positionOffset: [number, number, number] = [0, 0, 0]; + positionOffset[ind[2]] += + planeId === OrthoViews.PLANE_XY ? this.planeShift[ind[2]] : -this.planeShift[ind[2]]; + this.planes[planeId].dontOffsetForRenderingTDView( + new THREE.Vector3(...positionOffset), + originalPosition, + ); this.planes[planeId].materialFactory.uniforms.is3DViewBeingRendered.value = true; } } @@ -426,6 +446,7 @@ class SceneController { ); } + // TODO: maybe this needs to be removed!!! if (!optArbitraryPlane) { for (const currentPlane of _.values(this.planes)) { const [scaleX, scaleY] = getPlaneScalingFactor(state, flycam, currentPlane.planeID); diff --git a/frontend/javascripts/oxalis/geometries/arbitrary_plane.ts b/frontend/javascripts/oxalis/geometries/arbitrary_plane.ts index c7dd12856f0..45136788702 100644 --- a/frontend/javascripts/oxalis/geometries/arbitrary_plane.ts +++ b/frontend/javascripts/oxalis/geometries/arbitrary_plane.ts @@ -1,5 +1,5 @@ import _ from "lodash"; -import constants, { OrthoViews } from "oxalis/constants"; +import constants, { OrthoView, OrthoViews } from "oxalis/constants"; import getSceneController from "oxalis/controller/scene_controller_provider"; import PlaneMaterialFactory from "oxalis/geometries/materials/plane_material_factory"; import { getZoomedMatrix } from "oxalis/model/accessors/flycam_accessor"; @@ -27,12 +27,15 @@ type ArbitraryMeshes = { class ArbitraryPlane { meshes: ArbitraryMeshes; isDirty: boolean; + planeID: OrthoView; stopStoreListening: () => void; // @ts-expect-error ts-migrate(2564) FIXME: Property 'materialFactory' has no initializer and ... Remove this comment to see the full error message materialFactory: PlaneMaterialFactory; + baseRotation: THREE.Euler = new THREE.Euler(0, 0, 0); - constructor() { + constructor(planeID: OrthoView) { this.isDirty = true; + this.planeID = planeID; this.meshes = this.createMeshes(); this.stopStoreListening = Store.subscribe(() => { this.isDirty = true; @@ -43,6 +46,9 @@ class ArbitraryPlane { this.stopStoreListening(); this.materialFactory.stopListening(); } + setBaseRotation = (rotation: THREE.Euler) => { + this.baseRotation = rotation; + }; setPosition = (x: number, y: number, z: number) => { // @ts-expect-error ts-migrate(2339) FIXME: Property 'setGlobalPosition' does not exist on typ... Remove this comment to see the full error message @@ -66,6 +72,7 @@ class ArbitraryPlane { return; } + // TODOM: This needs to be done to the plane view planes as well const meshMatrix = new THREE.Matrix4(); meshMatrix.set( matrix[0], @@ -87,7 +94,7 @@ class ArbitraryPlane { ); mesh.matrix.identity(); mesh.matrix.multiply(meshMatrix); - mesh.matrix.multiply(new THREE.Matrix4().makeRotationY(Math.PI)); + mesh.matrix.multiply(new THREE.Matrix4().makeRotationFromEuler(this.baseRotation)); mesh.matrixWorldNeedsUpdate = true; }; @@ -106,7 +113,7 @@ class ArbitraryPlane { return _plane; }; - this.materialFactory = new PlaneMaterialFactory(OrthoViews.PLANE_XY, false, 4); + this.materialFactory = new PlaneMaterialFactory(this.planeID, false, 4); const textureMaterial = this.materialFactory.setup().getMaterial(); const mainPlane = adaptPlane( new THREE.Mesh( diff --git a/frontend/javascripts/oxalis/geometries/plane.ts b/frontend/javascripts/oxalis/geometries/plane.ts index 1e064a2fbfc..1f3eadfe7d7 100644 --- a/frontend/javascripts/oxalis/geometries/plane.ts +++ b/frontend/javascripts/oxalis/geometries/plane.ts @@ -1,3 +1,4 @@ +import type { Matrix4x4 } from "libs/mjs"; import _ from "lodash"; import type { OrthoView, Vector3 } from "oxalis/constants"; import constants, { @@ -6,6 +7,7 @@ import constants, { OrthoViewGrayCrosshairColor, OrthoViewValues, } from "oxalis/constants"; +import getSceneController from "oxalis/controller/scene_controller_provider"; import PlaneMaterialFactory from "oxalis/geometries/materials/plane_material_factory"; import Dimensions from "oxalis/model/dimensions"; import { getBaseVoxelFactorsInUnit } from "oxalis/model/scaleinfo"; @@ -31,7 +33,7 @@ class Plane { // This class is supposed to collect all the Geometries that belong to one single plane such as // the plane itself, its texture, borders and crosshairs. // @ts-expect-error ts-migrate(2564) FIXME: Property 'plane' has no initializer and is not def... Remove this comment to see the full error message - plane: THREE.Mesh; + plane: THREE.Mesh; planeID: OrthoView; materialFactory!: PlaneMaterialFactory; displayCrosshair: boolean; @@ -41,11 +43,18 @@ class Plane { // @ts-expect-error ts-migrate(2564) FIXME: Property 'TDViewBorders' has no initializer and is... Remove this comment to see the full error message TDViewBorders: THREE.Line; lastScaleFactors: [number, number]; + isDirty: boolean; + baseRotation: THREE.Euler; + stopStoreListening: () => void; + // Stores whether the meshes are currently offset for rendering one of the ortho views. This should not be the case when rendering the TD view. + areMeshesOffsetForOrthoViewRendering: boolean; constructor(planeID: OrthoView) { this.planeID = planeID; this.displayCrosshair = true; this.lastScaleFactors = [-1, -1]; + this.isDirty = true; + this.areMeshesOffsetForOrthoViewRendering = false; // VIEWPORT_WIDTH means that the plane should be that many voxels wide in the // dimension with the highest mag. In all other dimensions, the plane // is smaller in voxels, so that it is squared in nm. @@ -53,20 +62,27 @@ class Plane { const baseVoxelFactors = getBaseVoxelFactorsInUnit(Store.getState().dataset.dataSource.scale); const scaleArray = Dimensions.transDim(baseVoxelFactors, this.planeID); this.baseScaleVector = new THREE.Vector3(...scaleArray); + this.baseRotation = new THREE.Euler(0, 0, 0); this.createMeshes(); + this.stopStoreListening = Store.subscribe(() => { + this.isDirty = true; + }); } createMeshes(): void { const pWidth = constants.VIEWPORT_WIDTH; // create plane - const planeGeo = new THREE.PlaneGeometry(pWidth, pWidth, PLANE_SUBDIVISION, PLANE_SUBDIVISION); + const planeGeo = new THREE.PlaneGeometry(pWidth, pWidth, 100, 100); this.materialFactory = new PlaneMaterialFactory( this.planeID, - true, + false, OrthoViewValues.indexOf(this.planeID), ); const textureMaterial = this.materialFactory.setup().getMaterial(); this.plane = new THREE.Mesh(planeGeo, textureMaterial); + this.plane.name = `${this.planeID}-plane`; + this.plane.matrixAutoUpdate = false; + this.plane.material.side = THREE.DoubleSide; // create crosshair const crosshairGeometries = []; this.crosshair = new Array(2); @@ -94,6 +110,8 @@ class Plane { // The default renderOrder is 0. In order for the crosshairs to be shown // render them AFTER the plane has been rendered. this.crosshair[i].renderOrder = 1; + this.crosshair[i].name = `${this.planeID}-crosshair-${i}`; + this.crosshair[i].matrixAutoUpdate = false; } // create borders @@ -109,6 +127,8 @@ class Plane { tdViewBordersGeo, this.getLineBasicMaterial(OrthoViewColors[this.planeID], 1), ); + this.TDViewBorders.name = `${this.planeID}-TDViewBorders`; + this.TDViewBorders.matrixAutoUpdate = false; } setDisplayCrosshair = (value: boolean): void => { @@ -139,12 +159,21 @@ class Plane { }); }; + // Is always called after updateToFlycamMatrix. thus, adding the new scale on top should be fine (I think). setScale(xFactor: number, yFactor: number): void { + // TODOM: This scale will likely be overwritten by the flycam matrix if (this.lastScaleFactors[0] === xFactor && this.lastScaleFactors[1] === yFactor) { return; } this.lastScaleFactors[0] = xFactor; this.lastScaleFactors[1] = yFactor; + /*const additionalScale = new THREE.Vector3(xFactor, yFactor, 1); + this.getMeshes().forEach((mesh) => { + console.log("mesh", mesh.name, mesh.matrix.elements); + mesh.matrix.multiply(new THREE.Matrix4().makeScale(...additionalScale.toArray())); + console.log("mesh", mesh.name, mesh.matrix.elements); + mesh.matrixWorldNeedsUpdate = true; + }); const scaleVec = new THREE.Vector3().multiplyVectors( new THREE.Vector3(xFactor, yFactor, 1), @@ -153,13 +182,11 @@ class Plane { this.plane.scale.copy(scaleVec); this.TDViewBorders.scale.copy(scaleVec); this.crosshair[0].scale.copy(scaleVec); - this.crosshair[1].scale.copy(scaleVec); + this.crosshair[1].scale.copy(scaleVec);*/ } - setRotation = (rotVec: THREE.Euler): void => { - [this.plane, this.TDViewBorders, this.crosshair[0], this.crosshair[1]].map((mesh) => - mesh.setRotationFromEuler(rotVec), - ); + setBaseRotation = (rotVec: THREE.Euler): void => { + this.baseRotation.copy(rotVec); }; // In case the plane's position was offset to make geometries @@ -174,10 +201,8 @@ class Plane { this.plane.position.set(x, y, z); if (originalPosition == null) { - // @ts-expect-error ts-migrate(2339) FIXME: Property 'setGlobalPosition' does not exist on typ... Remove this comment to see the full error message this.plane.material.setGlobalPosition(x, y, z); } else { - // @ts-expect-error ts-migrate(2339) FIXME: Property 'setGlobalPosition' does not exist on typ... Remove this comment to see the full error message this.plane.material.setGlobalPosition( originalPosition[0], originalPosition[1], @@ -186,6 +211,111 @@ class Plane { } }; + offsetForRenderingOrthoView = (offset: THREE.Vector3, original: Vector3): void => { + if (this.areMeshesOffsetForOrthoViewRendering) { + return; + } + this.getMeshes().forEach((mesh) => { + mesh.matrix.multiply(new THREE.Matrix4().makeTranslation(offset)); + }); + // TODO: Test whether this.plane.position is up to date with what stored in the matrix + // get position from matrix + const currentPosition = new THREE.Vector3( + this.plane.matrix.elements[12], + this.plane.matrix.elements[13], + this.plane.matrix.elements[14], + ); + this.plane.material.setGlobalPosition( + currentPosition.x + offset.x, + currentPosition.y + offset.y, + currentPosition.z + offset.z, + ); + console.log( + "offsetForRenderingOrthoView", + offset, + currentPosition, + this.plane.material.globalPosition, + "original", + original, + ); + this.areMeshesOffsetForOrthoViewRendering = true; + }; + + dontOffsetForRenderingTDView = (offset: THREE.Vector3, original: Vector3): void => { + if (!this.areMeshesOffsetForOrthoViewRendering) { + return; + } + offset.negate(); + this.getMeshes().forEach((mesh) => { + mesh.matrix.multiply(new THREE.Matrix4().makeTranslation(offset)); + }); + // TODO: Test whether this.plane.position is up to date with what stored in the matrix + const currentPosition = new THREE.Vector3( + this.plane.matrix.elements[12], + this.plane.matrix.elements[13], + this.plane.matrix.elements[14], + ); + this.plane.material.setGlobalPosition( + currentPosition.x + offset.x, + currentPosition.y + offset.y, + currentPosition.z + offset.z, + ); + console.log( + "offsetForRenderingOrthoView", + offset, + this.plane.position, + currentPosition.sub(offset), + "original", + original, + ); + this.areMeshesOffsetForOrthoViewRendering = false; + }; + + updateToFlycamMatrix(flycamMatrix: Matrix4x4): void { + // TODOM: + if (this.isDirty) { + const meshMatrix = new THREE.Matrix4(); + meshMatrix.set( + flycamMatrix[0], + flycamMatrix[4], + flycamMatrix[8], + flycamMatrix[12], + flycamMatrix[1], + flycamMatrix[5], + flycamMatrix[9], + flycamMatrix[13], + flycamMatrix[2], + flycamMatrix[6], + flycamMatrix[10], + flycamMatrix[14], + flycamMatrix[3], + flycamMatrix[7], + flycamMatrix[11], + flycamMatrix[15], + ); + const updateMesh = (mesh: THREE.Mesh | THREE.Line | null | undefined) => { + if (!mesh) { + return; + } + mesh.matrix.identity(); + mesh.matrix.multiply(meshMatrix); + mesh.matrix.multiply(new THREE.Matrix4().makeRotationFromEuler(this.baseRotation)); + mesh.matrix.multiply( + new THREE.Matrix4().makeScale(this.lastScaleFactors[0], this.lastScaleFactors[1], 1), + ); + if (this.planeID === "PLANE_XY") { + // console.log("matrix plane", mesh.name, mesh.matrix); + } + mesh.matrixWorldNeedsUpdate = true; + }; + this.getMeshes().forEach(updateMesh); + + this.isDirty = false; + // TODOM: ignore error for now. + getSceneController().update(); + } + } + setVisible = (isVisible: boolean, isDataVisible?: boolean): void => { this.plane.visible = isDataVisible != null ? isDataVisible : isVisible; this.TDViewBorders.visible = isVisible; @@ -195,11 +325,11 @@ class Plane { getMeshes = () => [this.plane, this.TDViewBorders, this.crosshair[0], this.crosshair[1]]; setLinearInterpolationEnabled = (enabled: boolean) => { - // @ts-expect-error ts-migrate(2339) FIXME: Property 'setUseBilinearFiltering' does not exist ... Remove this comment to see the full error message this.plane.material.setUseBilinearFiltering(enabled); }; destroy() { + this.stopStoreListening(); this.materialFactory.destroy(); } } diff --git a/frontend/javascripts/oxalis/model/bucket_data_handling/prefetch_strategy_plane.ts b/frontend/javascripts/oxalis/model/bucket_data_handling/prefetch_strategy_plane.ts index 8b1244e88ad..d148cf4821a 100644 --- a/frontend/javascripts/oxalis/model/bucket_data_handling/prefetch_strategy_plane.ts +++ b/frontend/javascripts/oxalis/model/bucket_data_handling/prefetch_strategy_plane.ts @@ -70,6 +70,8 @@ export class AbstractPrefetchStrategy { return buckets; } } + +// TODOM: in case of rotation, a PrefetchStrategyArbitrary is needed for every viewport export class PrefetchStrategy extends AbstractPrefetchStrategy { velocityRangeStart = 0; velocityRangeEnd = Number.POSITIVE_INFINITY; diff --git a/frontend/javascripts/oxalis/model/reducers/flycam_reducer.ts b/frontend/javascripts/oxalis/model/reducers/flycam_reducer.ts index c6f50d5659b..a71804525fc 100644 --- a/frontend/javascripts/oxalis/model/reducers/flycam_reducer.ts +++ b/frontend/javascripts/oxalis/model/reducers/flycam_reducer.ts @@ -275,13 +275,7 @@ function FlycamReducer(state: OxalisState, action: Action): OxalisState { } case "SET_ROTATION": { - // This action should only be dispatched when *not* being in orthogonal mode, - // because this would lead to incorrect buckets being selected for rendering. - if (state.temporaryConfiguration.viewMode !== "orthogonal") { - return setRotationReducer(state, action.rotation); - } - // No-op - return state; + return setRotationReducer(state, action.rotation); } case "SET_DIRECTION": { diff --git a/frontend/javascripts/oxalis/model/reducers/settings_reducer.ts b/frontend/javascripts/oxalis/model/reducers/settings_reducer.ts index 31428b2f39d..b783e9c5f75 100644 --- a/frontend/javascripts/oxalis/model/reducers/settings_reducer.ts +++ b/frontend/javascripts/oxalis/model/reducers/settings_reducer.ts @@ -183,8 +183,8 @@ function SettingsReducer(state: OxalisState, action: Action): OxalisState { viewMode: action.viewMode, }); if (action.viewMode !== "orthogonal") { - return newState; } + return newState; // Restore rotation because it might have been changed by the user // in flight/oblique mode. Since this affects the matrix (which is // also used in orthogonal mode), the rotation needs to be reset. diff --git a/frontend/javascripts/oxalis/shaders/coords.glsl.ts b/frontend/javascripts/oxalis/shaders/coords.glsl.ts index 66b044c8447..76f5c3c41f2 100644 --- a/frontend/javascripts/oxalis/shaders/coords.glsl.ts +++ b/frontend/javascripts/oxalis/shaders/coords.glsl.ts @@ -54,11 +54,8 @@ export const getWorldCoordUVW: ShaderModule = { // In orthogonal mode, the planes are offset in 3D space to allow skeletons to be rendered before // each plane. Since w (e.g., z for xy plane) is // the same for all texels computed in this shader, we simply use globalPosition[w] instead - <% if (isOrthogonal) { %> - getW(globalPosition) - <% } else { %> - worldCoordUVW.z / voxelSizeFactorUVW.z - <% } %> + // TODOM: if not rotated, getW can be used + worldCoordUVW.z / voxelSizeFactorUVW.z ); return worldCoordUVW; diff --git a/frontend/javascripts/oxalis/shaders/filtering.glsl.ts b/frontend/javascripts/oxalis/shaders/filtering.glsl.ts index 47299b96a14..1b69297e9e9 100644 --- a/frontend/javascripts/oxalis/shaders/filtering.glsl.ts +++ b/frontend/javascripts/oxalis/shaders/filtering.glsl.ts @@ -79,6 +79,7 @@ export const getTrilinearColorFor: ShaderModule = { const getMaybeFilteredColor: ShaderModule = { requirements: [getColorForCoords, getBilinearColorFor, getTrilinearColorFor], code: ` + // TODOM: Consider passing an argument like "isRotated" to determine whether bilinear or trilinear filtering should be used. vec4 getMaybeFilteredColor( float layerIndex, float d_texture_width, diff --git a/frontend/javascripts/oxalis/shaders/main_data_shaders.glsl.ts b/frontend/javascripts/oxalis/shaders/main_data_shaders.glsl.ts index d6e481c282f..c63a3cb77f7 100644 --- a/frontend/javascripts/oxalis/shaders/main_data_shaders.glsl.ts +++ b/frontend/javascripts/oxalis/shaders/main_data_shaders.glsl.ts @@ -480,7 +480,7 @@ void main() { // Remember the original z position, since it can subtly diverge in the // following calculations due to floating point inaccuracies. This can // result in artifacts, such as the crosshair disappearing. - float originalZ = gl_Position.z; + /*float originalZ = gl_Position.z; // Remember, the top of the viewport has Y=1 whereas the left has X=-1. vec3 worldCoordTopLeft = transDim((modelMatrix * vec4(-PLANE_WIDTH/2., PLANE_WIDTH/2., 0., 1.)).xyz); @@ -590,7 +590,7 @@ void main() { } } } - <% }) %> + <% }) %>*/ } `)({ ...params, diff --git a/frontend/javascripts/oxalis/shaders/texture_access.glsl.ts b/frontend/javascripts/oxalis/shaders/texture_access.glsl.ts index 6a03dc8e990..bc784d84324 100644 --- a/frontend/javascripts/oxalis/shaders/texture_access.glsl.ts +++ b/frontend/javascripts/oxalis/shaders/texture_access.glsl.ts @@ -206,8 +206,8 @@ export const getColorForCoords: ShaderModule = { // To avoid rare rendering artifacts, don't use the precomputed // bucket address when being at the border of buckets. - bool beSafe = false; - { + bool beSafe = true; + /*{ renderedMagIdx = outputMagIdx[globalLayerIndex]; vec3 coords = floor(getAbsoluteCoords(worldPositionUVW, renderedMagIdx, globalLayerIndex)); vec3 absoluteBucketPosition = div(coords, bucketWidth); @@ -220,7 +220,7 @@ export const getColorForCoords: ShaderModule = { ) { beSafe = true; } - } + }*/ if (beSafe || !supportsPrecomputedBucketAddress) { diff --git a/frontend/javascripts/oxalis/view/action-bar/dataset_position_view.tsx b/frontend/javascripts/oxalis/view/action-bar/dataset_position_view.tsx index 69049585aca..db82e2f84c7 100644 --- a/frontend/javascripts/oxalis/view/action-bar/dataset_position_view.tsx +++ b/frontend/javascripts/oxalis/view/action-bar/dataset_position_view.tsx @@ -6,7 +6,6 @@ import Toast from "libs/toast"; import { Vector3Input } from "libs/vector_input"; import message from "messages"; import type { Vector3, ViewMode } from "oxalis/constants"; -import constants from "oxalis/constants"; import { getDatasetExtentInVoxel } from "oxalis/model/accessors/dataset_accessor"; import { getPosition, getRotation } from "oxalis/model/accessors/flycam_accessor"; import { setPositionAction, setRotationAction } from "oxalis/model/actions/flycam_actions"; @@ -112,7 +111,6 @@ class DatasetPositionView extends PureComponent { } const rotation = V3.round(getRotation(this.props.flycam)); - const isArbitraryMode = constants.MODES_ARBITRARY.includes(this.props.viewMode); const positionView = (
{ /> - {isArbitraryMode ? ( + { { allowDecimals /> - ) : null} + }
); return ( diff --git a/frontend/javascripts/oxalis/view/arbitrary_view.ts b/frontend/javascripts/oxalis/view/arbitrary_view.ts index f2980f65345..6ef97c767a1 100644 --- a/frontend/javascripts/oxalis/view/arbitrary_view.ts +++ b/frontend/javascripts/oxalis/view/arbitrary_view.ts @@ -43,6 +43,7 @@ class ArbitraryView { group: THREE.Object3D; cameraPosition: Array; unsubscribeFunctions: Array<() => void> = []; + baseRotation = new THREE.Euler(0, 0, 0); constructor() { this.animate = this.animateImpl.bind(this); @@ -75,6 +76,10 @@ class ArbitraryView { return this.cameras; } + setBaseRotation = (rotVec: THREE.Euler): void => { + this.baseRotation.copy(rotVec); + }; + start(): void { if (!this.isRunning) { this.isRunning = true; diff --git a/frontend/javascripts/oxalis/view/plane_view.ts b/frontend/javascripts/oxalis/view/plane_view.ts index cc5567f176d..b087aaec7e0 100644 --- a/frontend/javascripts/oxalis/view/plane_view.ts +++ b/frontend/javascripts/oxalis/view/plane_view.ts @@ -3,16 +3,25 @@ import VisibilityAwareRaycaster from "libs/visibility_aware_raycaster"; import window from "libs/window"; import _ from "lodash"; import type { OrthoViewMap, Vector2, Vector3, Viewport } from "oxalis/constants"; -import Constants, { OrthoViewColors, OrthoViewValues, OrthoViews } from "oxalis/constants"; +import Constants, { + ARBITRARY_CAM_DISTANCE, + OrthoViewColors, + OrthoViewValues, + OrthoViewValuesWithoutTDView, + OrthoViews, +} from "oxalis/constants"; import type { VertexSegmentMapping } from "oxalis/controller/mesh_helpers"; +import { OrthoBaseRotations } from "oxalis/controller/scene_controller"; import getSceneController, { getSceneControllerOrNull, } from "oxalis/controller/scene_controller_provider"; import type { MeshSceneNode, SceneGroupForMeshes } from "oxalis/controller/segment_mesh_controller"; +import { getZoomedMatrix } from "oxalis/model/accessors/flycam_accessor"; import { AnnotationTool } from "oxalis/model/accessors/tool_accessor"; import { getInputCatcherRect } from "oxalis/model/accessors/view_mode_accessor"; import { getActiveSegmentationTracing } from "oxalis/model/accessors/volumetracing_accessor"; import { updateTemporarySettingAction } from "oxalis/model/actions/settings_actions"; +import Dimensions from "oxalis/model/dimensions"; import { listenToStoreProperty } from "oxalis/model/helpers/listener_helpers"; import Store from "oxalis/store"; import { getGroundTruthLayoutRect } from "oxalis/view/layouting/default_layout_configs"; @@ -73,6 +82,7 @@ class PlaneView { scene.add(cameras[plane]); } this.cameras = cameras; + // TODOM: next up rotate the cameras as well createDirLight([10, 10, 10], [0, 0, 10], LIGHT_INTENSITY, this.cameras[OrthoViews.TDView]); createDirLight([-10, 10, 10], [0, 0, 10], LIGHT_INTENSITY, this.cameras[OrthoViews.TDView]); @@ -84,6 +94,10 @@ class PlaneView { this.cameras[OrthoViews.PLANE_YZ].up = new THREE.Vector3(0, -1, 0); this.cameras[OrthoViews.PLANE_XZ].up = new THREE.Vector3(0, 0, -1); this.cameras[OrthoViews.TDView].up = new THREE.Vector3(0, 0, -1); + this.cameras[OrthoViews.PLANE_XY].matrixAutoUpdate = false; + this.cameras[OrthoViews.PLANE_YZ].matrixAutoUpdate = false; + this.cameras[OrthoViews.PLANE_XZ].matrixAutoUpdate = false; + this.cameras[OrthoViews.TDView].matrixAutoUpdate = true; for (const plane of OrthoViewValues) { this.cameras[plane].lookAt(new THREE.Vector3(0, 0, 0)); @@ -101,20 +115,57 @@ class PlaneView { window.requestAnimationFrame(() => this.animate()); } + logMatrix(m: THREE.Matrix4): void { + const scale = new THREE.Vector3(); + const rotation = new THREE.Quaternion(); + const position = new THREE.Vector3(); + m.decompose(position, rotation, scale); + const euler = new THREE.Euler(); + euler.setFromQuaternion(rotation); + console.log("position", position, "rotation", euler, "scale", scale); + console.log("matrix", m); + } + renderFunction(forceRender: boolean = false): void { // This is the main render function. // All 3D meshes and the trianglesplane are rendered here. TWEEN.update(); - const SceneController = getSceneController(); + const sceneController = getSceneController(); // skip rendering if nothing has changed // This prevents the GPU/CPU from constantly // working and keeps your lap cool // ATTENTION: this limits the FPS to 60 FPS (depending on the keypress update frequency) if (forceRender || this.needsRerender) { - const { renderer, scene } = SceneController; - SceneController.update(); + const { renderer, scene, planes } = sceneController; + sceneController.update(); const storeState = Store.getState(); + const zoomedFlycamMatrix = getZoomedMatrix(storeState.flycam); + for (const planeId of OrthoViewValuesWithoutTDView) { + planes[planeId].updateToFlycamMatrix(zoomedFlycamMatrix); + // TODOM: camera adjustment here!!! + const camera = this.cameras[planeId]; + const m = zoomedFlycamMatrix; + //this.logMatrix(camera.matrix); + // biome-ignore format: don't format array + camera.matrix.set( + m[0], m[4], m[8], m[12], + m[1], m[5], m[9], m[13], + m[2], m[6], m[10], m[14], + m[3], m[7], m[11], m[15], + ); + //this.logMatrix(camera.matrix); + const rotationMatrix = new THREE.Matrix4().makeRotationFromEuler( + OrthoBaseRotations[planeId], + ); + camera.matrix.multiply(rotationMatrix); + camera.matrix.multiply(new THREE.Matrix4().makeTranslation(0, 0, ARBITRARY_CAM_DISTANCE)); + + //this.logMatrix(camera.matrix); + + //this.logMatrix(camera.matrix); + camera.matrixWorldNeedsUpdate = true; + } const viewport = { [OrthoViews.PLANE_XY]: getInputCatcherRect(storeState, "PLANE_XY"), [OrthoViews.PLANE_YZ]: getInputCatcherRect(storeState, "PLANE_YZ"), @@ -125,7 +176,7 @@ class PlaneView { clearCanvas(renderer); for (const plane of OrthoViewValues) { - SceneController.updateSceneForCam(plane); + sceneController.updateSceneForCam(plane); const { left, top, width, height } = viewport[plane]; if (width > 0 && height > 0) {