From b8dde9e148e94cf9f1b05928edb28eecf9327ba4 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Fri, 15 May 2020 15:30:31 +0300 Subject: [PATCH 01/88] Implemented drawing on image --- .../components/Three/Helpers/drawSettings.js | 5 ++ .../Three/Helpers/drawing-on-texture.js | 75 +++++++++++++++++++ .../shot-generator/components/Three/Image.js | 53 +++++++++++-- 3 files changed, 125 insertions(+), 8 deletions(-) create mode 100644 src/js/shot-generator/components/Three/Helpers/drawSettings.js create mode 100644 src/js/shot-generator/components/Three/Helpers/drawing-on-texture.js diff --git a/src/js/shot-generator/components/Three/Helpers/drawSettings.js b/src/js/shot-generator/components/Three/Helpers/drawSettings.js new file mode 100644 index 0000000000..75edadf19c --- /dev/null +++ b/src/js/shot-generator/components/Three/Helpers/drawSettings.js @@ -0,0 +1,5 @@ +let drawSetting = { + color:'#00ff00', + meshSize: 3 +} +export default drawSetting; \ No newline at end of file diff --git a/src/js/shot-generator/components/Three/Helpers/drawing-on-texture.js b/src/js/shot-generator/components/Three/Helpers/drawing-on-texture.js new file mode 100644 index 0000000000..45166e3d3f --- /dev/null +++ b/src/js/shot-generator/components/Three/Helpers/drawing-on-texture.js @@ -0,0 +1,75 @@ +import * as THREE from 'three' +import drawSetting from './drawSettings' +class DrawingTexture { + constructor(){ + this.drawingCanvas = document.createElement('canvas'); + this.drawingCtx = this.drawingCanvas.getContext('2d'); + this.raycaster = new THREE.Raycaster(); + this.material = null; + this.isEnabled = true; + drawSetting.meshSize = 5; + drawSetting.color = '#ff0000'; + + this.prevX = null + this.prevY = null + } + + set Enabled(value) { + this.isEnabled = value; + } + + get Enabled() { + return this.isEnabled; + } + + createMaterial() { + let texture = new THREE.CanvasTexture(this.drawingCanvas); + let material = new THREE.MeshToonMaterial({ map: texture, transparent: true }); + material.needsUpdate = true; + this.material = material; + return material + } + + setTexture(texture) { + const { width, height } = texture.image; + this.drawingCanvas.width = width; + this.drawingCanvas.height = height; + + this.drawingCtx.drawImage(texture.image, 0, 0, width, height); + this.material.map.needsUpdate = true; + } + + intersectImage (x, y, object, camera) { + this.raycaster.setFromCamera({x,y}, camera); + let intersects = this.raycaster.intersectObject(object, true); + return intersects.length && intersects[0].uv; + } + + draw (event, gl, object, camera){ + if(!this.isEnabled) return; + const rect = gl.domElement.getBoundingClientRect(); + let worldX = ( ( event.clientX - rect.left ) / rect.width ) * 2 - 1; + let worldY = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1; + const { width, height } = this.material.map.image; + let coordinates = this.intersectImage(worldX, worldY, object, camera); + if(!coordinates) return; + let screenX = coordinates.x * width; + let screenY = ( 1 - coordinates.y) * height; + if(!this.prevX || !this.prevY) { + this.prevX = screenX + this.prevY = screenY + } + this.drawingCtx.drawImage(this.material.map.image, 0, 0); + this.drawingCtx.beginPath(); + this.drawingCtx.moveTo(this.prevX, this.prevY); + this.drawingCtx.lineTo(screenX, screenY); + this.drawingCtx.strokeStyle = drawSetting.color; + this.drawingCtx.lineWidth = drawSetting.meshSize; + this.drawingCtx.stroke(); + this.drawingCtx.closePath(); + this.material.map.needsUpdate = true; + this.prevX = screenX + this.prevY = screenY + } +} +export default DrawingTexture; \ No newline at end of file diff --git a/src/js/shot-generator/components/Three/Image.js b/src/js/shot-generator/components/Three/Image.js index 3e01cd774f..1b6408b6fb 100644 --- a/src/js/shot-generator/components/Three/Image.js +++ b/src/js/shot-generator/components/Three/Image.js @@ -1,27 +1,30 @@ import * as THREE from 'three' -import React, { useEffect, useMemo, useRef } from 'react' -import { extend } from 'react-three-fiber' +import React, { useEffect, useMemo, useRef, useLayoutEffect } from 'react' +import { extend, useThree } from 'react-three-fiber' import { useAsset } from '../../hooks/use-assets-manager' import { SHOT_LAYERS } from '../../utils/ShotLayers' import RoundedBoxGeometryCreator from './../../../vendor/three-rounded-box' import { axis } from "../../../shared/IK/utils/TransformControls" +import DrawingTexture from "./Helpers/drawing-on-texture" +import KeyCommandsSingleton from '../KeyHandler/KeyCommandsSingleton' const RoundedBoxGeometry = RoundedBoxGeometryCreator(THREE) extend({RoundedBoxGeometry}) const Image = React.memo(({ sceneObject, isSelected, imagesPaths, ...props }) => { const {asset: texture} = useAsset(imagesPaths[0] || null) - + const { gl, camera } = useThree() const aspect = useRef(1) const ref = useRef() - + const drawingTexture = useRef(new DrawingTexture()) + const isDrawingMode = useRef(false) const material = useMemo(() => { - return new THREE.MeshToonMaterial({ transparent: true }) + let material = drawingTexture.current.createMaterial() + return material }, []) useMemo(() => { if(!texture) return - texture.wrapS = texture.wrapT = THREE.RepeatWrapping texture.offset.set(0, 0) texture.repeat.set(1, 1) @@ -30,7 +33,7 @@ const Image = React.memo(({ sceneObject, isSelected, imagesPaths, ...props }) => aspect.current = width / height if (material) { - material.map = texture + drawingTexture.current.setTexture(texture) material.needsUpdate = true } }, [texture, imagesPaths[0]]) @@ -46,6 +49,7 @@ const Image = React.memo(({ sceneObject, isSelected, imagesPaths, ...props }) => useEffect(() => { if (isSelected) { + drawingTexture.current.Enabled = true props.objectRotationControl.setUpdateCharacter((name, rotation) => { let euler = new THREE.Euler().setFromQuaternion(ref.current.worldQuaternion()) props.updateObject(ref.current.userData.id, { @@ -59,13 +63,26 @@ const Image = React.memo(({ sceneObject, isSelected, imagesPaths, ...props }) => props.objectRotationControl.selectObject(ref.current, ref.current.uuid) props.objectRotationControl.IsEnabled = !sceneObject.locked props.objectRotationControl.control.setShownAxis(axis.X_axis | axis.Y_axis | axis.Z_axis) + + KeyCommandsSingleton.getInstance().addKeyCommand({ + key: "camera-controls", + keyCustomCheck: onKeyDown, + value: () => {}}) + gl.domElement.addEventListener('mousemove', draw) + window.addEventListener( 'keyup', onKeyUp, false ) + } else { + drawingTexture.current.Enabled = false if(props.objectRotationControl && props.objectRotationControl.isSelected(ref.current)) { props.objectRotationControl.deselectObject() } + + gl.domElement.removeEventListener('mousemove', draw) + window.removeEventListener( 'keyup', onKeyUp, false ) + KeyCommandsSingleton.getInstance().removeKeyCommand({key: "camera-controls"}) } }, [isSelected]) - + const { x, y, z, visible, height, rotation, locked } = sceneObject useEffect(() => { @@ -73,6 +90,26 @@ const Image = React.memo(({ sceneObject, isSelected, imagesPaths, ...props }) => props.objectRotationControl.IsEnabled = !locked }, [locked]) + const draw = (event) => { + if(!isDrawingMode.current) return + drawingTexture.current.draw(event, gl, ref.current, camera); + } + + const onKeyDown = (event) => { + if ( event.keyCode === 16 ) { + isDrawingMode.current = true + props.objectRotationControl.deselectObject() + } + } + + const onKeyUp = (event) => { + if ( event.keyCode === 16 ) { + isDrawingMode.current = false + props.objectRotationControl.selectObject(ref.current, ref.current.uuid) + props.objectRotationControl.IsEnabled = !sceneObject.locked + } + } + return ( Date: Fri, 15 May 2020 16:25:47 +0300 Subject: [PATCH 02/88] Added reseting of mesh's prev position --- .../components/Three/Helpers/drawing-on-texture.js | 14 ++++---------- src/js/shot-generator/components/Three/Image.js | 12 ++++++++++-- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/js/shot-generator/components/Three/Helpers/drawing-on-texture.js b/src/js/shot-generator/components/Three/Helpers/drawing-on-texture.js index 45166e3d3f..4405da47d9 100644 --- a/src/js/shot-generator/components/Three/Helpers/drawing-on-texture.js +++ b/src/js/shot-generator/components/Three/Helpers/drawing-on-texture.js @@ -37,7 +37,7 @@ class DrawingTexture { this.drawingCtx.drawImage(texture.image, 0, 0, width, height); this.material.map.needsUpdate = true; - } + } intersectImage (x, y, object, camera) { this.raycaster.setFromCamera({x,y}, camera); @@ -45,20 +45,14 @@ class DrawingTexture { return intersects.length && intersects[0].uv; } - draw (event, gl, object, camera){ + draw (mousePosition, object, camera){ if(!this.isEnabled) return; - const rect = gl.domElement.getBoundingClientRect(); - let worldX = ( ( event.clientX - rect.left ) / rect.width ) * 2 - 1; - let worldY = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1; + const { width, height } = this.material.map.image; - let coordinates = this.intersectImage(worldX, worldY, object, camera); + let coordinates = this.intersectImage(mousePosition.x, mousePosition.y, object, camera); if(!coordinates) return; let screenX = coordinates.x * width; let screenY = ( 1 - coordinates.y) * height; - if(!this.prevX || !this.prevY) { - this.prevX = screenX - this.prevY = screenY - } this.drawingCtx.drawImage(this.material.map.image, 0, 0); this.drawingCtx.beginPath(); this.drawingCtx.moveTo(this.prevX, this.prevY); diff --git a/src/js/shot-generator/components/Three/Image.js b/src/js/shot-generator/components/Three/Image.js index 1b6408b6fb..f60c17fc9a 100644 --- a/src/js/shot-generator/components/Three/Image.js +++ b/src/js/shot-generator/components/Three/Image.js @@ -10,7 +10,12 @@ import KeyCommandsSingleton from '../KeyHandler/KeyCommandsSingleton' const RoundedBoxGeometry = RoundedBoxGeometryCreator(THREE) extend({RoundedBoxGeometry}) - +const mouse = (event, gl) => { + const rect = gl.domElement.getBoundingClientRect(); + let worldX = ( ( event.clientX - rect.left ) / rect.width ) * 2 - 1; + let worldY = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1; + return { x: worldX, y: worldY } +} const Image = React.memo(({ sceneObject, isSelected, imagesPaths, ...props }) => { const {asset: texture} = useAsset(imagesPaths[0] || null) const { gl, camera } = useThree() @@ -92,13 +97,16 @@ const Image = React.memo(({ sceneObject, isSelected, imagesPaths, ...props }) => const draw = (event) => { if(!isDrawingMode.current) return - drawingTexture.current.draw(event, gl, ref.current, camera); + drawingTexture.current.draw(mouse(event, gl), ref.current, camera); } const onKeyDown = (event) => { if ( event.keyCode === 16 ) { isDrawingMode.current = true props.objectRotationControl.deselectObject() + let { x, y } = mouse(event, gl) + drawingTexture.current.prevX = x + drawingTexture.current.prevY = y } } From 10dc60e42e84a972b04a17c42c2644ae0186d14b Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Mon, 18 May 2020 13:28:12 +0300 Subject: [PATCH 03/88] Changed drawing to percentage based --- .../Three/Helpers/drawing-on-texture.js | 63 +++++++++++++++---- .../shot-generator/components/Three/Image.js | 18 +++--- 2 files changed, 59 insertions(+), 22 deletions(-) diff --git a/src/js/shot-generator/components/Three/Helpers/drawing-on-texture.js b/src/js/shot-generator/components/Three/Helpers/drawing-on-texture.js index 4405da47d9..3b5e7a80ec 100644 --- a/src/js/shot-generator/components/Three/Helpers/drawing-on-texture.js +++ b/src/js/shot-generator/components/Three/Helpers/drawing-on-texture.js @@ -8,10 +8,11 @@ class DrawingTexture { this.material = null; this.isEnabled = true; drawSetting.meshSize = 5; - drawSetting.color = '#ff0000'; + drawSetting.color = '#000000'; - this.prevX = null - this.prevY = null + this.prevX = null; + this.prevY = null; + this.uvBased = false; } set Enabled(value) { @@ -22,6 +23,11 @@ class DrawingTexture { return this.isEnabled; } + resetMeshPos() { + this.prevX = null + this.prevY = null + } + createMaterial() { let texture = new THREE.CanvasTexture(this.drawingCanvas); let material = new THREE.MeshToonMaterial({ map: texture, transparent: true }); @@ -40,19 +46,52 @@ class DrawingTexture { } intersectImage (x, y, object, camera) { - this.raycaster.setFromCamera({x,y}, camera); - let intersects = this.raycaster.intersectObject(object, true); - return intersects.length && intersects[0].uv; + this.raycaster.setFromCamera({x,y}, camera); + let intersects = this.raycaster.intersectObject(object, true); + if(this.uvBased) return intersects.length && intersects[0].uv + let percentage = null + if(intersects.length && intersects[0]) { + let scale = object.scale.clone()//new THREE.Vector3(); + scale.z = 0; + scale.y = -scale.y + let quaternion = object.worldQuaternion() + scale.applyQuaternion(quaternion) + scale.divideScalar(2) + let intersectPos = intersects[0].point + let position = object.worldPosition()//.applyEuler(euler) + let topPosition = position.clone().sub(scale) + let bottomPosition = position.clone().add(scale) + quaternion.inverse() + bottomPosition.applyQuaternion(quaternion) + topPosition.applyQuaternion(quaternion) + intersectPos.applyQuaternion(quaternion) + bottomPosition.sub(topPosition) + intersectPos.sub(topPosition) + intersectPos.divide(bottomPosition) + percentage = {} + percentage.x = intersectPos.x + percentage.y = intersectPos.y + percentage.z = intersectPos.z + } + return percentage; } draw (mousePosition, object, camera){ if(!this.isEnabled) return; const { width, height } = this.material.map.image; - let coordinates = this.intersectImage(mousePosition.x, mousePosition.y, object, camera); - if(!coordinates) return; - let screenX = coordinates.x * width; - let screenY = ( 1 - coordinates.y) * height; + let percentage = this.intersectImage(mousePosition.x, mousePosition.y, object, camera); + if(percentage === null) { + this.resetMeshPos(); + return; + } + let screenX = this.uvBased ? percentage.x * width : width * percentage.x; + let screenY = this.uvBased ? ( 1 - percentage.y) * height : height * percentage.y; + if(!this.prevX || !this.prevY) { + this.prevX = screenX; + this.prevY = screenY; + } + this.drawingCtx.drawImage(this.material.map.image, 0, 0); this.drawingCtx.beginPath(); this.drawingCtx.moveTo(this.prevX, this.prevY); @@ -62,8 +101,8 @@ class DrawingTexture { this.drawingCtx.stroke(); this.drawingCtx.closePath(); this.material.map.needsUpdate = true; - this.prevX = screenX - this.prevY = screenY + this.prevX = screenX; + this.prevY = screenY; } } export default DrawingTexture; \ No newline at end of file diff --git a/src/js/shot-generator/components/Three/Image.js b/src/js/shot-generator/components/Three/Image.js index f60c17fc9a..7feb8ee5f1 100644 --- a/src/js/shot-generator/components/Three/Image.js +++ b/src/js/shot-generator/components/Three/Image.js @@ -70,7 +70,7 @@ const Image = React.memo(({ sceneObject, isSelected, imagesPaths, ...props }) => props.objectRotationControl.control.setShownAxis(axis.X_axis | axis.Y_axis | axis.Z_axis) KeyCommandsSingleton.getInstance().addKeyCommand({ - key: "camera-controls", + key: "image-drawing", keyCustomCheck: onKeyDown, value: () => {}}) gl.domElement.addEventListener('mousemove', draw) @@ -84,7 +84,7 @@ const Image = React.memo(({ sceneObject, isSelected, imagesPaths, ...props }) => gl.domElement.removeEventListener('mousemove', draw) window.removeEventListener( 'keyup', onKeyUp, false ) - KeyCommandsSingleton.getInstance().removeKeyCommand({key: "camera-controls"}) + KeyCommandsSingleton.getInstance().removeKeyCommand({key: "image-drawing"}) } }, [isSelected]) @@ -102,19 +102,17 @@ const Image = React.memo(({ sceneObject, isSelected, imagesPaths, ...props }) => const onKeyDown = (event) => { if ( event.keyCode === 16 ) { - isDrawingMode.current = true - props.objectRotationControl.deselectObject() - let { x, y } = mouse(event, gl) - drawingTexture.current.prevX = x - drawingTexture.current.prevY = y + isDrawingMode.current = true; + props.objectRotationControl.deselectObject(); } } const onKeyUp = (event) => { if ( event.keyCode === 16 ) { - isDrawingMode.current = false - props.objectRotationControl.selectObject(ref.current, ref.current.uuid) - props.objectRotationControl.IsEnabled = !sceneObject.locked + isDrawingMode.current = false; + props.objectRotationControl.selectObject(ref.current, ref.current.uuid); + props.objectRotationControl.IsEnabled = !sceneObject.locked; + drawingTexture.current.resetMeshPos(); } } From 29f33171572868efba6016c22bda043294431ac3 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Mon, 18 May 2020 15:05:07 +0300 Subject: [PATCH 04/88] Impelemented image texture saving --- src/js/shot-generator/SceneManagerR3fLarge.js | 1 + .../Three/Helpers/drawing-on-texture.js | 6 +++++ .../shot-generator/components/Three/Image.js | 24 ++++++++++++++++++- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/js/shot-generator/SceneManagerR3fLarge.js b/src/js/shot-generator/SceneManagerR3fLarge.js index 18d1dc4ef3..927db1a07c 100644 --- a/src/js/shot-generator/SceneManagerR3fLarge.js +++ b/src/js/shot-generator/SceneManagerR3fLarge.js @@ -402,6 +402,7 @@ const SceneManagerR3fLarge = connect( return { let worldY = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1; return { x: worldX, y: worldY } } + + +let saveDataURLtoFile = (dataURL, filename, boardPath, updateObject, sceneObject) => { + let imageData = dataURL.replace(/^data:image\/\w+;base64,/, '') + let imageFilePath = path.join(path.dirname(boardPath), 'models/images', filename) + + let isImageExist = fs.pathExistsSync(imageFilePath) + + let projectDir = path.dirname(boardPath) + let assetsDir = path.join(projectDir, 'models', 'images') + fs.ensureDirSync(assetsDir) + let dst = path.join(assetsDir, path.basename(imageFilePath)) + let id = path.relative(projectDir, dst) + fs.writeFileSync(imageFilePath, imageData, 'base64') + if(!isImageExist || !sceneObject.imageAttachmentIds || !sceneObject.imageAttachmentIds.find(ids => ids === id)) { + updateObject(sceneObject.id, {imageAttachmentIds: [id]}) + } +} + const Image = React.memo(({ sceneObject, isSelected, imagesPaths, ...props }) => { const {asset: texture} = useAsset(imagesPaths[0] || null) const { gl, camera } = useThree() @@ -113,6 +134,7 @@ const Image = React.memo(({ sceneObject, isSelected, imagesPaths, ...props }) => props.objectRotationControl.selectObject(ref.current, ref.current.uuid); props.objectRotationControl.IsEnabled = !sceneObject.locked; drawingTexture.current.resetMeshPos(); + saveDataURLtoFile(drawingTexture.current.getImage(), `${sceneObject.id}-texture.png`, props.storyboarderFilePath, props.updateObject, sceneObject) } } From 247f14adfb3e148a86d46521a603cefafd4f3a0b Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Mon, 25 May 2020 15:00:01 +0300 Subject: [PATCH 05/88] Fixed drawing resets on save as a new shot --- .../shot-generator/components/Three/Image.js | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/js/shot-generator/components/Three/Image.js b/src/js/shot-generator/components/Three/Image.js index 8f6e64abf2..dea6dc8f31 100644 --- a/src/js/shot-generator/components/Three/Image.js +++ b/src/js/shot-generator/components/Three/Image.js @@ -90,12 +90,8 @@ const Image = React.memo(({ sceneObject, isSelected, imagesPaths, ...props }) => props.objectRotationControl.IsEnabled = !sceneObject.locked props.objectRotationControl.control.setShownAxis(axis.X_axis | axis.Y_axis | axis.Z_axis) - KeyCommandsSingleton.getInstance().addKeyCommand({ - key: "image-drawing", - keyCustomCheck: onKeyDown, - value: () => {}}) + gl.domElement.addEventListener('mousemove', draw) - window.addEventListener( 'keyup', onKeyUp, false ) } else { drawingTexture.current.Enabled = false @@ -104,10 +100,23 @@ const Image = React.memo(({ sceneObject, isSelected, imagesPaths, ...props }) => } gl.domElement.removeEventListener('mousemove', draw) - window.removeEventListener( 'keyup', onKeyUp, false ) - KeyCommandsSingleton.getInstance().removeKeyCommand({key: "image-drawing"}) + } }, [isSelected]) + + useEffect(() => { + if(isSelected && ref.current) { + KeyCommandsSingleton.getInstance().addKeyCommand({ + key: "image-drawing", + keyCustomCheck: onKeyDown, + value: () => {}}) + window.addEventListener( 'keyup', onKeyUp, false ) + } + return () => { + window.removeEventListener( 'keyup', onKeyUp ) + KeyCommandsSingleton.getInstance().removeKeyCommand({key: "image-drawing"}) + } + }, [isSelected, ref.current]) const { x, y, z, visible, height, rotation, locked } = sceneObject @@ -131,10 +140,10 @@ const Image = React.memo(({ sceneObject, isSelected, imagesPaths, ...props }) => const onKeyUp = (event) => { if ( event.keyCode === 16 ) { isDrawingMode.current = false; - props.objectRotationControl.selectObject(ref.current, ref.current.uuid); - props.objectRotationControl.IsEnabled = !sceneObject.locked; drawingTexture.current.resetMeshPos(); saveDataURLtoFile(drawingTexture.current.getImage(), `${sceneObject.id}-texture.png`, props.storyboarderFilePath, props.updateObject, sceneObject) + props.objectRotationControl.selectObject(ref.current, ref.current.uuid); + props.objectRotationControl.IsEnabled = !sceneObject.locked; } } From 7f85ec297c869942e355b7fe785e58775bfb78e5 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Wed, 27 May 2020 12:12:59 +0300 Subject: [PATCH 06/88] Added image tab for mesh settings --- .../shared/actions/scene-object-creators.js | 2 +- .../InspectedElement/MeshInspector/index.js | 55 +++++++++++++++++++ .../components/InspectedElement/index.js | 11 ++++ .../Three/Helpers/drawing-on-texture.js | 10 ++-- .../shot-generator/components/Three/Image.js | 15 +++-- 5 files changed, 83 insertions(+), 10 deletions(-) create mode 100644 src/js/shot-generator/components/InspectedElement/MeshInspector/index.js diff --git a/src/js/shared/actions/scene-object-creators.js b/src/js/shared/actions/scene-object-creators.js index e16436a8d6..27238ec69b 100644 --- a/src/js/shared/actions/scene-object-creators.js +++ b/src/js/shared/actions/scene-object-creators.js @@ -168,7 +168,7 @@ const createImage = (id, camera, room) => { x, y, z: 1, rotation: { x: 0, y: rotation, z: 0 }, - + mesh: {size:2, color:"#000000"}, visible: true, opacity: 1, visibleToCam: true, diff --git a/src/js/shot-generator/components/InspectedElement/MeshInspector/index.js b/src/js/shot-generator/components/InspectedElement/MeshInspector/index.js new file mode 100644 index 0000000000..f671623d3a --- /dev/null +++ b/src/js/shot-generator/components/InspectedElement/MeshInspector/index.js @@ -0,0 +1,55 @@ +import {connect} from 'react-redux' +import React, {useMemo} from 'react' +import ColorSelect from '../../ColorSelect' +import { + getSelections, + getSceneObjects, + updateObject + } from './../../../../shared/reducers/shot-generator' +import {formatters, NumberSlider, transforms, textFormatters, textConstraints} from '../../NumberSlider' +import deepEqualSelector from './../../../../utils/deepEqualSelector' +const getObjectData = deepEqualSelector([getSelections, getSceneObjects], (selections, sceneObjects) => { + return sceneObjects[selections[0]] +}) + +const MeshInspector = connect((state) => ({ + sceneObject: getObjectData(state) +}), +{ + updateObject +} +)( +React.memo(({ + updateObject, + sceneObject +}) => { + console.log(sceneObject) + + const setSize = (value) => { + updateObject(sceneObject.id, {mesh: {...sceneObject.mesh, size: value}}) + } + + const setColor = (value) => { + updateObject(sceneObject.id, {mesh: {...sceneObject.mesh, color: value}}) + } + + return ( + + + + + + + ) +})) + +export default MeshInspector \ No newline at end of file diff --git a/src/js/shot-generator/components/InspectedElement/index.js b/src/js/shot-generator/components/InspectedElement/index.js index 1415e5bd3b..1504516093 100644 --- a/src/js/shot-generator/components/InspectedElement/index.js +++ b/src/js/shot-generator/components/InspectedElement/index.js @@ -16,12 +16,14 @@ import HandInspector from './HandInspector/HandPresetsEditor/index' import PosePresetsInspector from './PosePresetsInspector/index' import ModelInspector from './ModelInspector/index' import AttachableInspector from './AttachableInspector/index' +import MeshInspector from './MeshInspector/index' import Icon from '../Icon' import Modal from '../Modal' const isChar = (type) => type === 'character' const isObj = (type) => type === 'object' +const isImage = (type) => type === 'image' const nullTab = {tab: null, panel: null} const Inspector = React.memo(({id, selectedName, selectedType, updateObject}) => { @@ -63,6 +65,13 @@ const Inspector = React.memo(({id, selectedName, selectedType, updateObject}) => } }, [selectedType]) + const meshTab = useMemo(() => { + if (!isImage(selectedType)) return nullTab + return { + tab: , + panel: + } + }, [selectedType]) return ( @@ -98,6 +107,7 @@ const Inspector = React.memo(({id, selectedName, selectedType, updateObject}) => {charPoseTab.tab} {modelTab.tab} {attachmentTab.tab} + {meshTab.tab}
@@ -106,6 +116,7 @@ const Inspector = React.memo(({id, selectedName, selectedType, updateObject}) => {charPoseTab.panel} {modelTab.panel} {attachmentTab.panel} + {meshTab.panel}
diff --git a/src/js/shot-generator/components/Three/Helpers/drawing-on-texture.js b/src/js/shot-generator/components/Three/Helpers/drawing-on-texture.js index 2f4b81410a..fdaa7e7d7c 100644 --- a/src/js/shot-generator/components/Three/Helpers/drawing-on-texture.js +++ b/src/js/shot-generator/components/Three/Helpers/drawing-on-texture.js @@ -82,7 +82,7 @@ class DrawingTexture { return percentage; } - draw (mousePosition, object, camera){ + draw (mousePosition, object, camera, mesh){ if(!this.isEnabled) return; const { width, height } = this.material.map.image; @@ -97,13 +97,15 @@ class DrawingTexture { this.prevX = screenX; this.prevY = screenY; } - + console.log(mesh) this.drawingCtx.drawImage(this.material.map.image, 0, 0); + this.drawingCtx.strokeStyle = mesh.color; + this.drawingCtx.lineWidth = mesh.size; + this.drawingCtx.lineJoin = true this.drawingCtx.beginPath(); this.drawingCtx.moveTo(this.prevX, this.prevY); this.drawingCtx.lineTo(screenX, screenY); - this.drawingCtx.strokeStyle = drawSetting.color; - this.drawingCtx.lineWidth = drawSetting.meshSize; + this.drawingCtx.stroke(); this.drawingCtx.closePath(); this.material.map.needsUpdate = true; diff --git a/src/js/shot-generator/components/Three/Image.js b/src/js/shot-generator/components/Three/Image.js index dea6dc8f31..3c19a1763d 100644 --- a/src/js/shot-generator/components/Three/Image.js +++ b/src/js/shot-generator/components/Three/Image.js @@ -90,31 +90,36 @@ const Image = React.memo(({ sceneObject, isSelected, imagesPaths, ...props }) => props.objectRotationControl.IsEnabled = !sceneObject.locked props.objectRotationControl.control.setShownAxis(axis.X_axis | axis.Y_axis | axis.Z_axis) - gl.domElement.addEventListener('mousemove', draw) } else { + gl.domElement.removeEventListener('mousemove', draw) drawingTexture.current.Enabled = false if(props.objectRotationControl && props.objectRotationControl.isSelected(ref.current)) { props.objectRotationControl.deselectObject() } - gl.domElement.removeEventListener('mousemove', draw) } + return () => { + gl.domElement.removeEventListener('mousemove', draw) + } }, [isSelected]) useEffect(() => { if(isSelected && ref.current) { KeyCommandsSingleton.getInstance().addKeyCommand({ - key: "image-drawing", + key: `image-drawing ${ref.current.uuid}`, keyCustomCheck: onKeyDown, value: () => {}}) window.addEventListener( 'keyup', onKeyUp, false ) + } else { + window.removeEventListener( 'keyup', onKeyUp ) + KeyCommandsSingleton.getInstance().removeKeyCommand({key: `image-drawing ${ref.current.uuid}`}) } return () => { window.removeEventListener( 'keyup', onKeyUp ) - KeyCommandsSingleton.getInstance().removeKeyCommand({key: "image-drawing"}) + KeyCommandsSingleton.getInstance().removeKeyCommand({key: `image-drawing ${ref.current.uuid}`}) } }, [isSelected, ref.current]) @@ -127,7 +132,7 @@ const Image = React.memo(({ sceneObject, isSelected, imagesPaths, ...props }) => const draw = (event) => { if(!isDrawingMode.current) return - drawingTexture.current.draw(mouse(event, gl), ref.current, camera); + drawingTexture.current.draw(mouse(event, gl), ref.current, camera, sceneObject.mesh); } const onKeyDown = (event) => { From a3ebaa9163388fea2ebcc5da99ce2bb18a2a78b2 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Wed, 27 May 2020 14:50:58 +0300 Subject: [PATCH 07/88] Added eraser mesh and fixed image needs to be reselected to apply mesh changes --- .../InspectedElement/MeshInspector/index.js | 24 +++++++++-- .../Three/Helpers/Meshes/EraserMesh.js | 37 ++++++++++++++++ .../components/Three/Helpers/Meshes/Mesh.js | 19 +++++++++ .../Three/Helpers/Meshes/SimpleMesh.js | 25 +++++++++++ .../Three/Helpers/Meshes/TextureMeshTypes.js | 5 +++ .../Three/Helpers/drawing-on-texture.js | 42 ++++++++++--------- .../shot-generator/components/Three/Image.js | 27 +++++++----- 7 files changed, 145 insertions(+), 34 deletions(-) create mode 100644 src/js/shot-generator/components/Three/Helpers/Meshes/EraserMesh.js create mode 100644 src/js/shot-generator/components/Three/Helpers/Meshes/Mesh.js create mode 100644 src/js/shot-generator/components/Three/Helpers/Meshes/SimpleMesh.js create mode 100644 src/js/shot-generator/components/Three/Helpers/Meshes/TextureMeshTypes.js diff --git a/src/js/shot-generator/components/InspectedElement/MeshInspector/index.js b/src/js/shot-generator/components/InspectedElement/MeshInspector/index.js index f671623d3a..3e4f2bde0b 100644 --- a/src/js/shot-generator/components/InspectedElement/MeshInspector/index.js +++ b/src/js/shot-generator/components/InspectedElement/MeshInspector/index.js @@ -8,6 +8,7 @@ import { } from './../../../../shared/reducers/shot-generator' import {formatters, NumberSlider, transforms, textFormatters, textConstraints} from '../../NumberSlider' import deepEqualSelector from './../../../../utils/deepEqualSelector' +import MeshType from '../../Three/Helpers/Meshes/TextureMeshTypes' const getObjectData = deepEqualSelector([getSelections, getSceneObjects], (selections, sceneObjects) => { return sceneObjects[selections[0]] }) @@ -33,9 +34,25 @@ React.memo(({ updateObject(sceneObject.id, {mesh: {...sceneObject.mesh, color: value}}) } + const setType = (event) => { + updateObject(sceneObject.id, {mesh: {...sceneObject.mesh, type: event.target.value}}) + } + return ( - +
+
Type
+ +
- + onSetValue={setColor}/> } +
) })) diff --git a/src/js/shot-generator/components/Three/Helpers/Meshes/EraserMesh.js b/src/js/shot-generator/components/Three/Helpers/Meshes/EraserMesh.js new file mode 100644 index 0000000000..0cc7ed1c32 --- /dev/null +++ b/src/js/shot-generator/components/Three/Helpers/Meshes/EraserMesh.js @@ -0,0 +1,37 @@ +import Mesh from './Mesh' +class EraserMesh extends Mesh { + constructor(drawingCtx) { + super(drawingCtx); + } + + draw(currentPos, mesh) { + super.draw(currentPos); + this.drawingCtx.fillStyle = 'white'; + + let circle = new Path2D(); + let xOffset = currentPos.x - this.prevPos.x; + let yOffset = currentPos.y - this.prevPos.y; + let length + if(Math.abs(xOffset) < Math.abs(yOffset)) { + length = Math.abs(yOffset) + + } else { + length = Math.abs(xOffset) + } + xOffset /= length; + yOffset /= length; + let size = mesh.size + for(let i = 0; i < length; i++) { + let x = xOffset * i; + let y = yOffset * i; + circle.moveTo(this.prevPos.x + x, this.prevPos.y + x); + circle.arc(this.prevPos.x + x - size , this.prevPos.y + y - size, size, 0, 2 * Math.PI) + } + this.drawingCtx.stroke(); + this.drawingCtx.fill(circle) + this.prevPos.x = currentPos.x; + this.prevPos.y = currentPos.y; + } +} + +export default EraserMesh; \ No newline at end of file diff --git a/src/js/shot-generator/components/Three/Helpers/Meshes/Mesh.js b/src/js/shot-generator/components/Three/Helpers/Meshes/Mesh.js new file mode 100644 index 0000000000..97ecaf32b7 --- /dev/null +++ b/src/js/shot-generator/components/Three/Helpers/Meshes/Mesh.js @@ -0,0 +1,19 @@ +class Mesh { + constructor(drawingCtx) { + this.drawingCtx = drawingCtx; + this.resetMeshPos(); + } + + resetMeshPos() { + this.prevPos = null + } + + draw(currentPos) { + if(!this.prevPos) { + this.prevPos = {} + this.prevPos.x = currentPos.x; + this.prevPos.y = currentPos.y; + } + } +} +export default Mesh diff --git a/src/js/shot-generator/components/Three/Helpers/Meshes/SimpleMesh.js b/src/js/shot-generator/components/Three/Helpers/Meshes/SimpleMesh.js new file mode 100644 index 0000000000..5df2dcfe24 --- /dev/null +++ b/src/js/shot-generator/components/Three/Helpers/Meshes/SimpleMesh.js @@ -0,0 +1,25 @@ +import Mesh from './Mesh' +class SimpleMesh extends Mesh { + + constructor(drawingCtx) { + super(drawingCtx); + this.drawingCtx.lineJoin = true; + } + + draw(currentPos, mesh) { + super.draw(currentPos); + this.drawingCtx.strokeStyle = mesh.color; + this.drawingCtx.lineWidth = mesh.size; + + this.drawingCtx.beginPath(); + this.drawingCtx.moveTo(this.prevPos.x, this.prevPos.y); + this.drawingCtx.lineTo(currentPos.x, currentPos.y); + + this.drawingCtx.stroke(); + this.drawingCtx.closePath(); + this.prevPos.x = currentPos.x; + this.prevPos.y = currentPos.y; + } +} + +export default SimpleMesh \ No newline at end of file diff --git a/src/js/shot-generator/components/Three/Helpers/Meshes/TextureMeshTypes.js b/src/js/shot-generator/components/Three/Helpers/Meshes/TextureMeshTypes.js new file mode 100644 index 0000000000..312e860de9 --- /dev/null +++ b/src/js/shot-generator/components/Three/Helpers/Meshes/TextureMeshTypes.js @@ -0,0 +1,5 @@ +const MeshType = { + SIMPLE: "Simple", + ERASER: "Eraser" +} +export default MeshType \ No newline at end of file diff --git a/src/js/shot-generator/components/Three/Helpers/drawing-on-texture.js b/src/js/shot-generator/components/Three/Helpers/drawing-on-texture.js index fdaa7e7d7c..9cfef69865 100644 --- a/src/js/shot-generator/components/Three/Helpers/drawing-on-texture.js +++ b/src/js/shot-generator/components/Three/Helpers/drawing-on-texture.js @@ -1,5 +1,8 @@ import * as THREE from 'three' import drawSetting from './drawSettings' +import SimpleMesh from './Meshes/SimpleMesh'; +import EraserMesh from './Meshes/EraserMesh'; + class DrawingTexture { constructor(){ this.drawingCanvas = document.createElement('canvas'); @@ -13,6 +16,7 @@ class DrawingTexture { this.prevX = null; this.prevY = null; this.uvBased = false; + this.drawingMesh = new EraserMesh(this.drawingCtx) } set Enabled(value) { @@ -24,8 +28,20 @@ class DrawingTexture { } resetMeshPos() { - this.prevX = null - this.prevY = null + this.drawingMesh.resetMeshPos() + } + + setMesh(type) { + switch(type) { + case "Simple": + this.drawingMesh = new SimpleMesh(this.drawingCtx) + break; + case "Eraser": + this.drawingMesh = new EraserMesh(this.drawingCtx) + break; + default: + this.drawingMesh = new SimpleMesh(this.drawingCtx) + } } getImage() { @@ -57,14 +73,14 @@ class DrawingTexture { if(this.uvBased) return intersects.length && intersects[0].uv let percentage = null if(intersects.length && intersects[0]) { - let scale = object.scale.clone()//new THREE.Vector3(); + let scale = object.scale.clone() scale.z = 0; scale.y = -scale.y let quaternion = object.worldQuaternion() scale.applyQuaternion(quaternion) scale.divideScalar(2) let intersectPos = intersects[0].point - let position = object.worldPosition()//.applyEuler(euler) + let position = object.worldPosition() let topPosition = position.clone().sub(scale) let bottomPosition = position.clone().add(scale) quaternion.inverse() @@ -93,24 +109,10 @@ class DrawingTexture { } let screenX = this.uvBased ? percentage.x * width : width * percentage.x; let screenY = this.uvBased ? ( 1 - percentage.y) * height : height * percentage.y; - if(!this.prevX || !this.prevY) { - this.prevX = screenX; - this.prevY = screenY; - } - console.log(mesh) - this.drawingCtx.drawImage(this.material.map.image, 0, 0); - this.drawingCtx.strokeStyle = mesh.color; - this.drawingCtx.lineWidth = mesh.size; - this.drawingCtx.lineJoin = true - this.drawingCtx.beginPath(); - this.drawingCtx.moveTo(this.prevX, this.prevY); - this.drawingCtx.lineTo(screenX, screenY); + // this.drawingCtx.drawImage(this.material.map.image, 0, 0); + this.drawingMesh.draw({ x: screenX, y: screenY }, mesh) - this.drawingCtx.stroke(); - this.drawingCtx.closePath(); this.material.map.needsUpdate = true; - this.prevX = screenX; - this.prevY = screenY; } } export default DrawingTexture; \ No newline at end of file diff --git a/src/js/shot-generator/components/Three/Image.js b/src/js/shot-generator/components/Three/Image.js index 3c19a1763d..c7b8676d7b 100644 --- a/src/js/shot-generator/components/Three/Image.js +++ b/src/js/shot-generator/components/Three/Image.js @@ -1,5 +1,5 @@ import * as THREE from 'three' -import React, { useEffect, useMemo, useRef } from 'react' +import React, { useEffect, useMemo, useRef, useLayoutEffect } from 'react' import { extend, useThree } from 'react-three-fiber' import { useAsset } from '../../hooks/use-assets-manager' import path from 'path' @@ -90,10 +90,7 @@ const Image = React.memo(({ sceneObject, isSelected, imagesPaths, ...props }) => props.objectRotationControl.IsEnabled = !sceneObject.locked props.objectRotationControl.control.setShownAxis(axis.X_axis | axis.Y_axis | axis.Z_axis) - gl.domElement.addEventListener('mousemove', draw) - } else { - gl.domElement.removeEventListener('mousemove', draw) drawingTexture.current.Enabled = false if(props.objectRotationControl && props.objectRotationControl.isSelected(ref.current)) { props.objectRotationControl.deselectObject() @@ -101,9 +98,6 @@ const Image = React.memo(({ sceneObject, isSelected, imagesPaths, ...props }) => } - return () => { - gl.domElement.removeEventListener('mousemove', draw) - } }, [isSelected]) useEffect(() => { @@ -113,9 +107,6 @@ const Image = React.memo(({ sceneObject, isSelected, imagesPaths, ...props }) => keyCustomCheck: onKeyDown, value: () => {}}) window.addEventListener( 'keyup', onKeyUp, false ) - } else { - window.removeEventListener( 'keyup', onKeyUp ) - KeyCommandsSingleton.getInstance().removeKeyCommand({key: `image-drawing ${ref.current.uuid}`}) } return () => { window.removeEventListener( 'keyup', onKeyUp ) @@ -130,11 +121,25 @@ const Image = React.memo(({ sceneObject, isSelected, imagesPaths, ...props }) => props.objectRotationControl.IsEnabled = !locked }, [locked]) + useEffect(() => { + drawingTexture.current.setMesh(sceneObject.mesh.type) + }, [sceneObject.mesh.type]) + + useLayoutEffect(() => { + if(!isSelected) return + gl.domElement.addEventListener('mousemove', draw) + return () => { + gl.domElement.removeEventListener('mousemove', draw) + } + }, [isSelected, sceneObject.mesh]) + const draw = (event) => { if(!isDrawingMode.current) return + console.log(sceneObject.mesh) drawingTexture.current.draw(mouse(event, gl), ref.current, camera, sceneObject.mesh); } - + + const onKeyDown = (event) => { if ( event.keyCode === 16 ) { isDrawingMode.current = true; From 1057d1f4892e79999169742169502036a4d5e5e4 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Thu, 28 May 2020 09:27:50 +0300 Subject: [PATCH 08/88] Improved simple drawing mesh path --- .../components/Three/Helpers/Meshes/EraserMesh.js | 4 ++-- .../components/Three/Helpers/Meshes/SimpleMesh.js | 2 +- src/js/shot-generator/components/Three/Image.js | 2 -- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/js/shot-generator/components/Three/Helpers/Meshes/EraserMesh.js b/src/js/shot-generator/components/Three/Helpers/Meshes/EraserMesh.js index 0cc7ed1c32..786f37fbf2 100644 --- a/src/js/shot-generator/components/Three/Helpers/Meshes/EraserMesh.js +++ b/src/js/shot-generator/components/Three/Helpers/Meshes/EraserMesh.js @@ -24,8 +24,8 @@ class EraserMesh extends Mesh { for(let i = 0; i < length; i++) { let x = xOffset * i; let y = yOffset * i; - circle.moveTo(this.prevPos.x + x, this.prevPos.y + x); - circle.arc(this.prevPos.x + x - size , this.prevPos.y + y - size, size, 0, 2 * Math.PI) + circle.moveTo(this.prevPos.x + x, this.prevPos.y + y); + circle.arc(this.prevPos.x + x, this.prevPos.y + y, size, 0, 2 * Math.PI) } this.drawingCtx.stroke(); this.drawingCtx.fill(circle) diff --git a/src/js/shot-generator/components/Three/Helpers/Meshes/SimpleMesh.js b/src/js/shot-generator/components/Three/Helpers/Meshes/SimpleMesh.js index 5df2dcfe24..90d8c513a2 100644 --- a/src/js/shot-generator/components/Three/Helpers/Meshes/SimpleMesh.js +++ b/src/js/shot-generator/components/Three/Helpers/Meshes/SimpleMesh.js @@ -16,7 +16,7 @@ class SimpleMesh extends Mesh { this.drawingCtx.lineTo(currentPos.x, currentPos.y); this.drawingCtx.stroke(); - this.drawingCtx.closePath(); + //this.drawingCtx.closePath(); this.prevPos.x = currentPos.x; this.prevPos.y = currentPos.y; } diff --git a/src/js/shot-generator/components/Three/Image.js b/src/js/shot-generator/components/Three/Image.js index c7b8676d7b..1ea7ca65b9 100644 --- a/src/js/shot-generator/components/Three/Image.js +++ b/src/js/shot-generator/components/Three/Image.js @@ -135,11 +135,9 @@ const Image = React.memo(({ sceneObject, isSelected, imagesPaths, ...props }) => const draw = (event) => { if(!isDrawingMode.current) return - console.log(sceneObject.mesh) drawingTexture.current.draw(mouse(event, gl), ref.current, camera, sceneObject.mesh); } - const onKeyDown = (event) => { if ( event.keyCode === 16 ) { isDrawingMode.current = true; From 0dbedc93b4dc896314d434ed42c70bd4b5e51e97 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Thu, 28 May 2020 10:20:56 +0300 Subject: [PATCH 09/88] Fixed modified texture csaves not on board save --- .../shot-generator/components/Three/Image.js | 21 ++-------- .../components/Three/SaveShot.js | 40 +++++++++++++++++-- 2 files changed, 41 insertions(+), 20 deletions(-) diff --git a/src/js/shot-generator/components/Three/Image.js b/src/js/shot-generator/components/Three/Image.js index 1ea7ca65b9..be66fdfb66 100644 --- a/src/js/shot-generator/components/Three/Image.js +++ b/src/js/shot-generator/components/Three/Image.js @@ -19,22 +19,11 @@ const mouse = (event, gl) => { return { x: worldX, y: worldY } } - -let saveDataURLtoFile = (dataURL, filename, boardPath, updateObject, sceneObject) => { +let saveDataURLtoFile = (dataURL, filename, boardPath) => { let imageData = dataURL.replace(/^data:image\/\w+;base64,/, '') - let imageFilePath = path.join(path.dirname(boardPath), 'models/images', filename) - - let isImageExist = fs.pathExistsSync(imageFilePath) - - let projectDir = path.dirname(boardPath) - let assetsDir = path.join(projectDir, 'models', 'images') - fs.ensureDirSync(assetsDir) - let dst = path.join(assetsDir, path.basename(imageFilePath)) - let id = path.relative(projectDir, dst) + //let imageFilePath = path.join(path.dirname(boardPath), 'models/images', filename) + let imageFilePath = path.join(path.dirname(boardPath), 'models/images', `temp_${filename}`) fs.writeFileSync(imageFilePath, imageData, 'base64') - if(!isImageExist || !sceneObject.imageAttachmentIds || !sceneObject.imageAttachmentIds.find(ids => ids === id)) { - updateObject(sceneObject.id, {imageAttachmentIds: [id]}) - } } const Image = React.memo(({ sceneObject, isSelected, imagesPaths, ...props }) => { @@ -95,8 +84,6 @@ const Image = React.memo(({ sceneObject, isSelected, imagesPaths, ...props }) => if(props.objectRotationControl && props.objectRotationControl.isSelected(ref.current)) { props.objectRotationControl.deselectObject() } - - } }, [isSelected]) @@ -149,7 +136,7 @@ const Image = React.memo(({ sceneObject, isSelected, imagesPaths, ...props }) => if ( event.keyCode === 16 ) { isDrawingMode.current = false; drawingTexture.current.resetMeshPos(); - saveDataURLtoFile(drawingTexture.current.getImage(), `${sceneObject.id}-texture.png`, props.storyboarderFilePath, props.updateObject, sceneObject) + saveDataURLtoFile(drawingTexture.current.getImage(), `${sceneObject.id}-texture.png`, props.storyboarderFilePath) props.objectRotationControl.selectObject(ref.current, ref.current.uuid); props.objectRotationControl.IsEnabled = !sceneObject.locked; } diff --git a/src/js/shot-generator/components/Three/SaveShot.js b/src/js/shot-generator/components/Three/SaveShot.js index 4cd52e58b6..2fb8bcd4aa 100644 --- a/src/js/shot-generator/components/Three/SaveShot.js +++ b/src/js/shot-generator/components/Three/SaveShot.js @@ -3,20 +3,25 @@ import { connect } from 'react-redux' import { getSelections, getSerializedState, + updateObject, markSaved, - selectObject + selectObject, + getSceneObjects } from '../../../shared/reducers/shot-generator' import { ipcRenderer } from 'electron' import { useThree } from 'react-three-fiber' import { SHOT_LAYERS } from '../../utils/ShotLayers' import { OutlineEffect } from '../../../vendor/OutlineEffect' import { remote } from 'electron' +import path from 'path' +import fs from 'fs-extra' const { dialog } = remote const withState = (fn) => (dispatch, getState) => fn(dispatch, getState()) const SaveShot = connect( state => ({ + storyboarderFilePath : state.meta.storyboarderFilePath }), { getSelections, @@ -24,6 +29,7 @@ const SaveShot = connect( withState, markSaved, selectObject, + updateObject, saveScene: filepath => (dispatch, getState) => { let state = getState() let contents = getSerializedState(state) @@ -33,14 +39,16 @@ const SaveShot = connect( }) ( React.memo(({ withState, + storyboarderFilePath, markSaved, isPlot = false, - selectObject + selectObject, + updateObject }) => { const { scene, camera } = useThree() const imageRenderer = useRef() const outlineEffect = useRef() - + useEffect(() => { if (!imageRenderer.current) { imageRenderer.current = new THREE.WebGLRenderer({ antialias: true }), { defaultThickness:0.008 } @@ -53,6 +61,7 @@ const SaveShot = connect( }, []) const saveShot = () => { + saveImages() selectObject(null) if(!isPlot) { withState((dispatch, state) => { @@ -79,6 +88,7 @@ const SaveShot = connect( } const insertShot = useCallback(() => { + saveImages() selectObject(null) if(!isPlot) { withState((dispatch, state) => { @@ -106,6 +116,30 @@ const SaveShot = connect( } }, [scene]) + + const saveImages = () => { + let imageObjects + withState((dispatch, state) => { + imageObjects = Object.values(getSceneObjects(state)).filter(obj => obj.type === "image") + }) + for( let i = 0; i < imageObjects.length; i++ ) { + let image = imageObjects[i] + let tempImageFilePath = path.join(path.dirname(storyboarderFilePath), 'models/images', `temp_${image.id}-texture.png`) + let imageFilePath = path.join(path.dirname(storyboarderFilePath), 'models/images', `${image.id}-texture.png`) + + let isImageExist = fs.pathExistsSync(tempImageFilePath) + if(!isImageExist) continue + fs.rename(tempImageFilePath, imageFilePath) + let projectDir = path.dirname(storyboarderFilePath) + let assetsDir = path.join(projectDir, 'models', 'images') + fs.ensureDirSync(assetsDir) + let dst = path.join(assetsDir, path.basename(imageFilePath)) + let id = path.relative(projectDir, dst) + if(!image.imageAttachmentIds || !image.imageAttachmentIds.find(ids => ids === id)) { + updateObject(image.id, {imageAttachmentIds: [id]}) + } + } + } // add handlers once, and use refs for callbacks useEffect(() => { From 559f658ad968dd8c89248eb728679fdac31fbbb7 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Mon, 1 Jun 2020 10:59:29 +0300 Subject: [PATCH 10/88] Changed geometry to rounded plane geometry --- .../Three/Helpers/create-rounded-plane.js | 59 +++++++++++++++++++ .../Three/Helpers/drawing-on-texture.js | 6 +- .../shot-generator/components/Three/Image.js | 3 +- 3 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 src/js/shot-generator/components/Three/Helpers/create-rounded-plane.js diff --git a/src/js/shot-generator/components/Three/Helpers/create-rounded-plane.js b/src/js/shot-generator/components/Three/Helpers/create-rounded-plane.js new file mode 100644 index 0000000000..292d0e41a7 --- /dev/null +++ b/src/js/shot-generator/components/Three/Helpers/create-rounded-plane.js @@ -0,0 +1,59 @@ + +const createRoundedPlane = (radius = 2, offset = 2, smooth = 16) => { + let geometry = new THREE.Geometry() + + offset = (offset - radius) / 2 + radius = radius / 4 + + let planeA = new THREE.PlaneGeometry((offset+radius) * 2, offset * 2) + geometry.merge(planeA) + + let planeB = new THREE.PlaneGeometry(offset * 2, (offset+radius) * 2) + geometry.merge(planeB) + + let cornerA = new THREE.CircleGeometry(radius, smooth, (Math.PI * 2 / 4) * 1, Math.PI * 2 / 4); + let matrixA = new THREE.Matrix4(); + matrixA.makeTranslation(0-offset, 0+offset, 0) + geometry.merge(cornerA, matrixA) + + let cornerB = new THREE.CircleGeometry(radius, smooth, (Math.PI * 2 / 4) * 0, Math.PI * 2 / 4); + let matrixB = new THREE.Matrix4(); + matrixB.makeTranslation(0+offset, 0+offset, 0) + geometry.merge(cornerB, matrixB) + + let cornerC = new THREE.CircleGeometry(radius, smooth, (Math.PI * 2 / 4) * 3, Math.PI * 2 / 4); + let matrixC = new THREE.Matrix4(); + matrixC.makeTranslation(0+offset, 0-offset, 0) + geometry.merge(cornerC, matrixC) + + let cornerD = new THREE.CircleGeometry(radius, smooth, (Math.PI * 2 / 4) * 2, Math.PI * 2 / 4); + let matrixD = new THREE.Matrix4(); + matrixD.makeTranslation(0-offset, 0-offset, 0) + geometry.merge(cornerD, matrixD) + + remapUVs(geometry) + return geometry +} + +const remapUVs = (geometry) => { + geometry.computeBoundingBox() + let min = geometry.boundingBox.min + let max = geometry.boundingBox.max + let offset = new THREE.Vector2(0 - min.x, 0 - min.y) + let size = new THREE.Vector2(max.x - min.x, max.y - min.y) + geometry.faceVertexUvs[0] = [] + for(const face of geometry.faces) { + let v1 = geometry.vertices[face.a] + let v2 = geometry.vertices[face.b] + let v3 = geometry.vertices[face.c] + geometry.faceVertexUvs[0].push([ + new THREE.Vector2((v1.x + offset.x)/size.x, (v1.y + offset.y)/size.y), + new THREE.Vector2((v2.x + offset.x)/size.x, (v2.y + offset.y)/size.y), + new THREE.Vector2((v3.x + offset.x)/size.x, (v3.y + offset.y)/size.y) + ]) + } + + geometry.uvsNeedUpdate = true +} + +export default createRoundedPlane \ No newline at end of file diff --git a/src/js/shot-generator/components/Three/Helpers/drawing-on-texture.js b/src/js/shot-generator/components/Three/Helpers/drawing-on-texture.js index 9cfef69865..e0a4581f88 100644 --- a/src/js/shot-generator/components/Three/Helpers/drawing-on-texture.js +++ b/src/js/shot-generator/components/Three/Helpers/drawing-on-texture.js @@ -15,7 +15,7 @@ class DrawingTexture { this.prevX = null; this.prevY = null; - this.uvBased = false; + this.uvBased = true; this.drawingMesh = new EraserMesh(this.drawingCtx) } @@ -52,7 +52,7 @@ class DrawingTexture { createMaterial() { let texture = new THREE.CanvasTexture(this.drawingCanvas); - let material = new THREE.MeshToonMaterial({ map: texture, transparent: true }); + let material = new THREE.MeshToonMaterial({ map: texture, transparent: true, side: THREE.DoubleSide }); material.needsUpdate = true; this.material = material; return material @@ -109,7 +109,7 @@ class DrawingTexture { } let screenX = this.uvBased ? percentage.x * width : width * percentage.x; let screenY = this.uvBased ? ( 1 - percentage.y) * height : height * percentage.y; - // this.drawingCtx.drawImage(this.material.map.image, 0, 0); + this.drawingMesh.draw({ x: screenX, y: screenY }, mesh) this.material.map.needsUpdate = true; diff --git a/src/js/shot-generator/components/Three/Image.js b/src/js/shot-generator/components/Three/Image.js index be66fdfb66..6d92377e94 100644 --- a/src/js/shot-generator/components/Three/Image.js +++ b/src/js/shot-generator/components/Three/Image.js @@ -9,6 +9,7 @@ import RoundedBoxGeometryCreator from './../../../vendor/three-rounded-box' import { axis } from "../../../shared/IK/utils/TransformControls" import DrawingTexture from "./Helpers/drawing-on-texture" import KeyCommandsSingleton from '../KeyHandler/KeyCommandsSingleton' +import createRoundedPlane from './Helpers/create-rounded-plane' const RoundedBoxGeometry = RoundedBoxGeometryCreator(THREE) extend({RoundedBoxGeometry}) @@ -157,7 +158,7 @@ const Image = React.memo(({ sceneObject, isSelected, imagesPaths, ...props }) => rotation={ [rotation.x, rotation.y, rotation.z] } > - +
From fd0211cf4a4bd84133bc769f7545629584b3abdf Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Mon, 1 Jun 2020 11:43:04 +0300 Subject: [PATCH 11/88] Changed drawing mesh to global --- src/js/shared/reducers/shot-generator.js | 13 ++++++- src/js/shot-generator/SceneManagerR3fLarge.js | 5 ++- .../index.js | 37 +++++++++---------- .../components/InspectedElement/index.js | 4 +- .../shot-generator/components/Three/Image.js | 8 ++-- src/js/windows/shot-generator/window.js | 5 ++- 6 files changed, 42 insertions(+), 30 deletions(-) rename src/js/shot-generator/components/InspectedElement/{MeshInspector => BrushInspector}/index.js (60%) diff --git a/src/js/shared/reducers/shot-generator.js b/src/js/shared/reducers/shot-generator.js index b5c9e52c80..5fa0907415 100644 --- a/src/js/shared/reducers/shot-generator.js +++ b/src/js/shared/reducers/shot-generator.js @@ -57,7 +57,8 @@ const getSerializedState = state => { return { world: getWorld(state), sceneObjects, - activeCamera: getActiveCamera(state) + activeCamera: getActiveCamera(state), + drawingMesh: state.drawingMesh } } @@ -662,6 +663,8 @@ const initialState = { board: {}, + drawingMesh: { color: '#000000', size: 2}, + undoable: { world: initialScene.world, activeCamera: initialScene.activeCamera, @@ -1458,6 +1461,12 @@ const mainReducer = (state/* = initialState*/, action) => { case 'ATTACHMENTS_DELETE': delete draft.attachments[action.payload.id] return + case 'UPDATE_DRAWING_MESH': + if(!action.payload) return + draft.drawingMesh.color = action.payload.color ? action.payload.color : draft.drawingMesh.color + draft.drawingMesh.size = action.payload.size ? action.payload.size : draft.drawingMesh.size + draft.drawingMesh.type = action.payload.type ? action.payload.type : draft.drawingMesh.type + return case 'UNDO_GROUP_START': batchGroupBy.start(action.payload) @@ -1692,7 +1701,7 @@ module.exports = { deleteScenePreset: id => ({ type: 'DELETE_SCENE_PRESET', payload: { id } }), createCharacterPreset: payload => ({ type: 'CREATE_CHARACTER_PRESET', payload }), - + updateDrawingMesh: payload => ({ type: 'UPDATE_DRAWING_MESH', payload}), createPosePreset: payload => ({ type: 'CREATE_POSE_PRESET', payload }), createHandPosePreset: payload => ({ type: 'CREATE_HAND_POSE_PRESET', payload }), updatePosePreset: (id, values) => ({ type: 'UPDATE_POSE_PRESET', payload: { id, ...values} }), diff --git a/src/js/shot-generator/SceneManagerR3fLarge.js b/src/js/shot-generator/SceneManagerR3fLarge.js index 927db1a07c..516d3be0d6 100644 --- a/src/js/shot-generator/SceneManagerR3fLarge.js +++ b/src/js/shot-generator/SceneManagerR3fLarge.js @@ -69,7 +69,8 @@ const SceneManagerR3fLarge = connect( models: state.models, selectedBone: getSelectedBone(state), cameraShots: state.cameraShots, - selectedAttachable: getSelectedAttachable(state) + selectedAttachable: getSelectedAttachable(state), + drawingMesh: state.drawingMesh }), { selectObject, @@ -94,6 +95,7 @@ const SceneManagerR3fLarge = connect( models, updateObjects, selectedBone, + drawingMesh, cameraShots, setLargeCanvasData, @@ -407,6 +409,7 @@ const SceneManagerR3fLarge = connect( isSelected={ selections.includes(id) } updateObject={ updateObject } objectRotationControl={ objectRotationControl.current } + drawingMesh={ drawingMesh } /> }) diff --git a/src/js/shot-generator/components/InspectedElement/MeshInspector/index.js b/src/js/shot-generator/components/InspectedElement/BrushInspector/index.js similarity index 60% rename from src/js/shot-generator/components/InspectedElement/MeshInspector/index.js rename to src/js/shot-generator/components/InspectedElement/BrushInspector/index.js index 3e4f2bde0b..e135521114 100644 --- a/src/js/shot-generator/components/InspectedElement/MeshInspector/index.js +++ b/src/js/shot-generator/components/InspectedElement/BrushInspector/index.js @@ -4,38 +4,35 @@ import ColorSelect from '../../ColorSelect' import { getSelections, getSceneObjects, - updateObject - } from './../../../../shared/reducers/shot-generator' + updateObject, + updateDrawingMesh + } from '../../../../shared/reducers/shot-generator' import {formatters, NumberSlider, transforms, textFormatters, textConstraints} from '../../NumberSlider' -import deepEqualSelector from './../../../../utils/deepEqualSelector' +import deepEqualSelector from '../../../../utils/deepEqualSelector' import MeshType from '../../Three/Helpers/Meshes/TextureMeshTypes' -const getObjectData = deepEqualSelector([getSelections, getSceneObjects], (selections, sceneObjects) => { - return sceneObjects[selections[0]] -}) -const MeshInspector = connect((state) => ({ - sceneObject: getObjectData(state) +const BrushInspector = connect((state) => ({ + drawingMesh: state.drawingMesh }), { - updateObject + updateDrawingMesh } )( React.memo(({ - updateObject, - sceneObject + updateDrawingMesh, + drawingMesh }) => { - console.log(sceneObject) const setSize = (value) => { - updateObject(sceneObject.id, {mesh: {...sceneObject.mesh, size: value}}) + updateDrawingMesh({ size: value }) } const setColor = (value) => { - updateObject(sceneObject.id, {mesh: {...sceneObject.mesh, color: value}}) + updateDrawingMesh({ color: value }) } const setType = (event) => { - updateObject(sceneObject.id, {mesh: {...sceneObject.mesh, type: event.target.value}}) + updateDrawingMesh({ type: event.target.value }) } return ( @@ -43,7 +40,7 @@ React.memo(({
Type
- { Object.values(MeshType).map((preset, index) => + { Object.values(BrushType).map((preset, index) => )}
- {drawingMesh.type !== MeshType.ERASER && }
Clean Selected Image
diff --git a/src/js/shot-generator/components/Three/Helpers/Meshes/Mesh.js b/src/js/shot-generator/components/Three/Helpers/Brushes/Brush.js similarity index 80% rename from src/js/shot-generator/components/Three/Helpers/Meshes/Mesh.js rename to src/js/shot-generator/components/Three/Helpers/Brushes/Brush.js index a195a742c5..6ab414dc5b 100644 --- a/src/js/shot-generator/components/Three/Helpers/Meshes/Mesh.js +++ b/src/js/shot-generator/components/Three/Helpers/Brushes/Brush.js @@ -1,4 +1,4 @@ -class Mesh { +class Brush { constructor(drawingCtx) { this.drawingCtx = drawingCtx; this.resetMeshPos(); @@ -12,19 +12,19 @@ class Mesh { this.prevPos = null } - draw(currentPos, mesh) { + draw(currentPos, brush) { if(!this.prevPos) { this.prevPos = {} this.prevPos.x = currentPos.x; this.prevPos.y = currentPos.y; } - this.brushSize = mesh.size; + this.brushSize = brush.size; let { width, height } = this.drawingCtx.canvas; if(this.percentageBasedSize) { let smallerSide = width > height ? height : width; - let sizePercent = mesh.size / this.defaultHeight; + let sizePercent = brush.size / this.defaultHeight; this.brushSize = smallerSide * sizePercent; } } } -export default Mesh +export default Brush diff --git a/src/js/shot-generator/components/Three/Helpers/Meshes/EraserMesh.js b/src/js/shot-generator/components/Three/Helpers/Brushes/EraserBrush.js similarity index 85% rename from src/js/shot-generator/components/Three/Helpers/Meshes/EraserMesh.js rename to src/js/shot-generator/components/Three/Helpers/Brushes/EraserBrush.js index 3ff2ff90dd..97b59b69c3 100644 --- a/src/js/shot-generator/components/Three/Helpers/Meshes/EraserMesh.js +++ b/src/js/shot-generator/components/Three/Helpers/Brushes/EraserBrush.js @@ -1,11 +1,11 @@ -import Mesh from './Mesh' -class EraserMesh extends Mesh { +import Brush from './Brush' +class EraserBrush extends Brush { constructor(drawingCtx) { super(drawingCtx); } - draw(currentPos, mesh) { - super.draw(currentPos, mesh); + draw(currentPos, brush) { + super.draw(currentPos, brush); this.drawingCtx.fillStyle = 'white'; let circle = new Path2D(); @@ -34,4 +34,4 @@ class EraserMesh extends Mesh { } } -export default EraserMesh; \ No newline at end of file +export default EraserBrush; \ No newline at end of file diff --git a/src/js/shot-generator/components/Three/Helpers/Meshes/SimpleMesh.js b/src/js/shot-generator/components/Three/Helpers/Brushes/SimpleBrush.js similarity index 79% rename from src/js/shot-generator/components/Three/Helpers/Meshes/SimpleMesh.js rename to src/js/shot-generator/components/Three/Helpers/Brushes/SimpleBrush.js index b13906ce0c..b661fc91f5 100644 --- a/src/js/shot-generator/components/Three/Helpers/Meshes/SimpleMesh.js +++ b/src/js/shot-generator/components/Three/Helpers/Brushes/SimpleBrush.js @@ -1,15 +1,15 @@ -import Mesh from './Mesh' -class SimpleMesh extends Mesh { +import Brush from './Brush' +class SimpleBrush extends Brush { constructor(drawingCtx) { super(drawingCtx); // this.drawingCtx.lineJoin = true; } - draw(currentPos, mesh) { - super.draw(currentPos, mesh); - this.drawingCtx.strokeStyle = mesh.color; - this.drawingCtx.fillStyle = mesh.color; + draw(currentPos, brush) { + super.draw(currentPos, brush); + this.drawingCtx.strokeStyle = brush.color; + this.drawingCtx.fillStyle = brush.color; this.drawingCtx.lineWidth = this.brushSize; let circle = new Path2D(); @@ -38,4 +38,4 @@ class SimpleMesh extends Mesh { } } -export default SimpleMesh \ No newline at end of file +export default SimpleBrush; \ No newline at end of file diff --git a/src/js/shot-generator/components/Three/Helpers/Meshes/TextureMeshTypes.js b/src/js/shot-generator/components/Three/Helpers/Brushes/TextureBrushTypes.js similarity index 50% rename from src/js/shot-generator/components/Three/Helpers/Meshes/TextureMeshTypes.js rename to src/js/shot-generator/components/Three/Helpers/Brushes/TextureBrushTypes.js index 312e860de9..cfc2329015 100644 --- a/src/js/shot-generator/components/Three/Helpers/Meshes/TextureMeshTypes.js +++ b/src/js/shot-generator/components/Three/Helpers/Brushes/TextureBrushTypes.js @@ -1,5 +1,5 @@ -const MeshType = { +const BrushType = { SIMPLE: "Simple", ERASER: "Eraser" } -export default MeshType \ No newline at end of file +export default BrushType \ No newline at end of file diff --git a/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js b/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js index e78a15cf33..92688e6b87 100644 --- a/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js +++ b/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js @@ -19,7 +19,7 @@ class SimpleTexture extends DrawingTexture { setMesh(type) { super.setMesh(type); if(this.drawingCtxes[0]) { - this.drawingMesh.drawingCtx = this.drawingCtxes[0]; + this.drawingBrush.drawingCtx = this.drawingCtxes[0]; } } @@ -32,7 +32,7 @@ class SimpleTexture extends DrawingTexture { this.material = material; material.map.needsUpdate = true; material.needsUpdate = true; - this.drawingMesh.drawingCtx = this.drawingCtxes[0]; + this.drawingBrush.drawingCtx = this.drawingCtxes[0]; return material; } @@ -47,12 +47,12 @@ class SimpleTexture extends DrawingTexture { this.material.needsUpdate = true; } - draw (mousePosition, object, camera, mesh){ - let intersection = super.draw(mousePosition, object, camera, mesh) + draw (mousePosition, object, camera, brush){ + let intersection = super.draw(mousePosition, object, camera, brush) if(!intersection) return let screenX = (intersection.uv.x) * this.texture.image.width; let screenY = (1 - intersection.uv.y) * this.texture.image.height; - this.drawingMesh.draw({ x: screenX, y: screenY }, mesh) + this.drawingBrush.draw({ x: screenX, y: screenY }, brush) this.texture.needsUpdate = true; diff --git a/src/js/shot-generator/components/Three/SceneBackground.js b/src/js/shot-generator/components/Three/SceneBackground.js index ae55e44134..b494675d4a 100644 --- a/src/js/shot-generator/components/Three/SceneBackground.js +++ b/src/js/shot-generator/components/Three/SceneBackground.js @@ -5,7 +5,6 @@ import CubeMapDrawingTexture from './helpers/cubeMapDrawingTexture' import CubeTextureCreator from './helpers/CubeTextureCreator' import fs from 'fs-extra' import path from 'path' -const cubeTextureCreator = new CubeTextureCreator() const SceneBackground = React.memo(({ imagePath, world, storyboarderFilePath, updateWorld, drawingSceneTexture }) => { const texturePath = useRef() @@ -13,6 +12,7 @@ const SceneBackground = React.memo(({ imagePath, world, storyboarderFilePath, up const { asset: gltf } = useAsset(!scene.userData.tempPath ? imagePath[0] : imagePath[0].includes(scene.userData.tempPath ) ? null : imagePath[0]) const intersectionBox = useRef() const intersectionCamera = useRef() + const cubeTextureCreator = useRef( new CubeTextureCreator()) useEffect(() => { drawingSceneTexture.texture = new CubeMapDrawingTexture() @@ -33,13 +33,13 @@ const SceneBackground = React.memo(({ imagePath, world, storyboarderFilePath, up scene.background = new THREE.Color(world.backgroundColor) }, [world.backgroundColor]) - const draw = (mousePos, camera, drawingMesh) => { + const draw = (mousePos, camera, drawingBrush) => { drawingSceneTexture.texture.createMaterial(scene.background); intersectionCamera.current.copy(camera) intersectionCamera.current.position.set(0, 0, 0) intersectionCamera.current.quaternion.copy(camera.worldQuaternion()) intersectionCamera.current.updateMatrixWorld(true) - drawingSceneTexture.texture.draw(mousePos, intersectionBox.current, intersectionCamera.current, drawingMesh) + drawingSceneTexture.texture.draw(mousePos, intersectionBox.current, intersectionCamera.current, drawingBrush) } useMemo(() => { @@ -51,7 +51,7 @@ const SceneBackground = React.memo(({ imagePath, world, storyboarderFilePath, up scene.userData.tempPath = null } if(gltf instanceof THREE.Texture) { - cubeTexture = cubeTextureCreator.getCubeMapTexture(gltf, storyboarderFilePath); + cubeTexture = cubeTextureCreator.current.getCubeMapTexture(gltf, storyboarderFilePath); } if(cubeTexture) { @@ -65,7 +65,7 @@ const SceneBackground = React.memo(({ imagePath, world, storyboarderFilePath, up scene.userData.tempPath = null } let tempFileName = `temp_scenetexture-${Date.now()}.png` - cubeTextureCreator.saveCubeMapTexture(imagePath[0], scene.background, tempFileName) + cubeTextureCreator.current.saveCubeMapTexture(imagePath[0], scene.background, tempFileName) updateWorld({sceneTexture: 'models/sceneTextures/' + tempFileName}) scene.userData.tempPath = tempFileName texturePath.current = tempFileName diff --git a/src/js/shot-generator/components/Three/helpers/DrawingTexture.js b/src/js/shot-generator/components/Three/helpers/DrawingTexture.js index 3841c4c726..baf105f4cf 100644 --- a/src/js/shot-generator/components/Three/helpers/DrawingTexture.js +++ b/src/js/shot-generator/components/Three/helpers/DrawingTexture.js @@ -1,32 +1,32 @@ import * as THREE from 'three' -import SimpleMesh from './Meshes/SimpleMesh' -import EraserMesh from './Meshes/EraserMesh' +import SimpleBrush from './Brushes/SimpleBrush' +import EraserBrush from './Brushes/EraserBrush' class DrawingTexture { constructor() { this.drawingCanvases = []; this.drawingCtxes = []; this.raycaster = new THREE.Raycaster(); this.texture = null; - this.drawingMesh = null; + this.drawingBrush = null; this.isChanged = false; this.setMesh(); } resetMeshPos() { - this.drawingMesh.resetMeshPos(); + this.drawingBrush.resetMeshPos(); } setMesh(type) { switch(type) { case "Simple": - this.drawingMesh = new SimpleMesh(); + this.drawingBrush = new SimpleBrush(); break; case "Eraser": - this.drawingMesh = new EraserMesh(); + this.drawingBrush = new EraserBrush(); break; default: - this.drawingMesh = new SimpleMesh(); + this.drawingBrush = new SimpleBrush(); } } @@ -66,7 +66,7 @@ class DrawingTexture { return intersects.length && intersects[0]; } - draw (mousePosition, object, camera, mesh) { + draw (mousePosition, object, camera, brush) { let intersection = this.intersectImage(mousePosition.x, mousePosition.y, object, camera); if(intersection.uv === null) { diff --git a/src/js/shot-generator/components/Three/helpers/cubeMapDrawingTexture.js b/src/js/shot-generator/components/Three/helpers/cubeMapDrawingTexture.js index 0966fb74c3..91819d1a68 100644 --- a/src/js/shot-generator/components/Three/helpers/cubeMapDrawingTexture.js +++ b/src/js/shot-generator/components/Three/helpers/cubeMapDrawingTexture.js @@ -39,9 +39,9 @@ class CubeMapDrawingTexture extends DrawingTexture { this.texture.needsUpdate = true; } - draw (mousePosition, object, camera, mesh){ + draw (mousePosition, object, camera, brush){ - let intersection = super.draw(mousePosition, object, camera, mesh) + let intersection = super.draw(mousePosition, object, camera, brush) if(!intersection) return; let index ; if(intersection.face.normal.x) { @@ -61,8 +61,8 @@ class CubeMapDrawingTexture extends DrawingTexture { let screenX = (1 - intersection.uv.x) * this.texture.image[index].width; let screenY = (1 - intersection.uv.y) * this.texture.image[index].height; let drawingContext = this.drawingCtxes[index]; - this.drawingMesh.drawingCtx = drawingContext; - this.drawingMesh.draw({ x: screenX, y: screenY }, mesh); + this.drawingBrush.drawingCtx = drawingContext; + this.drawingBrush.draw({ x: screenX, y: screenY }, brush); this.texture.needsUpdate = true; } diff --git a/src/js/windows/shot-generator/window.js b/src/js/windows/shot-generator/window.js index d2c70b2037..31b688cc70 100644 --- a/src/js/windows/shot-generator/window.js +++ b/src/js/windows/shot-generator/window.js @@ -188,7 +188,7 @@ ipcRenderer.on('shot-generator:reload', async (event) => { const { board } = await service.getStoryboarderState() let aspectRatio = parseFloat(boardData.aspectRatio) - let drawingMesh = board.sg ? board.sg.data.drawingMesh : null + let drawingBrush = board.sg ? board.sg.data.drawingBrush : null store.dispatch({ type: 'SET_META_STORYBOARDER_FILE_PATH', payload: storyboarderFilePath @@ -199,7 +199,7 @@ ipcRenderer.on('shot-generator:reload', async (event) => { }) store.dispatch({ type: 'UPDATE_DRAWING_MESH', - payload: drawingMesh + payload: drawingBrush }) shotExplorer.createWindow(() => { shotExplorer.getWindow().webContents.send('shot-generator:open:shot-explorer') From cd21ba370c88f3dc5a0a3acaea2b0776216f337f Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Fri, 19 Jun 2020 14:28:12 +0300 Subject: [PATCH 41/88] Fixed cube map uv jumps on face end --- src/js/shot-generator/SceneManagerR3fLarge.js | 5 ++--- .../components/Three/helpers/cubeMapDrawingTexture.js | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/js/shot-generator/SceneManagerR3fLarge.js b/src/js/shot-generator/SceneManagerR3fLarge.js index 0b16f0e74f..f49eec8b8e 100644 --- a/src/js/shot-generator/SceneManagerR3fLarge.js +++ b/src/js/shot-generator/SceneManagerR3fLarge.js @@ -213,15 +213,14 @@ const SceneManagerR3fLarge = connect( if(!intersections.length && drawingSceneTexture.current.draw) { let texture = drawingSceneTexture.current texture.draw({x, y}, camera, drawingBrush) - } else if( drawingSceneTexture.current.texture.isChanged ) { + } else { drawingSceneTexture.current.texture.resetMeshPos() } for(let i = 0; i < keys.length; i++) { let key = keys[i] let drawingTexture = drawingTextures.current[key] if(!intersections.length) { - if(drawingTexture.isChanged) - drawingTexture.resetMeshPos() + drawingTexture.resetMeshPos() continue; } let object = imageObjects.find((obj) => obj.userData.id === key) diff --git a/src/js/shot-generator/components/Three/helpers/cubeMapDrawingTexture.js b/src/js/shot-generator/components/Three/helpers/cubeMapDrawingTexture.js index 91819d1a68..6c92fb11cb 100644 --- a/src/js/shot-generator/components/Three/helpers/cubeMapDrawingTexture.js +++ b/src/js/shot-generator/components/Three/helpers/cubeMapDrawingTexture.js @@ -43,7 +43,7 @@ class CubeMapDrawingTexture extends DrawingTexture { let intersection = super.draw(mousePosition, object, camera, brush) if(!intersection) return; - let index ; + let index; if(intersection.face.normal.x) { let x = intersection.face.normal.x; index = x === -1 ? 0 : 1; @@ -54,7 +54,7 @@ class CubeMapDrawingTexture extends DrawingTexture { let x = intersection.face.normal.z; index = x === -1 ? 5 : 4; } - if( this.prevFaceIndex && this.prevFaceIndex !== index ) { + if( this.prevFaceIndex !== null && this.prevFaceIndex !== index ) { this.resetMeshPos(); } this.prevFaceIndex = index; From b371b329baafd2e2736acf5967c0304a9b1efdbf Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Fri, 19 Jun 2020 16:28:05 +0300 Subject: [PATCH 42/88] Added hotkeys to go into drawing mode cmd+u --- src/js/menu.js | 9 ++++++++- src/js/shot-explorer/ShotExplorerSceneManager.js | 2 +- src/js/shot-generator/components/Editor/index.js | 13 +++++++++++-- .../InspectedElement/BrushInspector/index.js | 7 +++++++ src/js/windows/shot-generator/main.js | 4 ++++ 5 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/js/menu.js b/src/js/menu.js index 0a41ab3461..0f548e6930 100644 --- a/src/js/menu.js +++ b/src/js/menu.js @@ -928,7 +928,14 @@ const shotGeneratorMenu = [ click (item, focusedWindow, event) { ipcRenderer.send('shot-generator:menu:view:fps-meter') } - } + }, + { + accelerator: 'CommandOrControl+u', + label: 'Switch mode drawing/selecting', + click () { + ipcRenderer.send('shot-generator:menu:switchMode') + } + }, ] }, { diff --git a/src/js/shot-explorer/ShotExplorerSceneManager.js b/src/js/shot-explorer/ShotExplorerSceneManager.js index 6301f78d5e..616fbaab4b 100644 --- a/src/js/shot-explorer/ShotExplorerSceneManager.js +++ b/src/js/shot-explorer/ShotExplorerSceneManager.js @@ -218,7 +218,7 @@ const ShotExplorerSceneManager = connect( environment={world.environment} visible={world.environment.visible} /> } - { + { { + withState((dispatch, state) => { + dispatch(enableDrawMode(!state.isDrawingMode)) + }) + } useEffect(() => { ipcRenderer.on('shot-generator:menu:view:fps-meter', toggleStats) + ipcRenderer.on('shot-generator:menu:switchMode', switchMode) return () => { ipcRenderer.off('shot-generator:menu:view:fps-meter', toggleStats) + ipcRenderer.off('shot-generator:menu:switchMode', switchMode) } }, []) diff --git a/src/js/shot-generator/components/InspectedElement/BrushInspector/index.js b/src/js/shot-generator/components/InspectedElement/BrushInspector/index.js index cd578ee43c..f4fef6dbb8 100644 --- a/src/js/shot-generator/components/InspectedElement/BrushInspector/index.js +++ b/src/js/shot-generator/components/InspectedElement/BrushInspector/index.js @@ -11,6 +11,7 @@ import { } from '../../../../shared/reducers/shot-generator' import {formatters, NumberSlider, transforms, textFormatters, textConstraints} from '../../NumberSlider' import BrushType from '../../Three/Helpers/Brushes/TextureBrushTypes' +import { ipcRenderer } from 'electron' const BrushInspector = connect((state) => ({ drawingBrush: state.drawingBrush, @@ -30,10 +31,16 @@ React.memo(({ selections }) => { + const switchMode = () => { + enableDrawMode(true) + } + useEffect(() => { enableDrawMode(true) + ipcRenderer.on('shot-generator:menu:switchMode', switchMode) return () => { enableDrawMode(false) + ipcRenderer.off('shot-generator:menu:switchMode', switchMode) } }, []) diff --git a/src/js/windows/shot-generator/main.js b/src/js/windows/shot-generator/main.js index 39e7c52630..631ebd4715 100644 --- a/src/js/windows/shot-generator/main.js +++ b/src/js/windows/shot-generator/main.js @@ -129,6 +129,10 @@ ipcMain.on('shot-generator:menu:view:fps-meter', (event, value) => { win && win.webContents.send('shot-generator:menu:view:fps-meter', value) }) +ipcMain.on('shot-generator:menu:switchMode', (event) => { + win && win.webContents.send('shot-generator:menu:switchMode') +}) + ipcMain.on('shot-generator:object:duplicate', () => { win.webContents.send('shot-generator:object:duplicate') }) From 626e32626f447ce7a2b21bb1de5d68ff1f5332b6 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Mon, 22 Jun 2020 13:51:37 +0300 Subject: [PATCH 43/88] Implemented image compressing --- src/js/shot-generator/SceneManagerR3fLarge.js | 2 +- .../components/Three/Helpers/SimpleTexture.js | 4 +-- .../components/Three/SceneBackground.js | 2 +- .../Three/helpers/CubeTextureCreator.js | 24 ++++++------- .../Three/helpers/DrawingTexture.js | 3 +- .../helpers/saveDataURLtoFile.js | 1 + src/js/xr/src/SceneManagerXR.js | 1 - src/js/xr/src/components/SceneBackground.js | 35 ------------------- src/js/xr/src/hooks/use-assets-manager.js | 12 +++---- 9 files changed, 24 insertions(+), 60 deletions(-) delete mode 100644 src/js/xr/src/components/SceneBackground.js diff --git a/src/js/shot-generator/SceneManagerR3fLarge.js b/src/js/shot-generator/SceneManagerR3fLarge.js index f49eec8b8e..601f18afb8 100644 --- a/src/js/shot-generator/SceneManagerR3fLarge.js +++ b/src/js/shot-generator/SceneManagerR3fLarge.js @@ -258,7 +258,7 @@ const SceneManagerR3fLarge = connect( let object = scene.__interaction.find((obj) => obj.userData.id === key) if(drawingTextures.current[key].isChanged) { drawingTextures.current[key].isChanged = false - saveDataURLtoFile(drawingTextures.current[key].getImage(), storyboarderFilePath, updateObject, object) + saveDataURLtoFile(drawingTextures.current[key].getImage("image/png"), storyboarderFilePath, updateObject, object) } } if( drawingSceneTexture.current.save && drawingSceneTexture.current.texture.isChanged) { diff --git a/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js b/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js index 92688e6b87..4443a4a266 100644 --- a/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js +++ b/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js @@ -11,8 +11,8 @@ class SimpleTexture extends DrawingTexture { this.drawingCtxes.push(ctx); } - getImage() { - return super.getImage()[0]; + getImage(mime) { + return super.getImage(mime)[0]; } diff --git a/src/js/shot-generator/components/Three/SceneBackground.js b/src/js/shot-generator/components/Three/SceneBackground.js index b494675d4a..6f74baf926 100644 --- a/src/js/shot-generator/components/Three/SceneBackground.js +++ b/src/js/shot-generator/components/Three/SceneBackground.js @@ -64,7 +64,7 @@ const SceneBackground = React.memo(({ imagePath, world, storyboarderFilePath, up fs.remove(tempFile) scene.userData.tempPath = null } - let tempFileName = `temp_scenetexture-${Date.now()}.png` + let tempFileName = `temp_scenetexture-${Date.now()}.jpg` cubeTextureCreator.current.saveCubeMapTexture(imagePath[0], scene.background, tempFileName) updateWorld({sceneTexture: 'models/sceneTextures/' + tempFileName}) scene.userData.tempPath = tempFileName diff --git a/src/js/shot-generator/components/Three/helpers/CubeTextureCreator.js b/src/js/shot-generator/components/Three/helpers/CubeTextureCreator.js index ebabf76ca2..8287ec857b 100644 --- a/src/js/shot-generator/components/Three/helpers/CubeTextureCreator.js +++ b/src/js/shot-generator/components/Three/helpers/CubeTextureCreator.js @@ -32,15 +32,15 @@ class CubeTextureCreator { this.saveFace(croppedImage, element.x, element.y, element.width, element.height, element.name, this.boardPath ); } let {dir, ext, name} = path.parse(imagePath); - let dataUrl = this.drawingCtx.canvas.toDataURL(); + let dataUrl = this.drawingCtx.canvas.toDataURL("image/jpeg"); let properName = filename ? filename : name + ext; - saveDataURLtoFile(dataUrl, `${properName}`, 'models/sceneTextures/', this.boardPath); + saveDataURLtoFile(dataUrl, `${properName}`, 'models/sceneTextures', this.boardPath, true); } // Draw the specific mesh on drawingContext which contains original texture image saveFace( image, x, y, width, height, name, boardPath) { - this.drawingCtx.drawImage(image, x, y, width, height); - saveDataURLtoFile(image.toDataURL(), `${name}.png`, 'models/sceneTextures/cubetexture', boardPath, false); + this.drawingCtx.drawImage(image, x, y, width, height); + saveDataURLtoFile(image.toDataURL("image/jpeg"), `${name}.jpg`, 'models/sceneTextures/cubetexture', boardPath, true); } // Parses/crops passed cube texture and loading cube texture from them @@ -63,12 +63,12 @@ class CubeTextureCreator { return new THREE.CubeTextureLoader() .setPath( path.join(path.dirname(boardPath), 'models/sceneTextures/cubetexture/') ) .load( [ - 'px.png#' + new Date().getTime(), - 'nx.png#' + new Date().getTime(), - 'py.png#' + new Date().getTime(), - 'ny.png#' + new Date().getTime(), - 'pz.png#' + new Date().getTime(), - 'nz.png#' + new Date().getTime() + 'px.jpg#' + new Date().getTime(), + 'nx.jpg#' + new Date().getTime(), + 'py.jpg#' + new Date().getTime(), + 'ny.jpg#' + new Date().getTime(), + 'pz.jpg#' + new Date().getTime(), + 'nz.jpg#' + new Date().getTime() ]) } @@ -130,8 +130,8 @@ class CubeTextureCreator { this.drawingCtx.drawImage(image, 0, 0, image.width, image.height); var imageData = this.drawingCtx.getImageData(x, y, width, height); this.croppedCtx.putImageData(imageData, 0, 0); - let dataUrl = this.croppedCtx.canvas.toDataURL(); - saveDataURLtoFile(dataUrl, `${name}.png`, 'models/sceneTextures/cubetexture', boardPath); + let dataUrl = this.croppedCtx.canvas.toDataURL("image/jpeg"); + saveDataURLtoFile(dataUrl, `${name}.jpg`, 'models/sceneTextures/cubetexture', boardPath); } } diff --git a/src/js/shot-generator/components/Three/helpers/DrawingTexture.js b/src/js/shot-generator/components/Three/helpers/DrawingTexture.js index baf105f4cf..093128d38d 100644 --- a/src/js/shot-generator/components/Three/helpers/DrawingTexture.js +++ b/src/js/shot-generator/components/Three/helpers/DrawingTexture.js @@ -43,8 +43,7 @@ class DrawingTexture { this.texture.needsUpdate = true; } - getImage() { - let mime = "images/jpeg"; + getImage(mime) { let images = []; for( let i = 0; i < this.drawingCanvases.length; i++){ images.push(this.drawingCanvases[i].toDataURL(mime)); diff --git a/src/js/shot-generator/helpers/saveDataURLtoFile.js b/src/js/shot-generator/helpers/saveDataURLtoFile.js index e3ca6c718b..58dfa6718d 100644 --- a/src/js/shot-generator/helpers/saveDataURLtoFile.js +++ b/src/js/shot-generator/helpers/saveDataURLtoFile.js @@ -9,5 +9,6 @@ const saveDataURLtoFile = (dataURL, filename, type, boardPath, async = false) => } else { fs.writeFile(imageFilePath, imageData, 'base64') } + } export default saveDataURLtoFile; \ No newline at end of file diff --git a/src/js/xr/src/SceneManagerXR.js b/src/js/xr/src/SceneManagerXR.js index 679b890d00..f94e352e86 100644 --- a/src/js/xr/src/SceneManagerXR.js +++ b/src/js/xr/src/SceneManagerXR.js @@ -58,7 +58,6 @@ const Controller = require('./components/Controller') const TeleportTarget = require('./components/TeleportTarget') const { Log } = require('./components/Log') const SimpleErrorBoundary = require('./components/SimpleErrorBoundary') -const SceneBackground = require('./components/SceneBackground') const Controls = require('./components/ui/Controls') const Help = require('./components/ui/Help') diff --git a/src/js/xr/src/components/SceneBackground.js b/src/js/xr/src/components/SceneBackground.js deleted file mode 100644 index 8789fc38e9..0000000000 --- a/src/js/xr/src/components/SceneBackground.js +++ /dev/null @@ -1,35 +0,0 @@ -import React, { useEffect, useLayoutEffect, useRef, useCallback, useMemo } from 'react' -import { useThree } from 'react-three-fiber' - - -const SceneBackground = React.memo(({ texture, world }) => { - const { scene, camera, gl } = useThree() - const intersectionBox = useRef() - const intersectionCamera = useRef() - - useEffect(() => { - let geometry = new THREE.BoxBufferGeometry(1, 1, 1) - let material = new THREE.MeshBasicMaterial({ side: THREE.BackSide}) - intersectionBox.current = new THREE.Mesh(geometry, material) - intersectionCamera.current = camera.clone() - return () => { - intersectionBox.current.geometry.dispose() - intersectionBox.current.material.dispose() - if(scene.background instanceof THREE.Texture) { - scene.background.dispose() - } - } - }, []) - - useEffect(() => { - scene.background = new THREE.Color(world.backgroundColor) - }, [world.backgroundColor]) - - useMemo(() => { - if(!texture) return - - }, [texture]) - - return -}) -export default SceneBackground; \ No newline at end of file diff --git a/src/js/xr/src/hooks/use-assets-manager.js b/src/js/xr/src/hooks/use-assets-manager.js index caf8594b8f..760564204e 100644 --- a/src/js/xr/src/hooks/use-assets-manager.js +++ b/src/js/xr/src/hooks/use-assets-manager.js @@ -108,12 +108,12 @@ const useAssetsManager = () => { let info = path.dirname(id) cubeLoader.setPath(info + "/cubetexture/") load(cubeLoader, [ - 'px.png?ts=' + new Date().getTime(), - 'nx.png?ts=' + new Date().getTime(), - 'py.png?ts=' + new Date().getTime(), - 'ny.png?ts=' + new Date().getTime(), - 'pz.png?ts=' + new Date().getTime(), - 'nz.png?ts=' + new Date().getTime() + 'px.jpg?ts=' + new Date().getTime(), + 'nx.jpg?ts=' + new Date().getTime(), + 'py.jpg?ts=' + new Date().getTime(), + 'ny.jpg?ts=' + new Date().getTime(), + 'pz.jpg?ts=' + new Date().getTime(), + 'nz.jpg?ts=' + new Date().getTime() ], { onload: value => dispatch({ type: 'SUCCESS', payload: { id, value } }), onprogress: progress => dispatch({ type: 'PROGRESS', payload: { id, progress } }), From 47aba2794f5c11a94a4e69a2a60900223ea6f5c6 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Tue, 23 Jun 2020 11:25:36 +0300 Subject: [PATCH 44/88] Added tabs to the scene inspector --- .../components/ElementsPanel/Inspector.js | 7 ++- .../components/InspectedElement/index.js | 54 +++++++++---------- 2 files changed, 30 insertions(+), 31 deletions(-) diff --git a/src/js/shot-generator/components/ElementsPanel/Inspector.js b/src/js/shot-generator/components/ElementsPanel/Inspector.js index 1d5dadca7c..258ffe8925 100644 --- a/src/js/shot-generator/components/ElementsPanel/Inspector.js +++ b/src/js/shot-generator/components/ElementsPanel/Inspector.js @@ -1,4 +1,4 @@ -import InspectedWorld from '../InspectedWorld' + import InspectedElement from '../InspectedElement' import React, { useContext } from 'react' import MultiSelectionInspector from '../MultiSelectionInspector' @@ -14,9 +14,8 @@ const Inspector = ({ return
{(selectedCount > 1) ? - : (kind && data) - ? - : } + : + }
} export default Inspector diff --git a/src/js/shot-generator/components/InspectedElement/index.js b/src/js/shot-generator/components/InspectedElement/index.js index 9cca7cae9f..ad6db073e7 100644 --- a/src/js/shot-generator/components/InspectedElement/index.js +++ b/src/js/shot-generator/components/InspectedElement/index.js @@ -17,16 +17,16 @@ import PosePresetsInspector from './PosePresetsInspector/index' import ModelInspector from './ModelInspector/index' import AttachableInspector from './AttachableInspector/index' import BrushInspector from './BrushInspector' - +import InspectedWorld from '../InspectedWorld' import Icon from '../Icon' import Modal from '../Modal' const isChar = (type) => type === 'character' const isObj = (type) => type === 'object' -const isImage = (type) => type === 'image' +const isImage = (type) => type === 'image' || !type const nullTab = {tab: null, panel: null} -const Inspector = React.memo(({id, selectedName, selectedType, updateObject}) => { +const Inspector = React.memo(({id, selectedName, selectedType, updateObject, isInspectedWorld}) => { const [isModalShown, showModal] = useState(false) const [changedName, changeNameTo] = useState(false) const handPoseTab = useMemo(() => { @@ -75,31 +75,31 @@ const Inspector = React.memo(({id, selectedName, selectedType, updateObject}) => return ( - { isModalShown && showModal(false)}> -
- Select a Preset Name: -
-
- changeNameTo(value.currentTarget.value) }/> -
-
- -
+ { isModalShown && showModal(false)}> +
+ Select a Preset Name: +
+
+ changeNameTo(value.currentTarget.value) }/> +
+
+ +
} - showModal(true) }> + { !isInspectedWorld && showModal(true) }> {selectedName} Properties - + }
@@ -111,7 +111,7 @@ const Inspector = React.memo(({id, selectedName, selectedType, updateObject}) =>
- + {isInspectedWorld ? : } {handPoseTab.panel} {charPoseTab.panel} {modelTab.panel} From ea88693fe8a7940df8fa4366852b6b0aee6a18bb Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Tue, 23 Jun 2020 11:31:33 +0300 Subject: [PATCH 45/88] Removed hotkeys drawing mode switch --- src/js/menu.js | 9 +-------- src/js/shot-generator/components/Editor/index.js | 8 -------- .../components/InspectedElement/BrushInspector/index.js | 5 ----- .../shot-generator/components/InspectedElement/index.js | 2 +- src/js/windows/shot-generator/main.js | 4 ---- 5 files changed, 2 insertions(+), 26 deletions(-) diff --git a/src/js/menu.js b/src/js/menu.js index 0f548e6930..0a41ab3461 100644 --- a/src/js/menu.js +++ b/src/js/menu.js @@ -928,14 +928,7 @@ const shotGeneratorMenu = [ click (item, focusedWindow, event) { ipcRenderer.send('shot-generator:menu:view:fps-meter') } - }, - { - accelerator: 'CommandOrControl+u', - label: 'Switch mode drawing/selecting', - click () { - ipcRenderer.send('shot-generator:menu:switchMode') - } - }, + } ] }, { diff --git a/src/js/shot-generator/components/Editor/index.js b/src/js/shot-generator/components/Editor/index.js index 69f47b7f2e..4df759114f 100644 --- a/src/js/shot-generator/components/Editor/index.js +++ b/src/js/shot-generator/components/Editor/index.js @@ -88,18 +88,10 @@ const Editor = React.memo(({ } } - const switchMode = () => { - withState((dispatch, state) => { - dispatch(enableDrawMode(!state.isDrawingMode)) - }) - } - useEffect(() => { ipcRenderer.on('shot-generator:menu:view:fps-meter', toggleStats) - ipcRenderer.on('shot-generator:menu:switchMode', switchMode) return () => { ipcRenderer.off('shot-generator:menu:view:fps-meter', toggleStats) - ipcRenderer.off('shot-generator:menu:switchMode', switchMode) } }, []) diff --git a/src/js/shot-generator/components/InspectedElement/BrushInspector/index.js b/src/js/shot-generator/components/InspectedElement/BrushInspector/index.js index f4fef6dbb8..926fd66a89 100644 --- a/src/js/shot-generator/components/InspectedElement/BrushInspector/index.js +++ b/src/js/shot-generator/components/InspectedElement/BrushInspector/index.js @@ -31,16 +31,11 @@ React.memo(({ selections }) => { - const switchMode = () => { - enableDrawMode(true) - } useEffect(() => { enableDrawMode(true) - ipcRenderer.on('shot-generator:menu:switchMode', switchMode) return () => { enableDrawMode(false) - ipcRenderer.off('shot-generator:menu:switchMode', switchMode) } }, []) diff --git a/src/js/shot-generator/components/InspectedElement/index.js b/src/js/shot-generator/components/InspectedElement/index.js index ad6db073e7..6c8cce37e4 100644 --- a/src/js/shot-generator/components/InspectedElement/index.js +++ b/src/js/shot-generator/components/InspectedElement/index.js @@ -128,7 +128,7 @@ const getObjectInfo = (state) => { const object = getSceneObjects(state)[selected] if (!object) { - return null + return {} } return { diff --git a/src/js/windows/shot-generator/main.js b/src/js/windows/shot-generator/main.js index 631ebd4715..39e7c52630 100644 --- a/src/js/windows/shot-generator/main.js +++ b/src/js/windows/shot-generator/main.js @@ -129,10 +129,6 @@ ipcMain.on('shot-generator:menu:view:fps-meter', (event, value) => { win && win.webContents.send('shot-generator:menu:view:fps-meter', value) }) -ipcMain.on('shot-generator:menu:switchMode', (event) => { - win && win.webContents.send('shot-generator:menu:switchMode') -}) - ipcMain.on('shot-generator:object:duplicate', () => { win.webContents.send('shot-generator:object:duplicate') }) From d9ab5691e2fd0c0e73dd29ae7f67016ce158708f Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Tue, 23 Jun 2020 15:29:34 +0300 Subject: [PATCH 46/88] Imeplemented simple texture for background --- src/js/shared/reducers/shot-generator.js | 3 + src/js/shot-generator/SceneManagerR3fLarge.js | 6 +- .../components/FileInput/index.js | 3 +- .../InspectedWorld/SceneTextureType.js | 6 + .../components/InspectedWorld/index.js | 27 ++-- .../components/Three/SceneBackground.js | 116 ++++++++++++------ 6 files changed, 115 insertions(+), 46 deletions(-) create mode 100644 src/js/shot-generator/components/InspectedWorld/SceneTextureType.js diff --git a/src/js/shared/reducers/shot-generator.js b/src/js/shared/reducers/shot-generator.js index de7f20fcfe..626cc127c3 100644 --- a/src/js/shared/reducers/shot-generator.js +++ b/src/js/shared/reducers/shot-generator.js @@ -1258,6 +1258,9 @@ const worldReducer = (state = initialState.undoable.world, action) => { if (action.payload.hasOwnProperty('sceneTexture')) { draft.sceneTexture = action.payload.sceneTexture } + if (action.payload.hasOwnProperty('textureType')) { + draft.textureType = action.payload.textureType + } return case 'UPDATE_WORLD_ROOM': diff --git a/src/js/shot-generator/SceneManagerR3fLarge.js b/src/js/shot-generator/SceneManagerR3fLarge.js index 601f18afb8..ad8e397482 100644 --- a/src/js/shot-generator/SceneManagerR3fLarge.js +++ b/src/js/shot-generator/SceneManagerR3fLarge.js @@ -213,7 +213,7 @@ const SceneManagerR3fLarge = connect( if(!intersections.length && drawingSceneTexture.current.draw) { let texture = drawingSceneTexture.current texture.draw({x, y}, camera, drawingBrush) - } else { + } else if(drawingSceneTexture.current.texture) { drawingSceneTexture.current.texture.resetMeshPos() } for(let i = 0; i < keys.length; i++) { @@ -243,7 +243,7 @@ const SceneManagerR3fLarge = connect( let key = keys[i] drawingTextures.current[key].setMesh(drawingBrush.type) } - if(drawingSceneTexture.current) + if(drawingSceneTexture.current && drawingSceneTexture.current.texture) drawingSceneTexture.current.texture.setMesh(drawingBrush.type) }, [drawingBrush.type]) @@ -261,7 +261,7 @@ const SceneManagerR3fLarge = connect( saveDataURLtoFile(drawingTextures.current[key].getImage("image/png"), storyboarderFilePath, updateObject, object) } } - if( drawingSceneTexture.current.save && drawingSceneTexture.current.texture.isChanged) { + if( drawingSceneTexture.current.save && drawingSceneTexture.current.texture.isChanged) { drawingSceneTexture.current.texture.isChanged = false drawingSceneTexture.current.texture.resetMeshPos() drawingSceneTexture.current.save() diff --git a/src/js/shot-generator/components/FileInput/index.js b/src/js/shot-generator/components/FileInput/index.js index 81e3096ebf..a109a656d5 100644 --- a/src/js/shot-generator/components/FileInput/index.js +++ b/src/js/shot-generator/components/FileInput/index.js @@ -8,6 +8,7 @@ const FileInput = React.memo(({ label, onChange, wrapperClassName="input-group", refClassName="file-input", + filters = [], ...props }) => { const onFileSelect = useCallback((e) => { @@ -16,7 +17,7 @@ const FileInput = React.memo(({ return false } - let filepaths = dialog.showOpenDialog(null, {}) + let filepaths = dialog.showOpenDialog(null, {filters}) if (filepaths) { onChange({ diff --git a/src/js/shot-generator/components/InspectedWorld/SceneTextureType.js b/src/js/shot-generator/components/InspectedWorld/SceneTextureType.js new file mode 100644 index 0000000000..341b6878ca --- /dev/null +++ b/src/js/shot-generator/components/InspectedWorld/SceneTextureType.js @@ -0,0 +1,6 @@ +const SceneTextureType = { + CubeMap: "Cubemap", + Image: "ImageTexture" +} + +export default SceneTextureType; diff --git a/src/js/shot-generator/components/InspectedWorld/index.js b/src/js/shot-generator/components/InspectedWorld/index.js index e8c720f6d3..e37438994a 100644 --- a/src/js/shot-generator/components/InspectedWorld/index.js +++ b/src/js/shot-generator/components/InspectedWorld/index.js @@ -22,8 +22,9 @@ import { } from './../../../shared/reducers/shot-generator' import deepEqualSelector from './../../../utils/deepEqualSelector' -import CopyFile from "../../utils/CopyFile" -const isImageExtension = ( ext ) => [".jpg", ".jpeg", ".png", ".gif", ".dds"].includes(ext) +import CopyFile from '../../utils/CopyFile' +import SceneTextureType from './SceneTextureType' +const imageFilters = ["jpg", "jpeg", "png", "gif", "dds"] const InspectedWorld = React.memo(({updateObject, updateWorld, updateWorldRoom, updateWorldEnvironment, updateWorldFog, world, storyboarderFilePath}) => { const setGround = useCallback(() => updateWorld({ground: !world.ground}), [world.ground]) @@ -51,10 +52,13 @@ const InspectedWorld = React.memo(({updateObject, updateWorld, updateWorldRoom, const setSceneTextureFile = useCallback((event) => { if (event.file) { - const { ext } = path.parse(event.file); - if(isImageExtension(ext)) { - updateWorld({sceneTexture: CopyFile(storyboarderFilePath, event.file, 'sceneTexture')}) - } + updateWorld({textureType: SceneTextureType.Image, sceneTexture: CopyFile(storyboarderFilePath, event.file, 'sceneTexture')}) + } + }, []) + + const setSceneCubeMap = useCallback((event) => { + if (event.file) { + updateWorld({textureType: SceneTextureType.CubeMap, sceneTexture: CopyFile(storyboarderFilePath, event.file, 'sceneTexture')}) } }, []) @@ -114,11 +118,20 @@ const InspectedWorld = React.memo(({updateObject, updateWorld, updateWorldRoom, onSetValue={setBackground} />
} - + } + {(!world.textureType || world.textureType === SceneTextureType.Image) && + }
Room
diff --git a/src/js/shot-generator/components/Three/SceneBackground.js b/src/js/shot-generator/components/Three/SceneBackground.js index 6f74baf926..7bc4eec06f 100644 --- a/src/js/shot-generator/components/Three/SceneBackground.js +++ b/src/js/shot-generator/components/Three/SceneBackground.js @@ -1,84 +1,130 @@ +import * as THREE from 'three' import React, { useEffect, useLayoutEffect, useRef, useCallback, useMemo } from 'react' import { useThree } from 'react-three-fiber' import { useAsset } from '../../hooks/use-assets-manager' import CubeMapDrawingTexture from './helpers/cubeMapDrawingTexture' +import SimpleTexture from './helpers/SimpleTexture' import CubeTextureCreator from './helpers/CubeTextureCreator' import fs from 'fs-extra' import path from 'path' +import SceneTextureType from '../InspectedWorld/SceneTextureType' const SceneBackground = React.memo(({ imagePath, world, storyboarderFilePath, updateWorld, drawingSceneTexture }) => { const texturePath = useRef() const { scene, camera, gl } = useThree() - const { asset: gltf } = useAsset(!scene.userData.tempPath ? imagePath[0] : imagePath[0].includes(scene.userData.tempPath ) ? null : imagePath[0]) + const { asset: texture } = useAsset(!scene.userData.tempPath ? imagePath[0] : imagePath[0].includes(scene.userData.tempPath ) ? null : imagePath[0]) const intersectionBox = useRef() const intersectionCamera = useRef() const cubeTextureCreator = useRef( new CubeTextureCreator()) useEffect(() => { - drawingSceneTexture.texture = new CubeMapDrawingTexture() - let geometry = new THREE.BoxBufferGeometry(1, 1, 1) - let material = new THREE.MeshBasicMaterial({ side: THREE.BackSide}) - intersectionBox.current = new THREE.Mesh(geometry, material) - intersectionCamera.current = camera.clone() return () => { - intersectionBox.current.geometry.dispose() - intersectionBox.current.material.dispose() if(scene.background instanceof THREE.Texture) { scene.background.dispose() } } }, []) + useEffect(() => { + if(world.textureType === SceneTextureType.CubeMap) { + drawingSceneTexture.texture = new CubeMapDrawingTexture() + let geometry = new THREE.BoxBufferGeometry(1, 1, 1) + let material = new THREE.MeshBasicMaterial({ side: THREE.DoubleSide}) + intersectionBox.current = new THREE.Mesh(geometry, material) + intersectionCamera.current = camera.clone() + + } else if(world.textureType === SceneTextureType.Image) { + drawingSceneTexture.texture = new SimpleTexture() + let geometry = new THREE.PlaneBufferGeometry(1, 1) + let material = new THREE.MeshBasicMaterial({ side: THREE.DoubleSide}) + intersectionBox.current = new THREE.Mesh(geometry, material) + intersectionBox.current.position.set(0, 0, -1) + intersectionBox.current.updateMatrixWorld(true) + intersectionCamera.current = new THREE.OrthographicCamera(-1, 1, 1, 1, 1, 1000) + intersectionCamera.current.add(intersectionBox.current) + } + return () => { + if(intersectionBox.current) { + intersectionBox.current.geometry.dispose() + intersectionBox.current.material.dispose() + } + intersectionCamera.current = null + intersectionBox.current = null + } + }, [world.textureType]) + useEffect(() => { scene.background = new THREE.Color(world.backgroundColor) }, [world.backgroundColor]) const draw = (mousePos, camera, drawingBrush) => { - drawingSceneTexture.texture.createMaterial(scene.background); - intersectionCamera.current.copy(camera) - intersectionCamera.current.position.set(0, 0, 0) + if(world.textureType === SceneTextureType.CubeMap) { + drawingSceneTexture.texture.createMaterial(scene.background); + intersectionCamera.current.copy(camera) + intersectionCamera.current.position.set(0, 0, 0) + } intersectionCamera.current.quaternion.copy(camera.worldQuaternion()) intersectionCamera.current.updateMatrixWorld(true) + drawingSceneTexture.texture.draw(mousePos, intersectionBox.current, intersectionCamera.current, drawingBrush) } - useMemo(() => { - if(!gltf) return - let cubeTexture; + const cleanUpTempFile = () => { if(scene.userData.tempPath) { let tempFile = path.join(path.dirname(storyboarderFilePath), 'models/sceneTextures/', scene.userData.tempPath) fs.remove(tempFile) scene.userData.tempPath = null } - if(gltf instanceof THREE.Texture) { - cubeTexture = cubeTextureCreator.current.getCubeMapTexture(gltf, storyboarderFilePath); - } + } - if(cubeTexture) { - scene.background = cubeTexture; - scene.userData.texturePath = imagePath[0] - drawingSceneTexture.draw = draw - drawingSceneTexture.save = () => { - if(scene.userData.tempPath) { - let tempFile = path.join(path.dirname(storyboarderFilePath), 'models/sceneTextures/', scene.userData.tempPath) - fs.remove(tempFile) - scene.userData.tempPath = null + useEffect(() => { + if(!texture) return + cleanUpTempFile() + let backgroundTexture + if(world.textureType === SceneTextureType.CubeMap) { + backgroundTexture = cubeTextureCreator.current.getCubeMapTexture(texture, storyboarderFilePath); + if(backgroundTexture) { + drawingSceneTexture.save = () => { + cleanUpTempFile() + let tempFileName = `temp_scenetexture-${Date.now()}.jpg` + cubeTextureCreator.current.saveCubeMapTexture(imagePath[0], scene.background, tempFileName) + updateWorld({sceneTexture: 'models/sceneTextures/' + tempFileName}) + scene.userData.tempPath = tempFileName + texturePath.current = tempFileName } + } + } else if(world.textureType === SceneTextureType.Image) { + texture.wrapS = texture.wrapT = THREE.RepeatWrapping + texture.offset.set(0, 0) + texture.repeat.set(1, 1) + const { width, height } = texture.image + let aspect = width / height + intersectionCamera.current.left = 1 * aspect / -2 + intersectionCamera.current.right = 1 * aspect / 2 + intersectionCamera.current.top = 1 /2 + intersectionCamera.current.bottom = 1 / -2 + intersectionCamera.current.updateProjectionMatrix() + intersectionBox.current.scale.set(1 * aspect, 1, 1) + intersectionBox.current.updateMatrixWorld(true) + backgroundTexture = drawingSceneTexture.texture.createMaterial({map: texture}).map + drawingSceneTexture.texture.setTexture(texture) + drawingSceneTexture.save = () => { + cleanUpTempFile() let tempFileName = `temp_scenetexture-${Date.now()}.jpg` - cubeTextureCreator.current.saveCubeMapTexture(imagePath[0], scene.background, tempFileName) + let imageData = drawingSceneTexture.texture.getImage("image/png") + + let imageFilePath = path.join(path.dirname(storyboarderFilePath), 'models/sceneTextures', tempFileName) + fs.writeFileSync(imageFilePath, imageData, 'base64') + updateWorld({sceneTexture: 'models/sceneTextures/' + tempFileName}) scene.userData.tempPath = tempFileName texturePath.current = tempFileName } - } else { - if(scene.background instanceof THREE.CubeTexture) { - scene.background.dispose(); - gltf.dispose(); - scene.background = null; - } - updateWorld({sceneTexture:null}); } - }, [gltf]) + scene.userData.texturePath = imagePath[0] + scene.background = backgroundTexture; + drawingSceneTexture.draw = draw + }, [texture]) return null From e4191b0e9da97d1326a636e43a470be55923e0c2 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Tue, 23 Jun 2020 15:54:59 +0300 Subject: [PATCH 47/88] Implemented ability to remove selected background texture --- src/js/shot-generator/components/FileInput/index.js | 6 ++++-- src/js/shot-generator/components/InspectedWorld/index.js | 6 ++++++ .../shot-generator/components/Three/SceneBackground.js | 9 ++++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/js/shot-generator/components/FileInput/index.js b/src/js/shot-generator/components/FileInput/index.js index a109a656d5..318a34d56b 100644 --- a/src/js/shot-generator/components/FileInput/index.js +++ b/src/js/shot-generator/components/FileInput/index.js @@ -2,13 +2,14 @@ import React, {useCallback} from 'react' import {remote} from 'electron' const {dialog} = remote - +const defaultValue = "(none)" const FileInput = React.memo(({ - value = "(none)", + value = defaultValue, label, onChange, wrapperClassName="input-group", refClassName="file-input", filters = [], + canRemove = false, ...props }) => { const onFileSelect = useCallback((e) => { @@ -38,6 +39,7 @@ const FileInput = React.memo(({ return (
{label ?
{label}
: null} + {canRemove && (value && value !== defaultValue) && onChange({file: undefined, files: []}) }>X}
{ if (event.file) { updateWorld({textureType: SceneTextureType.Image, sceneTexture: CopyFile(storyboarderFilePath, event.file, 'sceneTexture')}) + } else { + updateWorld({textureType:null, sceneTexture: null}) } }, []) const setSceneCubeMap = useCallback((event) => { if (event.file) { updateWorld({textureType: SceneTextureType.CubeMap, sceneTexture: CopyFile(storyboarderFilePath, event.file, 'sceneTexture')}) + } else { + updateWorld({textureType:null, sceneTexture: null}) } }, []) @@ -123,6 +127,7 @@ const InspectedWorld = React.memo(({updateObject, updateWorld, updateWorldRoom, label={"Scene Cube map"} value={world.sceneTexture && path.basename(world.sceneTexture)} filters={ [ { name:"Images", extensions: imageFilters } ] } + canRemove={ true } /> } {(!world.textureType || world.textureType === SceneTextureType.Image) && }
diff --git a/src/js/shot-generator/components/Three/SceneBackground.js b/src/js/shot-generator/components/Three/SceneBackground.js index 7bc4eec06f..963ae7eb32 100644 --- a/src/js/shot-generator/components/Three/SceneBackground.js +++ b/src/js/shot-generator/components/Three/SceneBackground.js @@ -21,6 +21,7 @@ const SceneBackground = React.memo(({ imagePath, world, storyboarderFilePath, up return () => { if(scene.background instanceof THREE.Texture) { scene.background.dispose() + scene.background = null } } }, []) @@ -78,7 +79,13 @@ const SceneBackground = React.memo(({ imagePath, world, storyboarderFilePath, up } useEffect(() => { - if(!texture) return + if(!texture) { + if(scene.background instanceof THREE.Texture) { + scene.background.dispose() + scene.background = new THREE.Color(world.backgroundColor) + } + return + } cleanUpTempFile() let backgroundTexture if(world.textureType === SceneTextureType.CubeMap) { From d0f478ddea8705a6b6b996a00c38553d728b40b1 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Tue, 23 Jun 2020 16:11:27 +0300 Subject: [PATCH 48/88] Cleaned up --- .../components/FileInput/index.js | 2 +- .../components/InspectedWorld/index.js | 14 +++--- .../components/Three/SceneBackground.js | 43 ++++++++++--------- 3 files changed, 30 insertions(+), 29 deletions(-) diff --git a/src/js/shot-generator/components/FileInput/index.js b/src/js/shot-generator/components/FileInput/index.js index 318a34d56b..65bd13385e 100644 --- a/src/js/shot-generator/components/FileInput/index.js +++ b/src/js/shot-generator/components/FileInput/index.js @@ -39,7 +39,7 @@ const FileInput = React.memo(({ return (
{label ?
{label}
: null} - {canRemove && (value && value !== defaultValue) && onChange({file: undefined, files: []}) }>X} + {canRemove && (value && value !== defaultValue) && }
{ + const setWorldTexture = useCallback((type, event) => { if (event.file) { - updateWorld({textureType: SceneTextureType.Image, sceneTexture: CopyFile(storyboarderFilePath, event.file, 'sceneTexture')}) + updateWorld({textureType: type, sceneTexture: CopyFile(storyboarderFilePath, event.file, 'sceneTexture')}) } else { updateWorld({textureType:null, sceneTexture: null}) } }, []) + const setSceneTextureFile = useCallback((event) => { + setWorldTexture(SceneTextureType.Image, event) + }, []) + const setSceneCubeMap = useCallback((event) => { - if (event.file) { - updateWorld({textureType: SceneTextureType.CubeMap, sceneTexture: CopyFile(storyboarderFilePath, event.file, 'sceneTexture')}) - } else { - updateWorld({textureType:null, sceneTexture: null}) - } + setWorldTexture(SceneTextureType.CubeMap, event) }, []) const setAmbientIntensity = useCallback((intensity) => updateWorldEnvironment({intensity}), []) diff --git a/src/js/shot-generator/components/Three/SceneBackground.js b/src/js/shot-generator/components/Three/SceneBackground.js index 963ae7eb32..29bc153f81 100644 --- a/src/js/shot-generator/components/Three/SceneBackground.js +++ b/src/js/shot-generator/components/Three/SceneBackground.js @@ -1,5 +1,5 @@ import * as THREE from 'three' -import React, { useEffect, useLayoutEffect, useRef, useCallback, useMemo } from 'react' +import React, { useEffect, useRef } from 'react' import { useThree } from 'react-three-fiber' import { useAsset } from '../../hooks/use-assets-manager' import CubeMapDrawingTexture from './helpers/cubeMapDrawingTexture' @@ -77,12 +77,28 @@ const SceneBackground = React.memo(({ imagePath, world, storyboarderFilePath, up scene.userData.tempPath = null } } + + const save = () => { + cleanUpTempFile() + let tempFileName = `temp_scenetexture-${Date.now()}.jpg` + if(world.textureType === SceneTextureType.CubeMap) { + cubeTextureCreator.current.saveCubeMapTexture(imagePath[0], scene.background, tempFileName) + } else if(world.textureType === SceneTextureType.Image) { + let imageData = drawingSceneTexture.texture.getImage("image/png") + let imageFilePath = path.join(path.dirname(storyboarderFilePath), 'models/sceneTextures', tempFileName) + fs.writeFileSync(imageFilePath, imageData, 'base64') + } + updateWorld({sceneTexture: 'models/sceneTextures/' + tempFileName}) + scene.userData.tempPath = tempFileName + texturePath.current = tempFileName + } useEffect(() => { if(!texture) { if(scene.background instanceof THREE.Texture) { scene.background.dispose() scene.background = new THREE.Color(world.backgroundColor) + cleanUpTempFile() } return } @@ -91,14 +107,10 @@ const SceneBackground = React.memo(({ imagePath, world, storyboarderFilePath, up if(world.textureType === SceneTextureType.CubeMap) { backgroundTexture = cubeTextureCreator.current.getCubeMapTexture(texture, storyboarderFilePath); if(backgroundTexture) { - drawingSceneTexture.save = () => { - cleanUpTempFile() - let tempFileName = `temp_scenetexture-${Date.now()}.jpg` - cubeTextureCreator.current.saveCubeMapTexture(imagePath[0], scene.background, tempFileName) - updateWorld({sceneTexture: 'models/sceneTextures/' + tempFileName}) - scene.userData.tempPath = tempFileName - texturePath.current = tempFileName - } + drawingSceneTexture.save = save + } else { + updateWorld({ sceneTexture: null, textureType: null }) + return } } else if(world.textureType === SceneTextureType.Image) { texture.wrapS = texture.wrapT = THREE.RepeatWrapping @@ -115,18 +127,7 @@ const SceneBackground = React.memo(({ imagePath, world, storyboarderFilePath, up intersectionBox.current.updateMatrixWorld(true) backgroundTexture = drawingSceneTexture.texture.createMaterial({map: texture}).map drawingSceneTexture.texture.setTexture(texture) - drawingSceneTexture.save = () => { - cleanUpTempFile() - let tempFileName = `temp_scenetexture-${Date.now()}.jpg` - let imageData = drawingSceneTexture.texture.getImage("image/png") - - let imageFilePath = path.join(path.dirname(storyboarderFilePath), 'models/sceneTextures', tempFileName) - fs.writeFileSync(imageFilePath, imageData, 'base64') - - updateWorld({sceneTexture: 'models/sceneTextures/' + tempFileName}) - scene.userData.tempPath = tempFileName - texturePath.current = tempFileName - } + drawingSceneTexture.save = save } scene.userData.texturePath = imagePath[0] scene.background = backgroundTexture; From 7a91a3a96f1365bd8b14989ba9889dfb37706a73 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Tue, 23 Jun 2020 17:16:39 +0300 Subject: [PATCH 49/88] Fixed background disappears on first draw --- .../components/Three/SceneBackground.js | 21 +++++++++++-------- .../Three/helpers/CubeTextureCreator.js | 4 ++-- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/js/shot-generator/components/Three/SceneBackground.js b/src/js/shot-generator/components/Three/SceneBackground.js index 29bc153f81..3b6a1fabd0 100644 --- a/src/js/shot-generator/components/Three/SceneBackground.js +++ b/src/js/shot-generator/components/Three/SceneBackground.js @@ -26,6 +26,16 @@ const SceneBackground = React.memo(({ imagePath, world, storyboarderFilePath, up } }, []) + useEffect(() => { + if(!imagePath[0]) { + if(scene.background instanceof THREE.Texture) { + scene.background.dispose() + scene.background = new THREE.Color(world.backgroundColor) + cleanUpTempFile() + } + } + }, [imagePath[0]]) + useEffect(() => { if(world.textureType === SceneTextureType.CubeMap) { drawingSceneTexture.texture = new CubeMapDrawingTexture() @@ -84,7 +94,7 @@ const SceneBackground = React.memo(({ imagePath, world, storyboarderFilePath, up if(world.textureType === SceneTextureType.CubeMap) { cubeTextureCreator.current.saveCubeMapTexture(imagePath[0], scene.background, tempFileName) } else if(world.textureType === SceneTextureType.Image) { - let imageData = drawingSceneTexture.texture.getImage("image/png") + let imageData = drawingSceneTexture.texture.getImage("image/jpg") let imageFilePath = path.join(path.dirname(storyboarderFilePath), 'models/sceneTextures', tempFileName) fs.writeFileSync(imageFilePath, imageData, 'base64') } @@ -94,14 +104,7 @@ const SceneBackground = React.memo(({ imagePath, world, storyboarderFilePath, up } useEffect(() => { - if(!texture) { - if(scene.background instanceof THREE.Texture) { - scene.background.dispose() - scene.background = new THREE.Color(world.backgroundColor) - cleanUpTempFile() - } - return - } + if(!texture ) return cleanUpTempFile() let backgroundTexture if(world.textureType === SceneTextureType.CubeMap) { diff --git a/src/js/shot-generator/components/Three/helpers/CubeTextureCreator.js b/src/js/shot-generator/components/Three/helpers/CubeTextureCreator.js index 8287ec857b..1f7246d59b 100644 --- a/src/js/shot-generator/components/Three/helpers/CubeTextureCreator.js +++ b/src/js/shot-generator/components/Three/helpers/CubeTextureCreator.js @@ -34,13 +34,13 @@ class CubeTextureCreator { let {dir, ext, name} = path.parse(imagePath); let dataUrl = this.drawingCtx.canvas.toDataURL("image/jpeg"); let properName = filename ? filename : name + ext; - saveDataURLtoFile(dataUrl, `${properName}`, 'models/sceneTextures', this.boardPath, true); + saveDataURLtoFile(dataUrl, `${properName}`, 'models/sceneTextures', this.boardPath); } // Draw the specific mesh on drawingContext which contains original texture image saveFace( image, x, y, width, height, name, boardPath) { this.drawingCtx.drawImage(image, x, y, width, height); - saveDataURLtoFile(image.toDataURL("image/jpeg"), `${name}.jpg`, 'models/sceneTextures/cubetexture', boardPath, true); + saveDataURLtoFile(image.toDataURL("image/jpeg"), `${name}.jpg`, 'models/sceneTextures/cubetexture', boardPath); } // Parses/crops passed cube texture and loading cube texture from them From 3829b92bcbd2e9afa489cdf9aa9507165d7ddcb3 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Wed, 24 Jun 2020 13:30:51 +0300 Subject: [PATCH 50/88] Implemented position buffer and continous drawing --- src/js/shot-generator/SceneManagerR3fLarge.js | 26 ++++++------- .../components/Three/Helpers/Brushes/Brush.js | 21 +++++++--- .../Three/Helpers/Brushes/PointBuffer.js | 39 +++++++++++++++++++ .../Three/Helpers/Brushes/SimpleBrush.js | 33 ++++++++++------ .../components/Three/Helpers/SimpleTexture.js | 23 ++++++++++- .../Three/helpers/DrawingTexture.js | 9 +++-- 6 files changed, 117 insertions(+), 34 deletions(-) create mode 100644 src/js/shot-generator/components/Three/Helpers/Brushes/PointBuffer.js diff --git a/src/js/shot-generator/SceneManagerR3fLarge.js b/src/js/shot-generator/SceneManagerR3fLarge.js index ad8e397482..8600e8dbe5 100644 --- a/src/js/shot-generator/SceneManagerR3fLarge.js +++ b/src/js/shot-generator/SceneManagerR3fLarge.js @@ -213,27 +213,27 @@ const SceneManagerR3fLarge = connect( if(!intersections.length && drawingSceneTexture.current.draw) { let texture = drawingSceneTexture.current texture.draw({x, y}, camera, drawingBrush) - } else if(drawingSceneTexture.current.texture) { - drawingSceneTexture.current.texture.resetMeshPos() } for(let i = 0; i < keys.length; i++) { let key = keys[i] - let drawingTexture = drawingTextures.current[key] - if(!intersections.length) { - drawingTexture.resetMeshPos() - continue; - } - let object = imageObjects.find((obj) => obj.userData.id === key) + let drawingTexture = drawingTextures.current[key]; + let object = drawingTexture.material.parent.parent; if(!object || !object.visible) continue - if(intersections[0].object.parent.uuid === object.uuid) { - drawingTexture.draw({x, y}, object, camera, drawingBrush) - } + drawingTexture.draw({x, y}, object, camera, drawingBrush) } } const onKeyDown = (event) => { isDrawStarted.current = true; + let keys = Object.keys(drawingTextures.current) + for(let i = 0; i < keys.length; i++) { + let key = keys[i] + drawingTextures.current[key].prepareToDraw(); + } + if(drawingSceneTexture.current && drawingSceneTexture.current.texture) { + drawingSceneTexture.current.save() + } gl.domElement.addEventListener('mousemove', draw) } @@ -254,7 +254,7 @@ const SceneManagerR3fLarge = connect( let keys = Object.keys(drawingTextures.current) for(let i = 0; i < keys.length; i++) { let key = keys[i] - drawingTextures.current[key].resetMeshPos(); + drawingTextures.current[key].endDraw(); let object = scene.__interaction.find((obj) => obj.userData.id === key) if(drawingTextures.current[key].isChanged) { drawingTextures.current[key].isChanged = false @@ -263,7 +263,7 @@ const SceneManagerR3fLarge = connect( } if( drawingSceneTexture.current.save && drawingSceneTexture.current.texture.isChanged) { drawingSceneTexture.current.texture.isChanged = false - drawingSceneTexture.current.texture.resetMeshPos() + drawingSceneTexture.current.texture.endDraw() drawingSceneTexture.current.save() } } diff --git a/src/js/shot-generator/components/Three/Helpers/Brushes/Brush.js b/src/js/shot-generator/components/Three/Helpers/Brushes/Brush.js index 6ab414dc5b..730d0f89e8 100644 --- a/src/js/shot-generator/components/Three/Helpers/Brushes/Brush.js +++ b/src/js/shot-generator/components/Three/Helpers/Brushes/Brush.js @@ -1,3 +1,4 @@ +import PointBuffer from './PointBuffer' class Brush { constructor(drawingCtx) { this.drawingCtx = drawingCtx; @@ -6,18 +7,28 @@ class Brush { this.defaultHeight = 500; this.percentageBasedSize = true; this.brushSize; + this.positionBuffer = new PointBuffer(2); + this.isDrawing = false; + } + + startDrawing() { + this.isDrawing = true; + } + + stopDrawing() { + this.isDrawing = false; + this.positionBuffer.flushArray(); } + set DrawingContext(value) { + this.drawingCtx = value + } + resetMeshPos() { this.prevPos = null } draw(currentPos, brush) { - if(!this.prevPos) { - this.prevPos = {} - this.prevPos.x = currentPos.x; - this.prevPos.y = currentPos.y; - } this.brushSize = brush.size; let { width, height } = this.drawingCtx.canvas; if(this.percentageBasedSize) { diff --git a/src/js/shot-generator/components/Three/Helpers/Brushes/PointBuffer.js b/src/js/shot-generator/components/Three/Helpers/Brushes/PointBuffer.js new file mode 100644 index 0000000000..7e8939d75d --- /dev/null +++ b/src/js/shot-generator/components/Three/Helpers/Brushes/PointBuffer.js @@ -0,0 +1,39 @@ +class PointBuffer { + constructor(pointsAmount) { + this.defaultBufferSize = 20; + this.pointsAmount = pointsAmount; + this.flushArray(); + } + + addElements(...elements) { + let predictedLength = this.currentLength * this.pointsAmount + elements.length + if(this.buffer.length < predictedLength) { + + let newLength = this.buffer.length * 2; + while(newLength < predictedLength) { + newLength *= 2 + } + let resizedBuffer = new Int16Array(newLength); + resizedBuffer.set(this.buffer); + this.buffer = resizedBuffer; + } + this.buffer.set(elements, this.currentLength * this.pointsAmount); + this.currentLength += elements.length / this.pointsAmount; + } + + getElements(index) { + let offsetIndex = index * this.pointsAmount + return this.buffer.subarray(offsetIndex, offsetIndex + this.pointsAmount) + } + + flushArray() { + this.buffer = new Int16Array(this.defaultBufferSize * this.pointsAmount); + this.currentLength = 0; + } + + getLength() { + return this.currentLength; + } +} + +export default PointBuffer; \ No newline at end of file diff --git a/src/js/shot-generator/components/Three/Helpers/Brushes/SimpleBrush.js b/src/js/shot-generator/components/Three/Helpers/Brushes/SimpleBrush.js index b661fc91f5..0a48559370 100644 --- a/src/js/shot-generator/components/Three/Helpers/Brushes/SimpleBrush.js +++ b/src/js/shot-generator/components/Three/Helpers/Brushes/SimpleBrush.js @@ -1,4 +1,5 @@ import Brush from './Brush' + class SimpleBrush extends Brush { constructor(drawingCtx) { @@ -11,30 +12,38 @@ class SimpleBrush extends Brush { this.drawingCtx.strokeStyle = brush.color; this.drawingCtx.fillStyle = brush.color; this.drawingCtx.lineWidth = this.brushSize; - + let prevX + let prevY + if(this.positionBuffer.currentLength === 0) { + prevX = currentPos.x; + prevY = currentPos.y; + } else { + let prevElements = this.positionBuffer.getElements(this.positionBuffer.currentLength - 1); + prevX = prevElements[0]; + prevY = prevElements[1]; + } let circle = new Path2D(); - let xOffset = currentPos.x - this.prevPos.x; - let yOffset = currentPos.y - this.prevPos.y; - let length + let xOffset = currentPos.x - prevX; + let yOffset = currentPos.y - prevY; + let length; if(Math.abs(xOffset) < Math.abs(yOffset)) { - length = Math.abs(yOffset) + length = Math.abs(yOffset); } else { - length = Math.abs(xOffset) + length = Math.abs(xOffset); } xOffset /= length; yOffset /= length; - let size = this.brushSize + let size = this.brushSize; for(let i = 0; i < length; i++) { let x = xOffset * i; let y = yOffset * i; - circle.moveTo(this.prevPos.x + x, this.prevPos.y + y); - circle.arc(this.prevPos.x + x, this.prevPos.y + y, size, 0, 2 * Math.PI) + circle.moveTo(prevX + x, prevY + y); + circle.arc(prevX + x, prevY + y, size, 0, 2 * Math.PI) } this.drawingCtx.stroke(); - this.drawingCtx.fill(circle) - this.prevPos.x = currentPos.x; - this.prevPos.y = currentPos.y; + this.drawingCtx.fill(circle); + this.positionBuffer.addElements(currentPos.x, currentPos.y); } } diff --git a/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js b/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js index 4443a4a266..ad68989953 100644 --- a/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js +++ b/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js @@ -1,5 +1,15 @@ import * as THREE from 'three' import DrawingTexture from './DrawingTexture' + +const fromClipSpaceToWorldSpace = (mousePos, camera, targetZ) => { + let vector = new THREE.Vector3(); + vector.set(mousePos.x, mousePos.y, 0.5); + vector.unproject(camera); + vector.sub(camera.position).normalize(); + let distance = (targetZ - camera.position.z) / vector.z; + let position = new THREE.Vector3().copy(camera.position).add(vector.multiplyScalar(distance)); + return position; +} class SimpleTexture extends DrawingTexture { constructor(){ super(); @@ -49,7 +59,18 @@ class SimpleTexture extends DrawingTexture { draw (mousePosition, object, camera, brush){ let intersection = super.draw(mousePosition, object, camera, brush) - if(!intersection) return + // If we don't have a uv coordinates and as a last resort we trying to translate mouse into object coordinate + // From object coordinate we can sort of simulate uv coordinate logic for plain object + // NOTE() : This won't work for any object except plain object( image object ) + if(!intersection) { + let worldPos = fromClipSpaceToWorldSpace(mousePosition, camera, object.position.z) + worldPos.applyMatrix4(new THREE.Matrix4().getInverse(object.matrixWorld)) + intersection = {} + let geometry = object.getObjectByProperty("type", "Mesh").geometry; + let xOffset = geometry.boundingBox.max.x; + let yOffset = geometry.boundingBox.max.y; + intersection.uv = { x: (worldPos.x + xOffset), y: (worldPos.y + yOffset) } + } let screenX = (intersection.uv.x) * this.texture.image.width; let screenY = (1 - intersection.uv.y) * this.texture.image.height; this.drawingBrush.draw({ x: screenX, y: screenY }, brush) diff --git a/src/js/shot-generator/components/Three/helpers/DrawingTexture.js b/src/js/shot-generator/components/Three/helpers/DrawingTexture.js index 093128d38d..799f4a6b0a 100644 --- a/src/js/shot-generator/components/Three/helpers/DrawingTexture.js +++ b/src/js/shot-generator/components/Three/helpers/DrawingTexture.js @@ -13,8 +13,12 @@ class DrawingTexture { this.setMesh(); } - resetMeshPos() { - this.drawingBrush.resetMeshPos(); + prepareToDraw() { + this.drawingBrush.startDrawing(); + } + + endDraw() { + this.drawingBrush.stopDrawing(); } setMesh(type) { @@ -69,7 +73,6 @@ class DrawingTexture { let intersection = this.intersectImage(mousePosition.x, mousePosition.y, object, camera); if(intersection.uv === null) { - this.resetMeshPos(); return; } this.isChanged = true; From c64aa99d6ab43412f9731d95a8e9f1ce2f805c4b Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Wed, 24 Jun 2020 14:09:22 +0300 Subject: [PATCH 51/88] Fixed scene background drawing --- .../shot-generator/components/Three/SceneBackground.js | 2 +- .../components/Three/helpers/CubeTextureCreator.js | 9 +++++---- .../components/Three/helpers/cubeMapDrawingTexture.js | 3 ++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/js/shot-generator/components/Three/SceneBackground.js b/src/js/shot-generator/components/Three/SceneBackground.js index 3b6a1fabd0..5814836a79 100644 --- a/src/js/shot-generator/components/Three/SceneBackground.js +++ b/src/js/shot-generator/components/Three/SceneBackground.js @@ -12,7 +12,7 @@ import SceneTextureType from '../InspectedWorld/SceneTextureType' const SceneBackground = React.memo(({ imagePath, world, storyboarderFilePath, updateWorld, drawingSceneTexture }) => { const texturePath = useRef() const { scene, camera, gl } = useThree() - const { asset: texture } = useAsset(!scene.userData.tempPath ? imagePath[0] : imagePath[0].includes(scene.userData.tempPath ) ? null : imagePath[0]) + const { asset: texture } = useAsset( !imagePath[0] || !scene.userData.tempPath ? imagePath[0] : imagePath[0].includes(scene.userData.tempPath ) ? null : imagePath[0]) const intersectionBox = useRef() const intersectionCamera = useRef() const cubeTextureCreator = useRef( new CubeTextureCreator()) diff --git a/src/js/shot-generator/components/Three/helpers/CubeTextureCreator.js b/src/js/shot-generator/components/Three/helpers/CubeTextureCreator.js index 1f7246d59b..db3bf361a2 100644 --- a/src/js/shot-generator/components/Three/helpers/CubeTextureCreator.js +++ b/src/js/shot-generator/components/Three/helpers/CubeTextureCreator.js @@ -28,7 +28,6 @@ class CubeTextureCreator { for( let i = 0; i < this.imageElements.length; i++ ) { let element = this.imageElements[i]; let croppedImage = texture.image[i]; - this.saveFace(croppedImage, element.x, element.y, element.width, element.height, element.name, this.boardPath ); } let {dir, ext, name} = path.parse(imagePath); @@ -38,9 +37,11 @@ class CubeTextureCreator { } // Draw the specific mesh on drawingContext which contains original texture image - saveFace( image, x, y, width, height, name, boardPath) { - this.drawingCtx.drawImage(image, x, y, width, height); - saveDataURLtoFile(image.toDataURL("image/jpeg"), `${name}.jpg`, 'models/sceneTextures/cubetexture', boardPath); + saveFace( image, x, y, width, height, name, boardPath) { + this.croppedCtx.canvas.width = width; + this.croppedCtx.canvas.height = height; + this.croppedCtx.drawImage(image, x, y, width, height); + saveDataURLtoFile(this.croppedCtx.canvas.toDataURL("image/jpeg"), `${name}.jpg`, 'models/sceneTextures/cubetexture', boardPath); } // Parses/crops passed cube texture and loading cube texture from them diff --git a/src/js/shot-generator/components/Three/helpers/cubeMapDrawingTexture.js b/src/js/shot-generator/components/Three/helpers/cubeMapDrawingTexture.js index 6c92fb11cb..e77d19bf6e 100644 --- a/src/js/shot-generator/components/Three/helpers/cubeMapDrawingTexture.js +++ b/src/js/shot-generator/components/Three/helpers/cubeMapDrawingTexture.js @@ -55,7 +55,8 @@ class CubeMapDrawingTexture extends DrawingTexture { index = x === -1 ? 5 : 4; } if( this.prevFaceIndex !== null && this.prevFaceIndex !== index ) { - this.resetMeshPos(); + this.drawingBrush.stopDrawing(); + this.drawingBrush.startDrawing(); } this.prevFaceIndex = index; let screenX = (1 - intersection.uv.x) * this.texture.image[index].width; From 9f166e552dd29a9ae9025482aefadbe5003ae9d9 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Wed, 24 Jun 2020 14:14:22 +0300 Subject: [PATCH 52/88] Fixed eraser brush --- .../Three/Helpers/Brushes/EraserBrush.js | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/js/shot-generator/components/Three/Helpers/Brushes/EraserBrush.js b/src/js/shot-generator/components/Three/Helpers/Brushes/EraserBrush.js index 97b59b69c3..a25b8e05ea 100644 --- a/src/js/shot-generator/components/Three/Helpers/Brushes/EraserBrush.js +++ b/src/js/shot-generator/components/Three/Helpers/Brushes/EraserBrush.js @@ -7,10 +7,19 @@ class EraserBrush extends Brush { draw(currentPos, brush) { super.draw(currentPos, brush); this.drawingCtx.fillStyle = 'white'; - + let prevX + let prevY + if(this.positionBuffer.currentLength === 0) { + prevX = currentPos.x; + prevY = currentPos.y; + } else { + let prevElements = this.positionBuffer.getElements(this.positionBuffer.currentLength - 1); + prevX = prevElements[0]; + prevY = prevElements[1]; + } let circle = new Path2D(); - let xOffset = currentPos.x - this.prevPos.x; - let yOffset = currentPos.y - this.prevPos.y; + let xOffset = currentPos.x - prevX; + let yOffset = currentPos.y - prevY; let length if(Math.abs(xOffset) < Math.abs(yOffset)) { length = Math.abs(yOffset) @@ -24,13 +33,12 @@ class EraserBrush extends Brush { for(let i = 0; i < length; i++) { let x = xOffset * i; let y = yOffset * i; - circle.moveTo(this.prevPos.x + x, this.prevPos.y + y); - circle.arc(this.prevPos.x + x, this.prevPos.y + y, size, 0, 2 * Math.PI) + circle.moveTo(prevX + x, prevY + y); + circle.arc(prevX + x, prevY + y, size, 0, 2 * Math.PI) } this.drawingCtx.stroke(); this.drawingCtx.fill(circle) - this.prevPos.x = currentPos.x; - this.prevPos.y = currentPos.y; + this.positionBuffer.addElements(currentPos.x, currentPos.y); } } From 6ae2a2000955aa94506be72d44f3584d2a689d19 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Tue, 30 Jun 2020 14:05:35 +0300 Subject: [PATCH 53/88] Fixed cannot draw on cubemap on first click --- src/js/shot-generator/SceneManagerR3fLarge.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/shot-generator/SceneManagerR3fLarge.js b/src/js/shot-generator/SceneManagerR3fLarge.js index 8600e8dbe5..b824015970 100644 --- a/src/js/shot-generator/SceneManagerR3fLarge.js +++ b/src/js/shot-generator/SceneManagerR3fLarge.js @@ -232,7 +232,7 @@ const SceneManagerR3fLarge = connect( drawingTextures.current[key].prepareToDraw(); } if(drawingSceneTexture.current && drawingSceneTexture.current.texture) { - drawingSceneTexture.current.save() + drawingSceneTexture.current.texture.prepareToDraw() } gl.domElement.addEventListener('mousemove', draw) } From d4dcf9b68dd68d520ae30d7d61c4f319543cdb71 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Tue, 30 Jun 2020 18:05:39 +0300 Subject: [PATCH 54/88] Optimized drawing by removing saving of cropped image pieces --- .../components/Three/SaveShot.js | 1 + .../components/Three/SceneBackground.js | 2 +- .../Three/helpers/CubeTextureCreator.js | 33 +++--- .../hooks/use-assets-manager.js | 2 +- src/js/xr/src/SceneManagerXR.js | 6 +- src/js/xr/src/helpers/CubeTextureCreator.js | 101 ++++++++++++++++++ src/js/xr/src/hooks/use-assets-manager.js | 24 +---- 7 files changed, 126 insertions(+), 43 deletions(-) create mode 100644 src/js/xr/src/helpers/CubeTextureCreator.js diff --git a/src/js/shot-generator/components/Three/SaveShot.js b/src/js/shot-generator/components/Three/SaveShot.js index d26d5ce280..d0273ee1ef 100644 --- a/src/js/shot-generator/components/Three/SaveShot.js +++ b/src/js/shot-generator/components/Three/SaveShot.js @@ -150,6 +150,7 @@ const SaveShot = connect( if(scene.userData.tempPath) { let tempImageFilePath = path.join(path.dirname(storyboarderFilePath), 'models/sceneTextures/', scene.userData.tempPath) let imageFilePath = path.join(path.dirname(storyboarderFilePath), scene.userData.texturePath) + removeAsset(imageFilePath) fs.copySync(tempImageFilePath, imageFilePath, {overwrite:true}) fs.remove(tempImageFilePath) updateWorld({sceneTexture: scene.userData.texturePath}) diff --git a/src/js/shot-generator/components/Three/SceneBackground.js b/src/js/shot-generator/components/Three/SceneBackground.js index 5814836a79..e460a8cd43 100644 --- a/src/js/shot-generator/components/Three/SceneBackground.js +++ b/src/js/shot-generator/components/Three/SceneBackground.js @@ -132,7 +132,7 @@ const SceneBackground = React.memo(({ imagePath, world, storyboarderFilePath, up drawingSceneTexture.texture.setTexture(texture) drawingSceneTexture.save = save } - scene.userData.texturePath = imagePath[0] + scene.userData.texturePath = world.sceneTexture scene.background = backgroundTexture; drawingSceneTexture.draw = draw }, [texture]) diff --git a/src/js/shot-generator/components/Three/helpers/CubeTextureCreator.js b/src/js/shot-generator/components/Three/helpers/CubeTextureCreator.js index db3bf361a2..79a253b3c3 100644 --- a/src/js/shot-generator/components/Three/helpers/CubeTextureCreator.js +++ b/src/js/shot-generator/components/Three/helpers/CubeTextureCreator.js @@ -1,6 +1,5 @@ import saveDataURLtoFile from '../../../helpers/saveDataURLtoFile' import path from 'path' -import fs from 'fs-extra' import * as THREE from 'three' class CubeTextureCreator { @@ -40,8 +39,7 @@ class CubeTextureCreator { saveFace( image, x, y, width, height, name, boardPath) { this.croppedCtx.canvas.width = width; this.croppedCtx.canvas.height = height; - this.croppedCtx.drawImage(image, x, y, width, height); - saveDataURLtoFile(this.croppedCtx.canvas.toDataURL("image/jpeg"), `${name}.jpg`, 'models/sceneTextures/cubetexture', boardPath); + this.drawingCtx.drawImage(image, x, y, width, height); } // Parses/crops passed cube texture and loading cube texture from them @@ -52,25 +50,18 @@ class CubeTextureCreator { let image = gltf.image; this.drawingCanvas.width = image.width; this.drawingCanvas.height = image.height; - fs.removeSync(path.join(path.dirname(boardPath), 'models/sceneTextures/cubetexture')) this.recognizeTexturePattern(image); + let cubeTexture = new THREE.CubeTexture(); if( !this.imageElements.length ) return; for( let i = 0; i < this.imageElements.length; i++ ) { let element = this.imageElements[i]; - this.crop(image, element.x, element.y, element.width, element.height, element.name, boardPath); + let texture = this.crop(image, element.x, element.y, element.width, element.height, element.name, boardPath); + cubeTexture.images[i] = texture } + cubeTexture.needsUpdate = true - return new THREE.CubeTextureLoader() - .setPath( path.join(path.dirname(boardPath), 'models/sceneTextures/cubetexture/') ) - .load( [ - 'px.jpg#' + new Date().getTime(), - 'nx.jpg#' + new Date().getTime(), - 'py.jpg#' + new Date().getTime(), - 'ny.jpg#' + new Date().getTime(), - 'pz.jpg#' + new Date().getTime(), - 'nz.jpg#' + new Date().getTime() - ]) + return cubeTexture } // Tries to recognize image(Cube Texture) pattern if pattern is recognized it creates image element @@ -129,10 +120,14 @@ class CubeTextureCreator { // Crops a segment from images and saves it with passed name crop( image, x, y, width, height, name, boardPath ) { this.drawingCtx.drawImage(image, 0, 0, image.width, image.height); - var imageData = this.drawingCtx.getImageData(x, y, width, height); - this.croppedCtx.putImageData(imageData, 0, 0); - let dataUrl = this.croppedCtx.canvas.toDataURL("image/jpeg"); - saveDataURLtoFile(dataUrl, `${name}.jpg`, 'models/sceneTextures/cubetexture', boardPath); + let imageData = this.drawingCtx.getImageData(x, y, width, height); + let croppedCanvas = document.createElement('canvas'); + let croppedCtx = croppedCanvas.getContext('2d'); + croppedCanvas.width = width; + croppedCanvas.height = height; + croppedCtx.putImageData(imageData, 0, 0); + let texture = new THREE.Texture(croppedCtx.canvas) + return texture.image } } diff --git a/src/js/shot-generator/hooks/use-assets-manager.js b/src/js/shot-generator/hooks/use-assets-manager.js index 76ceade5a6..322bb4b069 100644 --- a/src/js/shot-generator/hooks/use-assets-manager.js +++ b/src/js/shot-generator/hooks/use-assets-manager.js @@ -68,7 +68,7 @@ export const loadAsset = (path) => { } else { /** Current resource is texture */ loader = textureLoader - filePath += "#" + Date.now() + filePath += "?ts=" + Date.now() } loader.load( diff --git a/src/js/xr/src/SceneManagerXR.js b/src/js/xr/src/SceneManagerXR.js index f94e352e86..7d746bc368 100644 --- a/src/js/xr/src/SceneManagerXR.js +++ b/src/js/xr/src/SceneManagerXR.js @@ -66,7 +66,7 @@ const Boards = require('./components/ui/Boards') const BonesHelper = require('./three/BonesHelper') const Voicer = require('./three/Voicer') - +const CubeTextureCreator = require('./helpers/CubeTextureCreator').default const musicSystem = require('./music-system') @@ -149,6 +149,7 @@ const SceneContent = connect( }) => { const { gl, camera, scene } = useThree() const prevSceneTexture = useRef() + const cubeMapCreator = useRef(new CubeTextureCreator()) const sceneTexture = getAsset(getSceneTextureFilePath(world, prevSceneTexture.current, removeAsset)) const teleportRef = useRef() // actions @@ -246,7 +247,8 @@ const SceneContent = connect( useEffect(() => { if(!sceneTexture) return - scene.background = sceneTexture + let cubeTexture = cubeMapCreator.current.getCubeMapTexture(sceneTexture) + scene.background = cubeTexture }, [sceneTexture]) const welcomeAudio = useMemo(() => { diff --git a/src/js/xr/src/helpers/CubeTextureCreator.js b/src/js/xr/src/helpers/CubeTextureCreator.js new file mode 100644 index 0000000000..1de6a54f6a --- /dev/null +++ b/src/js/xr/src/helpers/CubeTextureCreator.js @@ -0,0 +1,101 @@ +const THREE = require('three') + +class CubeTextureCreator { + constructor() { + this.drawingCanvas = document.createElement('canvas'); + this.croppedCanvas = document.createElement('canvas'); + this.drawingCtx = this.drawingCanvas.getContext('2d'); + this.croppedCtx = this.croppedCanvas.getContext('2d'); + this.faces = ['px', 'nx', 'py', 'ny', 'pz', 'nz']; + this.imageElements = []; + this.gltf; + this.boardPath; + } + + // Parses/crops passed cube texture and loading cube texture from them + // Require Cube map to be one of the recognizable patterns see "recognizeTexturePattern" + getCubeMapTexture( gltf, boardPath ) { + this.gltf = gltf; + this.boardPath = boardPath; + let image = gltf.image; + this.drawingCanvas.width = image.width; + this.drawingCanvas.height = image.height; + + this.recognizeTexturePattern(image); + let cubeTexture = new THREE.CubeTexture(); + if( !this.imageElements.length ) return; + for( let i = 0; i < this.imageElements.length; i++ ) { + let element = this.imageElements[i]; + let texture = this.crop(image, element.x, element.y, element.width, element.height, element.name, boardPath); + cubeTexture.images[i] = texture + } + cubeTexture.needsUpdate = true + + return cubeTexture + } + + // Tries to recognize image(Cube Texture) pattern if pattern is recognized it creates image element + // image element knows it's position and size plus knows name for future use in "getCubeMapTexture" and "saveCubeMapTexture" + // In order to recognize pattern image should have square elements (element should have same width and height) + // and Texture shouldn't have any leftover pixels. e.g. image is 4 columns by 3 rows, element size is 64 by 64(pixels) then image size + // supposed to be 256 by 192(pixels) + recognizeTexturePattern( image ) { + this.imageElements = []; + // 4 by 3 pattern when cubetexture has 4 columns and 3 rows + // - py - - + // nx pz px nz + // - ny - - + if( image.width / 4 === image.height / 3 ) { + let elementSize = image.width / 4; + this.croppedCanvas.width = elementSize; + this.croppedCanvas.height = elementSize; + this.imageElements.push({x:elementSize * 2, y: elementSize, width: elementSize, height:elementSize, name:"px"}); + this.imageElements.push({x:0, y: elementSize, width: elementSize, height:elementSize, name:"nx"}); + this.imageElements.push({x:elementSize, y: 0, width: elementSize, height:elementSize, name:"py"}); + this.imageElements.push({x:elementSize, y: elementSize * 2, width: elementSize, height:elementSize, name:"ny"}); + this.imageElements.push({x:elementSize, y: elementSize, width: elementSize, height:elementSize, name:"pz"}); + this.imageElements.push({x:elementSize * 3, y: elementSize, width: elementSize, height:elementSize, name:"nz"}); + } + // 3 by 4 pattern when cubetexture has 3 columns and 4 rows + // - py - + // pz px nz + // - ny - + // - nx - + if( image.width / 3 === image.height / 4 ) { + let elementSize = image.width / 3; + this.croppedCanvas.width = elementSize; + this.croppedCanvas.height = elementSize; + this.imageElements.push({x:elementSize, y: elementSize, width: elementSize, height:elementSize, name:"px"}); + this.imageElements.push({x:elementSize, y: elementSize * 3, width: elementSize, height:elementSize, name:"nx"}); + this.imageElements.push({x:elementSize, y: 0, width: elementSize, height:elementSize, name:"py"}); + this.imageElements.push({x:elementSize, y: elementSize * 2, width: elementSize, height:elementSize, name:"ny"}); + this.imageElements.push({x:0, y: elementSize, width: elementSize, height:elementSize, name:"pz"}); + this.imageElements.push({x:elementSize * 2, y: elementSize, width: elementSize, height:elementSize, name:"nz"}); + } + + let row = image.width / 6 === image.height; + let column = image.width === image.height / 6; + // 1 by 6 pattern when either we have 1 column or 1 row + // px nx py ny pz nz + if( row || column ) { + let elementSize = row ? image.height : image.width; + this.croppedCanvas.width = elementSize; + this.croppedCanvas.height = elementSize; + for( let i = 0; i < this.faces.length; i++ ) { + this.imageElements.push({x:elementSize * row * i, y: elementSize * i * column, width: elementSize, height:elementSize, name:this.faces[i]}); + } + } + } + + // Crops a segment from images and saves it with passed name + crop( image, x, y, width, height, name, boardPath ) { + this.drawingCtx.drawImage(image, 0, 0, image.width, image.height); + let imageData = this.drawingCtx.getImageData(x, y, width, height); + this.croppedCtx.putImageData(imageData, 0, 0); + let htmlImage = new Image(); + htmlImage.src = this.croppedCtx.canvas.toDataURL(); + return htmlImage + } +} + +export default CubeTextureCreator \ No newline at end of file diff --git a/src/js/xr/src/hooks/use-assets-manager.js b/src/js/xr/src/hooks/use-assets-manager.js index 760564204e..bac565f820 100644 --- a/src/js/xr/src/hooks/use-assets-manager.js +++ b/src/js/xr/src/hooks/use-assets-manager.js @@ -97,31 +97,15 @@ const useAssetsManager = () => { .filter(([_, o]) => o.status === 'NotAsked') .filter(([id]) => id !== false) .forEach(([id]) => { - if (id.includes('/images/')) { - load(textureLoader, `${id}?ts=` + Date.now(), { - onload: value => dispatch({ type: 'SUCCESS', payload: { id, value } }), - onprogress: progress => dispatch({ type: 'PROGRESS', payload: { id, progress } }), - onerror: error => dispatch({ type: 'ERROR', payload: { id, error } }) - }) - dispatch({ type: 'LOAD', payload: { id } }) - } else if(id.includes('/sceneTextures')) { - let info = path.dirname(id) - cubeLoader.setPath(info + "/cubetexture/") - load(cubeLoader, [ - 'px.jpg?ts=' + new Date().getTime(), - 'nx.jpg?ts=' + new Date().getTime(), - 'py.jpg?ts=' + new Date().getTime(), - 'ny.jpg?ts=' + new Date().getTime(), - 'pz.jpg?ts=' + new Date().getTime(), - 'nz.jpg?ts=' + new Date().getTime() - ], { + if (!id.includes('/images/') && !id.includes('/sceneTextures/')) { + load(loader, id, { onload: value => dispatch({ type: 'SUCCESS', payload: { id, value } }), onprogress: progress => dispatch({ type: 'PROGRESS', payload: { id, progress } }), onerror: error => dispatch({ type: 'ERROR', payload: { id, error } }) }) dispatch({ type: 'LOAD', payload: { id } }) - } else { - load(loader, id, { + } else { + load(textureLoader, `${id}?ts=` + Date.now(), { onload: value => dispatch({ type: 'SUCCESS', payload: { id, value } }), onprogress: progress => dispatch({ type: 'PROGRESS', payload: { id, progress } }), onerror: error => dispatch({ type: 'ERROR', payload: { id, error } }) From cab81fca8df4b81c6b7c93fc6d57a854be547ea5 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Wed, 1 Jul 2020 14:35:17 +0300 Subject: [PATCH 55/88] Changed simple brush algorithm to quadratic curve --- .../Three/Helpers/Brushes/SimpleBrush.js | 79 ++++++++++++------- .../components/Three/Helpers/SimpleTexture.js | 9 +-- 2 files changed, 54 insertions(+), 34 deletions(-) diff --git a/src/js/shot-generator/components/Three/Helpers/Brushes/SimpleBrush.js b/src/js/shot-generator/components/Three/Helpers/Brushes/SimpleBrush.js index 0a48559370..f4dd2ef516 100644 --- a/src/js/shot-generator/components/Three/Helpers/Brushes/SimpleBrush.js +++ b/src/js/shot-generator/components/Three/Helpers/Brushes/SimpleBrush.js @@ -1,5 +1,34 @@ import Brush from './Brush' +const midPointsBtw = (p1, p2) => { + return { + x: p1.x + (p2.x - p1.x) / 2, + y: p1.y + (p2.y - p1.y) / 2 + } +} + +const getDifferenceTo = (from, to) => { + return {x:from.x - to.x, y: from.y - to.y} +} +const getDistanceTo = (from, to) => { + const diff = getDifferenceTo(from, to) + return Math.sqrt(Math.pow(diff.x, 2) + Math.pow(diff.y, 2)) +} +const getAngleTo = (from, to) => { + const diff = getDifferenceTo(from, to) + return Math.atan2(diff.y, diff.x) +} + +const moveByAngle = (angle, distance, point) => { + const angleRotated = angle + (Math.PI / 2) + let x = point.x + (Math.sin(angleRotated) * distance) + let y = point.y - (Math.cos(angleRotated) * distance) + return {x, y} +} +const getVector2FromBuffer = (buffer, index) => { + let elements = buffer.getElements(index); + return { x:elements[0], y:elements[1] } +} class SimpleBrush extends Brush { constructor(drawingCtx) { @@ -11,39 +40,35 @@ class SimpleBrush extends Brush { super.draw(currentPos, brush); this.drawingCtx.strokeStyle = brush.color; this.drawingCtx.fillStyle = brush.color; - this.drawingCtx.lineWidth = this.brushSize; - let prevX - let prevY + this.drawingCtx.lineWidth = this.brushSize * 2; + this.drawingCtx.lineJoin = 'round' + this.drawingCtx.lineCap = 'round' + let prevPos if(this.positionBuffer.currentLength === 0) { - prevX = currentPos.x; - prevY = currentPos.y; + prevPos = currentPos } else { - let prevElements = this.positionBuffer.getElements(this.positionBuffer.currentLength - 1); - prevX = prevElements[0]; - prevY = prevElements[1]; + prevPos = getVector2FromBuffer(this.positionBuffer, this.positionBuffer.currentLength - 1); } - let circle = new Path2D(); - let xOffset = currentPos.x - prevX; - let yOffset = currentPos.y - prevY; - let length; - if(Math.abs(xOffset) < Math.abs(yOffset)) { - length = Math.abs(yOffset); - - } else { - length = Math.abs(xOffset); + if(this.positionBuffer.currentLength === 0) { + this.positionBuffer.addElements(currentPos.x, currentPos.y); + return } - xOffset /= length; - yOffset /= length; - let size = this.brushSize; - for(let i = 0; i < length; i++) { - let x = xOffset * i; - let y = yOffset * i; - circle.moveTo(prevX + x, prevY + y); - circle.arc(prevX + x, prevY + y, size, 0, 2 * Math.PI) + this.drawingCtx.moveTo(currentPos.x, currentPos.y) + this.drawingCtx.beginPath() + let distance = getDistanceTo(currentPos, prevPos) + let angle = getAngleTo(currentPos, prevPos) + let newPos = moveByAngle(angle, distance, prevPos) + this.positionBuffer.addElements(newPos.x, newPos.y); + let p1 = getVector2FromBuffer(this.positionBuffer, 0) + let p2 = getVector2FromBuffer(this.positionBuffer, 1) + for(let i = 1, length = this.positionBuffer.currentLength; i < length; i++) { + let midpoint = midPointsBtw(p1, p2) + this.drawingCtx.quadraticCurveTo(p1.x, p1.y, midpoint.x, midpoint.y) + p1 = getVector2FromBuffer(this.positionBuffer, i) + p2 = getVector2FromBuffer(this.positionBuffer, i+1) } + this.drawingCtx.lineTo(p1.x, p1.y) this.drawingCtx.stroke(); - this.drawingCtx.fill(circle); - this.positionBuffer.addElements(currentPos.x, currentPos.y); } } diff --git a/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js b/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js index ad68989953..bfd2a61cf0 100644 --- a/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js +++ b/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js @@ -63,13 +63,8 @@ class SimpleTexture extends DrawingTexture { // From object coordinate we can sort of simulate uv coordinate logic for plain object // NOTE() : This won't work for any object except plain object( image object ) if(!intersection) { - let worldPos = fromClipSpaceToWorldSpace(mousePosition, camera, object.position.z) - worldPos.applyMatrix4(new THREE.Matrix4().getInverse(object.matrixWorld)) - intersection = {} - let geometry = object.getObjectByProperty("type", "Mesh").geometry; - let xOffset = geometry.boundingBox.max.x; - let yOffset = geometry.boundingBox.max.y; - intersection.uv = { x: (worldPos.x + xOffset), y: (worldPos.y + yOffset) } + this.endDraw(); + return } let screenX = (intersection.uv.x) * this.texture.image.width; let screenY = (1 - intersection.uv.y) * this.texture.image.height; From fde5cbfcd23d76705969cd313d03499afbe93879 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Thu, 2 Jul 2020 14:50:21 +0300 Subject: [PATCH 56/88] Improved continuous drawing --- src/js/shot-generator/SceneManagerR3fLarge.js | 4 +- .../components/Three/Helpers/SimpleTexture.js | 328 +++++++++++++++++- 2 files changed, 324 insertions(+), 8 deletions(-) diff --git a/src/js/shot-generator/SceneManagerR3fLarge.js b/src/js/shot-generator/SceneManagerR3fLarge.js index b824015970..d15c421a36 100644 --- a/src/js/shot-generator/SceneManagerR3fLarge.js +++ b/src/js/shot-generator/SceneManagerR3fLarge.js @@ -206,7 +206,7 @@ const SceneManagerR3fLarge = connect( let raycaster = useRef(new THREE.Raycaster()) const draw = (event) => { let keys = Object.keys(drawingTextures.current) - let {x, y} = mouse(event, gl) + let {x, y} = mouse(event, activeGL) raycaster.current.setFromCamera({x, y}, camera) let imageObjects = getImageObjects() let intersections = raycaster.current.intersectObjects(imageObjects, true) @@ -219,7 +219,7 @@ const SceneManagerR3fLarge = connect( let drawingTexture = drawingTextures.current[key]; let object = drawingTexture.material.parent.parent; if(!object || !object.visible) continue - drawingTexture.draw({x, y}, object, camera, drawingBrush) + drawingTexture.draw({x, y}, object, camera, drawingBrush, activeGL) } } diff --git a/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js b/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js index bfd2a61cf0..37e97d693d 100644 --- a/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js +++ b/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js @@ -1,6 +1,21 @@ import * as THREE from 'three' import DrawingTexture from './DrawingTexture' +const fromWorldSpaceToClipSpace = (position, camera, gl) => { + let rect = gl.domElement.getBoundingClientRect() + let width = rect.width, height = rect.height; + let widthHalf = width / 2, heightHalf = height / 2; + + //let vector = new THREE.Vector3(); + // let projector = new THREE.Projector(); + position.project(camera); + // projector.projectVector( vector.setFromMatrixPosition( position ), camera ); + + position.x = ( position.x * widthHalf ) + widthHalf; + position.y = - ( position.y * heightHalf ) + heightHalf; + return position +} + const fromClipSpaceToWorldSpace = (mousePos, camera, targetZ) => { let vector = new THREE.Vector3(); vector.set(mousePos.x, mousePos.y, 0.5); @@ -10,6 +25,20 @@ const fromClipSpaceToWorldSpace = (mousePos, camera, targetZ) => { let position = new THREE.Vector3().copy(camera.position).add(vector.multiplyScalar(distance)); return position; } +const fromClipSpaceToScreenSpace = (mousePos, gl) => { + let rect = gl.domElement.getBoundingClientRect() + let x = (mousePos.x + 1) / 2 * rect.width + let y = (-mousePos.y - 1) / 2 * rect.height + return {x, y} +} + +const mouse = (position, gl) => { + const rect = gl.domElement.getBoundingClientRect(); + let worldX = ( ( position.x ) / rect.width ) * 2 - 1; + let worldY = - ( ( position.y ) / rect.height ) * 2 + 1; + return { x: worldX, y: worldY } + } + class SimpleTexture extends DrawingTexture { constructor(){ super(); @@ -19,6 +48,15 @@ class SimpleTexture extends DrawingTexture { this.drawingCanvases.push(canvas); this.drawingCtxes.push(ctx); + + let bgeometry = new THREE.BoxBufferGeometry(0.2, 0.2, 0.2) + let material = new THREE.MeshBasicMaterial({color:"#00FF00"}) + this.topCornerBox = new THREE.Mesh(bgeometry, material) + let nmaterial = new THREE.MeshBasicMaterial({color:"#0000FF"}) + this.bottomCornerBox = new THREE.Mesh(bgeometry, nmaterial) + nmaterial = new THREE.MeshBasicMaterial({color:"#FF0000"}) + this.pointerBox = new THREE.Mesh(bgeometry, nmaterial) + this.mousePrevPos = null } getImage(mime) { @@ -33,6 +71,14 @@ class SimpleTexture extends DrawingTexture { } } + endDraw() { + super.endDraw() + this.prevImageX = null + this.prevImageY = null + this.mousePrevPos = null + this.prevIntersection = null + } + createMaterial(material) { super.createMaterial(material.map); @@ -57,21 +103,291 @@ class SimpleTexture extends DrawingTexture { this.material.needsUpdate = true; } - draw (mousePosition, object, camera, brush){ + draw (mousePosition, object, camera, brush, gl){ let intersection = super.draw(mousePosition, object, camera, brush) // If we don't have a uv coordinates and as a last resort we trying to translate mouse into object coordinate // From object coordinate we can sort of simulate uv coordinate logic for plain object // NOTE() : This won't work for any object except plain object( image object ) + if(!intersection) { - this.endDraw(); - return - } + +/* if(!this.mousePrevPos || !this.prevIntersection) return + intersection = {uv:{}} */ + //intersection = super.draw(this.mousePrevPos, object, camera, brush) + //if(!intersection) { + // console.log("New intersection", intersection) + // this.mousePrevPos = null + // this.drawingBrush.stopDrawing() + // this.drawingBrush.startDrawing() + // return + //} + //let newPos = fromClipSpaceToWorldSpace(mousePosition, camera) + // newPos.applyMatrix4(new THREE.Matrix4().getInverse(object.matrixWorld)) + let worldPos = fromClipSpaceToWorldSpace(mousePosition, camera, object.position.z) +/* this.pointerBox.position.set(0, 0, 0) + this.pointerBox.quaternion.set(0, 0, 0, 1) + this.pointerBox.scale.set(0, 0, 0) + this.pointerBox.updateMatrixWorld(true) */ + let matrixInverse = new THREE.Matrix4().getInverse(object.matrixWorld) + //worldPos.applyMatrix4(matrixInverse) + intersection = {} + + let geometry = object.getObjectByProperty("type", "Mesh").geometry; + let box = new THREE.Box3().setFromObject(object) + let rotation = new THREE.Euler(object.rotation.x, object.rotation.y, object.rotation.z); //object.rotation.clone() + let rotationalMatrix = new THREE.Matrix4().makeRotationFromEuler(rotation) + let target = new THREE.Vector3() + //plane.projectPoint(worldPos,target) + // console.log("target", target) + //console.log("WorldPos", worldPos) + // box.applyMatrix4(rotationalMatrix) + // box.max.applyEuler(rotation) + //worldPos.applyEuler(rotation) + /* let scale = object.scale.clone()//new THREE.Vector3(); + //scale.z = 0; + + let quaternion = object.worldQuaternion() + // + // scale.divideScalar(2) + let boundingBox = object.getObjectByProperty("type", "Mesh").geometry.boundingBox + scale = boundingBox.max.clone()//.sub(boundingBox.min) + // scale.applyQuaternion(quaternion.clone().inverse()) + let position = object.worldPosition() + // console.log(position) + // scale.applyMatrix4(matrixInverse) + let euler = new THREE.Euler().setFromQuaternion(quaternion.inverse(), "XYZ") + scale.y = -scale.y + scale.applyEuler(euler) + //scale.divideScalar(2) + console.log("scale", scale) + let pos = new THREE.Vector3() + let topPosition = pos.sub(scale).applyMatrix4(matrixInverse) + let bottomPosition = pos.add(scale).applyMatrix4(matrixInverse) */ + + + + //new THREE.Matrix4().compose(object.position, new THREE.Quaternion(), object.scale) + + + + + //worldPos.z = topPosition.z + //topPosition.applyMatrix4(matrixInverse) + // bottomPosition.applyMatrix4(matrixInverse) + //worldPos.applyMatrix4() + /* console.log("Before quat", worldPos.clone()) + console.log("quat", quaternion.clone()) + // worldPos.applyQuaternion(quaternion) + console.log("after quat", worldPos.clone()) + console.log("topPosition", topPosition.clone()) + // console.log("topPosition", worldPos.clone()) + console.log("bottomPosition", bottomPosition.clone()) */ + + /* topPosition.copy(this.topCornerBox.worldPosition()) + bottomPosition.copy(this.bottomCornerBox.worldPosition()) */ + + + //worldPos.applyMatrix4(matrixInverse) +/* bottomPosition.applyMatrix4(matrixInverse) + topPosition.applyMatrix4(matrixInverse) + console.log("WorldPos", worldPos.clone()) + console.log("bottomPosition", bottomPosition.clone()) + console.log("topPosition", topPosition.clone()) + console.log("this.bottomCornerBox", this.bottomCornerBox.clone()) + console.log("this.topCornerBox", this.topCornerBox.clone()) */ + + /* this.topCornerBox.position.set(topPosition.x, topPosition.y, topPosition.z) + this.topCornerBox.updateMatrixWorld(true) + this.bottomCornerBox.position.set(bottomPosition.x, bottomPosition.y, bottomPosition.z) + this.bottomCornerBox.updateMatrixWorld(true) */ + + + + + intersection = {uv: {}} + //intersection.uv.x = (worldPos.x - xOffset) / xSize + //intersection.uv.y = (worldPos.y - yOffset) / ySize + + //worldTop.applyMatrix4(matrixInverse) + //worldBottom.applyMatrix4(matrixInverse) + +/* let clonedObject = object.clone() + clonedObject.applyQuaternion(clonedObject.worldQuaternion().inverse()) + clonedObject.updateMatrixWorld(true) + let newMatrixInverse = new THREE.Matrix4().getInverse(clonedObject.matrixWorld) + // + //worldPos.z = topPosition.z + console.log("Object world prev", worldPos.clone()) + topPosition.applyMatrix4(clonedObject.matrixWorld) + bottomPosition.applyMatrix4(clonedObject.matrixWorld) + //worldPos.applyMatrix4(clonedObject.matrixWorld) */ + +/* this.topCornerBox.position.set(topPosition.x, topPosition.y, topPosition.z) + this.topCornerBox.updateMatrixWorld(true) + this.bottomCornerBox.position.set(bottomPosition.x, bottomPosition.y, bottomPosition.z) + this.bottomCornerBox.updateMatrixWorld(true) + + object.parent.add(this.bottomCornerBox) + object.parent.add(this.topCornerBox) + this.pointerBox.position.set(worldPos.x, worldPos.y, worldPos.z) + this.pointerBox.updateMatrixWorld(true) + object.add(this.pointerBox) + object.updateMatrixWorld(true) + this.pointerBox.updateMatrixWorld(true) + worldPos.copy(this.pointerBox.position) + worldPos.applyMatrix4(object.matrixWorld) + worldPos.applyQuaternion(clonedObject.worldQuaternion().inverse()) */ + //this.pointerBox.position.applyMatrix4(object.matrixWorld) + // object.parent.add(this.pointerBox) + //this.pointerBox.position.applyQuaternion(clonedObject.worldQuaternion().inverse()) + this.pointerBox.updateMatrixWorld(true) + + + // worldPos.applyMatrix4(clonedObject.matrixWorld) + //this.pointerBox.updateMatrixWorld(true) + // this.pointerBox.applyMatrix4(matrixInverse) + //this.pointerBox.applyMatrix4(clonedObject.matrixWorld) + console.log(this.pointerBox.clone()) + // object.add(this.pointerBox) + // this.pointerBox.position.set(worldPos.x, worldPos.y, worldPos.z) + //worldPos.copy(this.pointerBox.worldPosition()) + //object.parent.add(this.pointerBox) + // this.pointerBox.updateMatrixWorld(true) + // this.pointerBox.position.applyMatrix4(object.matrixWorld) + // this.pointerBox.position.applyMatrix4(clonedObject.matrixWorld) + // this.pointerBox.updateMatrixWorld(true) + + // this.pointerBox.matrixWorld.decompose( this.pointerBox.position, this.pointerBox.quaternion, this.pointerBox.scale) + //object.parent.add(this.pointerBox) + // this.pointerBox.applyMatrix4(newMatrixInverse) + // this.pointerBox.updateMatrixWorld(true) + + +/* console.log("Object top", topPosition) + console.log("Object bottom", bottomPosition) + console.log("Object world new", worldPos) + //console.log("worldPos", worldPos) + let xSize = (topPosition.x - bottomPosition.x) + let ySize = (topPosition.y - bottomPosition.y) + let xOffset = ySize / 2; + let yOffset = xSize / 2; + + + bottomPosition.sub(topPosition) + worldPos.sub(topPosition) + worldPos.divide(bottomPosition) + intersection.uv.x = worldPos.x + intersection.uv.y = 1 - worldPos.y + /* if(bottomPosition.x > topPosition.x && bottomPosition.y < topPosition.y) { + + } else { + topPosition.sub(bottomPosition) + worldPos.sub(bottomPosition) + worldPos.divide(topPosition) + intersection.uv.x = 1 - worldPos.x + intersection.uv.y = worldPos.y + } + */ + + //console.log(intersection.uv) + // return + //return + /* bottomPosition.sub(topPosition) + worldPos.sub(topPosition) + worldPos.divide(bottomPosition) */ + // console.log(worldPos) + // console.log("xOffset", xOffset, yOffset) + + + //#region Clip space to world space method + let scale = object.scale.clone()//new THREE.Vector3(); + scale.z = 0; + scale.y = -scale.y + let quaternion = object.worldQuaternion() + scale.applyQuaternion(quaternion) + scale.divideScalar(2) + let intersectPos = worldPos.clone() + let position = object.worldPosition()//.applyEuler(euler) + let topPosition = position.clone().sub(scale) + let bottomPosition = position.clone().add(scale) +/* console.log("topPosition", topPosition) + console.log("bottomPosition", bottomPosition) */ + let top = fromWorldSpaceToClipSpace(topPosition, camera, gl) + let bottom = fromWorldSpaceToClipSpace(bottomPosition, camera, gl) +/* console.log("top", top) + console.log("bottom", bottom) */ + + let topMouse = mouse(top, gl) + let bottomMouse = mouse(bottom, gl) + let worldMouse = mouse(worldPos, gl) +/* console.log("topMouse", topMouse) */ +/* console.log("bottomMouse", bottomMouse) */ + let worldTop = fromClipSpaceToWorldSpace(topMouse, camera, object.position.z) + let worldBottom = fromClipSpaceToWorldSpace(bottomMouse, camera, object.position.z) + /* console.log("WorldTop", worldTop) + console.log("worldBottom", worldBottom) + console.log("worldPos", worldPos) */ + if(worldBottom.x > worldTop.x && worldBottom.y < worldTop.y) { + console.log("top is top") + worldBottom.sub(worldTop) + worldPos.sub(worldTop) + worldPos.divide(worldBottom) + intersection.uv.x = worldPos.x + intersection.uv.y = 1 - worldPos.y + } else { + console.log("bottom is top") + worldTop.sub(worldBottom) + worldPos.sub(worldBottom) + worldPos.divide(worldTop) + intersection.uv.x = 1 - worldPos.x + intersection.uv.y = worldPos.y + } + //#endregion + + +/* let rect = gl.domElement.getBoundingClientRect() + let { x:prevScreenX, y:prevScreenY } = fromClipSpaceToScreenSpace(this.mousePrevPos, gl) + let { x:screenX, y:screenY } = fromClipSpaceToScreenSpace(mousePosition, gl) + let deltaX = screenX - prevScreenX + let deltaY = screenY - prevScreenY + deltaX = (deltaX * this.texture.image.width / rect.width) * 10 + deltaY = (deltaY * this.texture.image.height / rect.height) * 10 + console.log("_____X_____") + console.log("prevScreenX", prevScreenX) + console.log("screenX", screenX) + console.log("deltaX", deltaX) + console.log("this.prevImageX",this.prevImageX) + console.log("_____X_____") + console.log("_____Y_____") + console.log("prevScreenY", prevScreenY) + console.log("screenY", screenY) + console.log("deltaY", deltaY) + console.log("this.prevImageY",this.prevImageY) + console.log("_____Y_____") + console.log("prevIntersection.uv", this.prevIntersection) + intersection.uv.x = (this.prevImageX + deltaX) / this.texture.image.width + intersection.uv.y = 1 - ((this.prevImageY + deltaY) / this.texture.image.height) + // intersection.uv.y = (this.prevIntersection.y * newPos.y) / this.mousePrevPos.y + // intersection.uv = { x: -(worldPos.x + xOffset) / xSize, y: -(worldPos.y + yOffset) / ySize } + console.log("intersection.uv", intersection.uv) + console.log("this.mousePrevPos", this.mousePrevPos) */ + } + if(Number.isNaN(intersection.uv.x) || Number.isNaN(intersection.uv.y)) return let screenX = (intersection.uv.x) * this.texture.image.width; let screenY = (1 - intersection.uv.y) * this.texture.image.height; this.drawingBrush.draw({ x: screenX, y: screenY }, brush) - + this.texture.needsUpdate = true; - + /* if(intersection.point) { + this.prevImageX = screenX + this.prevImageY = screenY + this.mousePrevPos = mousePosition//fromClipSpaceToWorldSpace(mousePosition, camera) + // this.mousePrevPos.applyMatrix4(new THREE.Matrix4().getInverse(object.matrixWorld)) + this.prevIntersection = { x: intersection.uv.x, y: intersection.uv.y } + } else { + this.endDraw(); + this.prepareToDraw(); + } */ } } export default SimpleTexture; \ No newline at end of file From 532031bd313e10fd25b59e5edc6632c0729238ee Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Thu, 2 Jul 2020 15:35:42 +0300 Subject: [PATCH 57/88] Cleaned up code --- .../components/Three/Helpers/SimpleTexture.js | 340 ++---------------- .../utils/ClipSpaceToWorldSpace.js | 11 + .../utils/WorldSpaceToClipSpace.js | 13 + 3 files changed, 54 insertions(+), 310 deletions(-) create mode 100644 src/js/shot-generator/utils/ClipSpaceToWorldSpace.js create mode 100644 src/js/shot-generator/utils/WorldSpaceToClipSpace.js diff --git a/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js b/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js index 37e97d693d..358cf37781 100644 --- a/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js +++ b/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js @@ -1,36 +1,7 @@ import * as THREE from 'three' import DrawingTexture from './DrawingTexture' - -const fromWorldSpaceToClipSpace = (position, camera, gl) => { - let rect = gl.domElement.getBoundingClientRect() - let width = rect.width, height = rect.height; - let widthHalf = width / 2, heightHalf = height / 2; - - //let vector = new THREE.Vector3(); - // let projector = new THREE.Projector(); - position.project(camera); - // projector.projectVector( vector.setFromMatrixPosition( position ), camera ); - - position.x = ( position.x * widthHalf ) + widthHalf; - position.y = - ( position.y * heightHalf ) + heightHalf; - return position -} - -const fromClipSpaceToWorldSpace = (mousePos, camera, targetZ) => { - let vector = new THREE.Vector3(); - vector.set(mousePos.x, mousePos.y, 0.5); - vector.unproject(camera); - vector.sub(camera.position).normalize(); - let distance = (targetZ - camera.position.z) / vector.z; - let position = new THREE.Vector3().copy(camera.position).add(vector.multiplyScalar(distance)); - return position; -} -const fromClipSpaceToScreenSpace = (mousePos, gl) => { - let rect = gl.domElement.getBoundingClientRect() - let x = (mousePos.x + 1) / 2 * rect.width - let y = (-mousePos.y - 1) / 2 * rect.height - return {x, y} -} +import fromWorldSpaceToClipSpace from "../../../utils/WorldSpaceToClipSpace" +import fromClipSpaceToWorldSpace from "../../../utils/ClipSpaceToWorldSpace" const mouse = (position, gl) => { const rect = gl.domElement.getBoundingClientRect(); @@ -48,15 +19,6 @@ class SimpleTexture extends DrawingTexture { this.drawingCanvases.push(canvas); this.drawingCtxes.push(ctx); - - let bgeometry = new THREE.BoxBufferGeometry(0.2, 0.2, 0.2) - let material = new THREE.MeshBasicMaterial({color:"#00FF00"}) - this.topCornerBox = new THREE.Mesh(bgeometry, material) - let nmaterial = new THREE.MeshBasicMaterial({color:"#0000FF"}) - this.bottomCornerBox = new THREE.Mesh(bgeometry, nmaterial) - nmaterial = new THREE.MeshBasicMaterial({color:"#FF0000"}) - this.pointerBox = new THREE.Mesh(bgeometry, nmaterial) - this.mousePrevPos = null } getImage(mime) { @@ -73,10 +35,6 @@ class SimpleTexture extends DrawingTexture { endDraw() { super.endDraw() - this.prevImageX = null - this.prevImageY = null - this.mousePrevPos = null - this.prevIntersection = null } createMaterial(material) { @@ -110,267 +68,39 @@ class SimpleTexture extends DrawingTexture { // NOTE() : This won't work for any object except plain object( image object ) if(!intersection) { - -/* if(!this.mousePrevPos || !this.prevIntersection) return - intersection = {uv:{}} */ - //intersection = super.draw(this.mousePrevPos, object, camera, brush) - //if(!intersection) { - // console.log("New intersection", intersection) - // this.mousePrevPos = null - // this.drawingBrush.stopDrawing() - // this.drawingBrush.startDrawing() - // return - //} - //let newPos = fromClipSpaceToWorldSpace(mousePosition, camera) - // newPos.applyMatrix4(new THREE.Matrix4().getInverse(object.matrixWorld)) - let worldPos = fromClipSpaceToWorldSpace(mousePosition, camera, object.position.z) -/* this.pointerBox.position.set(0, 0, 0) - this.pointerBox.quaternion.set(0, 0, 0, 1) - this.pointerBox.scale.set(0, 0, 0) - this.pointerBox.updateMatrixWorld(true) */ - let matrixInverse = new THREE.Matrix4().getInverse(object.matrixWorld) - //worldPos.applyMatrix4(matrixInverse) - intersection = {} - - let geometry = object.getObjectByProperty("type", "Mesh").geometry; - let box = new THREE.Box3().setFromObject(object) - let rotation = new THREE.Euler(object.rotation.x, object.rotation.y, object.rotation.z); //object.rotation.clone() - let rotationalMatrix = new THREE.Matrix4().makeRotationFromEuler(rotation) - let target = new THREE.Vector3() - //plane.projectPoint(worldPos,target) - // console.log("target", target) - //console.log("WorldPos", worldPos) - // box.applyMatrix4(rotationalMatrix) - // box.max.applyEuler(rotation) - //worldPos.applyEuler(rotation) - /* let scale = object.scale.clone()//new THREE.Vector3(); - //scale.z = 0; - - let quaternion = object.worldQuaternion() - // - // scale.divideScalar(2) - let boundingBox = object.getObjectByProperty("type", "Mesh").geometry.boundingBox - scale = boundingBox.max.clone()//.sub(boundingBox.min) - // scale.applyQuaternion(quaternion.clone().inverse()) - let position = object.worldPosition() - // console.log(position) - // scale.applyMatrix4(matrixInverse) - let euler = new THREE.Euler().setFromQuaternion(quaternion.inverse(), "XYZ") - scale.y = -scale.y - scale.applyEuler(euler) - //scale.divideScalar(2) - console.log("scale", scale) - let pos = new THREE.Vector3() - let topPosition = pos.sub(scale).applyMatrix4(matrixInverse) - let bottomPosition = pos.add(scale).applyMatrix4(matrixInverse) */ - - - - //new THREE.Matrix4().compose(object.position, new THREE.Quaternion(), object.scale) - - - - - //worldPos.z = topPosition.z - //topPosition.applyMatrix4(matrixInverse) - // bottomPosition.applyMatrix4(matrixInverse) - //worldPos.applyMatrix4() - /* console.log("Before quat", worldPos.clone()) - console.log("quat", quaternion.clone()) - // worldPos.applyQuaternion(quaternion) - console.log("after quat", worldPos.clone()) - console.log("topPosition", topPosition.clone()) - // console.log("topPosition", worldPos.clone()) - console.log("bottomPosition", bottomPosition.clone()) */ - - /* topPosition.copy(this.topCornerBox.worldPosition()) - bottomPosition.copy(this.bottomCornerBox.worldPosition()) */ - - - //worldPos.applyMatrix4(matrixInverse) -/* bottomPosition.applyMatrix4(matrixInverse) - topPosition.applyMatrix4(matrixInverse) - console.log("WorldPos", worldPos.clone()) - console.log("bottomPosition", bottomPosition.clone()) - console.log("topPosition", topPosition.clone()) - console.log("this.bottomCornerBox", this.bottomCornerBox.clone()) - console.log("this.topCornerBox", this.topCornerBox.clone()) */ - - /* this.topCornerBox.position.set(topPosition.x, topPosition.y, topPosition.z) - this.topCornerBox.updateMatrixWorld(true) - this.bottomCornerBox.position.set(bottomPosition.x, bottomPosition.y, bottomPosition.z) - this.bottomCornerBox.updateMatrixWorld(true) */ - - - - - intersection = {uv: {}} - //intersection.uv.x = (worldPos.x - xOffset) / xSize - //intersection.uv.y = (worldPos.y - yOffset) / ySize - - //worldTop.applyMatrix4(matrixInverse) - //worldBottom.applyMatrix4(matrixInverse) - -/* let clonedObject = object.clone() - clonedObject.applyQuaternion(clonedObject.worldQuaternion().inverse()) - clonedObject.updateMatrixWorld(true) - let newMatrixInverse = new THREE.Matrix4().getInverse(clonedObject.matrixWorld) - // - //worldPos.z = topPosition.z - console.log("Object world prev", worldPos.clone()) - topPosition.applyMatrix4(clonedObject.matrixWorld) - bottomPosition.applyMatrix4(clonedObject.matrixWorld) - //worldPos.applyMatrix4(clonedObject.matrixWorld) */ - -/* this.topCornerBox.position.set(topPosition.x, topPosition.y, topPosition.z) - this.topCornerBox.updateMatrixWorld(true) - this.bottomCornerBox.position.set(bottomPosition.x, bottomPosition.y, bottomPosition.z) - this.bottomCornerBox.updateMatrixWorld(true) - - object.parent.add(this.bottomCornerBox) - object.parent.add(this.topCornerBox) - this.pointerBox.position.set(worldPos.x, worldPos.y, worldPos.z) - this.pointerBox.updateMatrixWorld(true) - object.add(this.pointerBox) - object.updateMatrixWorld(true) - this.pointerBox.updateMatrixWorld(true) - worldPos.copy(this.pointerBox.position) - worldPos.applyMatrix4(object.matrixWorld) - worldPos.applyQuaternion(clonedObject.worldQuaternion().inverse()) */ - //this.pointerBox.position.applyMatrix4(object.matrixWorld) - // object.parent.add(this.pointerBox) - //this.pointerBox.position.applyQuaternion(clonedObject.worldQuaternion().inverse()) - this.pointerBox.updateMatrixWorld(true) - - - // worldPos.applyMatrix4(clonedObject.matrixWorld) - //this.pointerBox.updateMatrixWorld(true) - // this.pointerBox.applyMatrix4(matrixInverse) - //this.pointerBox.applyMatrix4(clonedObject.matrixWorld) - console.log(this.pointerBox.clone()) - // object.add(this.pointerBox) - // this.pointerBox.position.set(worldPos.x, worldPos.y, worldPos.z) - //worldPos.copy(this.pointerBox.worldPosition()) - //object.parent.add(this.pointerBox) - // this.pointerBox.updateMatrixWorld(true) - // this.pointerBox.position.applyMatrix4(object.matrixWorld) - // this.pointerBox.position.applyMatrix4(clonedObject.matrixWorld) - // this.pointerBox.updateMatrixWorld(true) - - // this.pointerBox.matrixWorld.decompose( this.pointerBox.position, this.pointerBox.quaternion, this.pointerBox.scale) - //object.parent.add(this.pointerBox) - // this.pointerBox.applyMatrix4(newMatrixInverse) - // this.pointerBox.updateMatrixWorld(true) - - -/* console.log("Object top", topPosition) - console.log("Object bottom", bottomPosition) - console.log("Object world new", worldPos) - //console.log("worldPos", worldPos) - let xSize = (topPosition.x - bottomPosition.x) - let ySize = (topPosition.y - bottomPosition.y) - let xOffset = ySize / 2; - let yOffset = xSize / 2; - - - bottomPosition.sub(topPosition) - worldPos.sub(topPosition) - worldPos.divide(bottomPosition) - intersection.uv.x = worldPos.x - intersection.uv.y = 1 - worldPos.y - /* if(bottomPosition.x > topPosition.x && bottomPosition.y < topPosition.y) { - + let worldPos = fromClipSpaceToWorldSpace(mousePosition, camera, object.position.z) + intersection = {uv: {}} + this.pointerBox.updateMatrixWorld(true) + //#region Clip space to world space method + let scale = object.scale.clone() + scale.z = 0; + scale.y = -scale.y + let quaternion = object.worldQuaternion() + scale.applyQuaternion(quaternion) + scale.divideScalar(2) + let position = object.worldPosition() + let topPosition = position.clone().sub(scale) + let bottomPosition = position.clone().add(scale) + let top = fromWorldSpaceToClipSpace(topPosition, camera, gl) + let bottom = fromWorldSpaceToClipSpace(bottomPosition, camera, gl) + let topMouse = mouse(top, gl) + let bottomMouse = mouse(bottom, gl) + let worldTop = fromClipSpaceToWorldSpace(topMouse, camera, object.position.z) + let worldBottom = fromClipSpaceToWorldSpace(bottomMouse, camera, object.position.z) + if(worldBottom.x > worldTop.x && worldBottom.y < worldTop.y) { + worldBottom.sub(worldTop) + worldPos.sub(worldTop) + worldPos.divide(worldBottom) + intersection.uv.x = worldPos.x + intersection.uv.y = 1 - worldPos.y } else { - topPosition.sub(bottomPosition) - worldPos.sub(bottomPosition) - worldPos.divide(topPosition) + worldTop.sub(worldBottom) + worldPos.sub(worldBottom) + worldPos.divide(worldTop) intersection.uv.x = 1 - worldPos.x intersection.uv.y = worldPos.y } - */ - - //console.log(intersection.uv) - // return - //return - /* bottomPosition.sub(topPosition) - worldPos.sub(topPosition) - worldPos.divide(bottomPosition) */ - // console.log(worldPos) - // console.log("xOffset", xOffset, yOffset) - - - //#region Clip space to world space method - let scale = object.scale.clone()//new THREE.Vector3(); - scale.z = 0; - scale.y = -scale.y - let quaternion = object.worldQuaternion() - scale.applyQuaternion(quaternion) - scale.divideScalar(2) - let intersectPos = worldPos.clone() - let position = object.worldPosition()//.applyEuler(euler) - let topPosition = position.clone().sub(scale) - let bottomPosition = position.clone().add(scale) -/* console.log("topPosition", topPosition) - console.log("bottomPosition", bottomPosition) */ - let top = fromWorldSpaceToClipSpace(topPosition, camera, gl) - let bottom = fromWorldSpaceToClipSpace(bottomPosition, camera, gl) -/* console.log("top", top) - console.log("bottom", bottom) */ - - let topMouse = mouse(top, gl) - let bottomMouse = mouse(bottom, gl) - let worldMouse = mouse(worldPos, gl) -/* console.log("topMouse", topMouse) */ -/* console.log("bottomMouse", bottomMouse) */ - let worldTop = fromClipSpaceToWorldSpace(topMouse, camera, object.position.z) - let worldBottom = fromClipSpaceToWorldSpace(bottomMouse, camera, object.position.z) - /* console.log("WorldTop", worldTop) - console.log("worldBottom", worldBottom) - console.log("worldPos", worldPos) */ - if(worldBottom.x > worldTop.x && worldBottom.y < worldTop.y) { - console.log("top is top") - worldBottom.sub(worldTop) - worldPos.sub(worldTop) - worldPos.divide(worldBottom) - intersection.uv.x = worldPos.x - intersection.uv.y = 1 - worldPos.y - } else { - console.log("bottom is top") - worldTop.sub(worldBottom) - worldPos.sub(worldBottom) - worldPos.divide(worldTop) - intersection.uv.x = 1 - worldPos.x - intersection.uv.y = worldPos.y - } //#endregion - - -/* let rect = gl.domElement.getBoundingClientRect() - let { x:prevScreenX, y:prevScreenY } = fromClipSpaceToScreenSpace(this.mousePrevPos, gl) - let { x:screenX, y:screenY } = fromClipSpaceToScreenSpace(mousePosition, gl) - let deltaX = screenX - prevScreenX - let deltaY = screenY - prevScreenY - deltaX = (deltaX * this.texture.image.width / rect.width) * 10 - deltaY = (deltaY * this.texture.image.height / rect.height) * 10 - console.log("_____X_____") - console.log("prevScreenX", prevScreenX) - console.log("screenX", screenX) - console.log("deltaX", deltaX) - console.log("this.prevImageX",this.prevImageX) - console.log("_____X_____") - console.log("_____Y_____") - console.log("prevScreenY", prevScreenY) - console.log("screenY", screenY) - console.log("deltaY", deltaY) - console.log("this.prevImageY",this.prevImageY) - console.log("_____Y_____") - console.log("prevIntersection.uv", this.prevIntersection) - intersection.uv.x = (this.prevImageX + deltaX) / this.texture.image.width - intersection.uv.y = 1 - ((this.prevImageY + deltaY) / this.texture.image.height) - // intersection.uv.y = (this.prevIntersection.y * newPos.y) / this.mousePrevPos.y - // intersection.uv = { x: -(worldPos.x + xOffset) / xSize, y: -(worldPos.y + yOffset) / ySize } - console.log("intersection.uv", intersection.uv) - console.log("this.mousePrevPos", this.mousePrevPos) */ } if(Number.isNaN(intersection.uv.x) || Number.isNaN(intersection.uv.y)) return let screenX = (intersection.uv.x) * this.texture.image.width; @@ -378,16 +108,6 @@ class SimpleTexture extends DrawingTexture { this.drawingBrush.draw({ x: screenX, y: screenY }, brush) this.texture.needsUpdate = true; - /* if(intersection.point) { - this.prevImageX = screenX - this.prevImageY = screenY - this.mousePrevPos = mousePosition//fromClipSpaceToWorldSpace(mousePosition, camera) - // this.mousePrevPos.applyMatrix4(new THREE.Matrix4().getInverse(object.matrixWorld)) - this.prevIntersection = { x: intersection.uv.x, y: intersection.uv.y } - } else { - this.endDraw(); - this.prepareToDraw(); - } */ } } export default SimpleTexture; \ No newline at end of file diff --git a/src/js/shot-generator/utils/ClipSpaceToWorldSpace.js b/src/js/shot-generator/utils/ClipSpaceToWorldSpace.js new file mode 100644 index 0000000000..c6a25f84aa --- /dev/null +++ b/src/js/shot-generator/utils/ClipSpaceToWorldSpace.js @@ -0,0 +1,11 @@ +const fromClipSpaceToWorldSpace = (mousePos, camera, targetZ) => { + let vector = new THREE.Vector3(); + vector.set(mousePos.x, mousePos.y, 0.5); + vector.unproject(camera); + vector.sub(camera.position).normalize(); + let distance = (targetZ - camera.position.z) / vector.z; + let position = new THREE.Vector3().copy(camera.position).add(vector.multiplyScalar(distance)); + return position; +} + +export default fromClipSpaceToWorldSpace; \ No newline at end of file diff --git a/src/js/shot-generator/utils/WorldSpaceToClipSpace.js b/src/js/shot-generator/utils/WorldSpaceToClipSpace.js new file mode 100644 index 0000000000..a69cbf1069 --- /dev/null +++ b/src/js/shot-generator/utils/WorldSpaceToClipSpace.js @@ -0,0 +1,13 @@ +const fromWorldSpaceToClipSpace = (position, camera, gl) => { + let rect = gl.domElement.getBoundingClientRect(); + let width = rect.width, height = rect.height; + let widthHalf = width / 2, heightHalf = height / 2; + + position.project(camera); + + position.x = ( position.x * widthHalf ) + widthHalf; + position.y = - ( position.y * heightHalf ) + heightHalf; + return position; +} + +export default fromWorldSpaceToClipSpace; \ No newline at end of file From b3a99cf8f6e6c4b1eb6140d9014da5d44563a2cf Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Thu, 2 Jul 2020 15:48:49 +0300 Subject: [PATCH 58/88] Fixed drawing should affect changed objects --- src/js/shot-generator/components/Three/Helpers/SimpleTexture.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js b/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js index 358cf37781..37dfa087db 100644 --- a/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js +++ b/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js @@ -68,9 +68,9 @@ class SimpleTexture extends DrawingTexture { // NOTE() : This won't work for any object except plain object( image object ) if(!intersection) { + if(!this.isChanged) return let worldPos = fromClipSpaceToWorldSpace(mousePosition, camera, object.position.z) intersection = {uv: {}} - this.pointerBox.updateMatrixWorld(true) //#region Clip space to world space method let scale = object.scale.clone() scale.z = 0; From 0bfc9858932c6126f5a94696ae9edbc1fe5c8119 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Wed, 15 Jul 2020 10:36:03 +0300 Subject: [PATCH 59/88] Removed creation of brush in image creation --- src/js/shared/actions/scene-object-creators.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/js/shared/actions/scene-object-creators.js b/src/js/shared/actions/scene-object-creators.js index 27238ec69b..9bca20a030 100644 --- a/src/js/shared/actions/scene-object-creators.js +++ b/src/js/shared/actions/scene-object-creators.js @@ -168,7 +168,6 @@ const createImage = (id, camera, room) => { x, y, z: 1, rotation: { x: 0, y: rotation, z: 0 }, - mesh: {size:2, color:"#000000"}, visible: true, opacity: 1, visibleToCam: true, From 65af7bedc9c0a22fdc24aca44d15eebc22428d9b Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Wed, 15 Jul 2020 10:49:06 +0300 Subject: [PATCH 60/88] Added drawing of point on click --- src/js/shot-generator/SceneManagerR3fLarge.js | 1 + .../components/Three/Helpers/Brushes/SimpleBrush.js | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/js/shot-generator/SceneManagerR3fLarge.js b/src/js/shot-generator/SceneManagerR3fLarge.js index d15c421a36..2c06662ca6 100644 --- a/src/js/shot-generator/SceneManagerR3fLarge.js +++ b/src/js/shot-generator/SceneManagerR3fLarge.js @@ -234,6 +234,7 @@ const SceneManagerR3fLarge = connect( if(drawingSceneTexture.current && drawingSceneTexture.current.texture) { drawingSceneTexture.current.texture.prepareToDraw() } + draw(event) gl.domElement.addEventListener('mousemove', draw) } diff --git a/src/js/shot-generator/components/Three/Helpers/Brushes/SimpleBrush.js b/src/js/shot-generator/components/Three/Helpers/Brushes/SimpleBrush.js index f4dd2ef516..d35c3e79e3 100644 --- a/src/js/shot-generator/components/Three/Helpers/Brushes/SimpleBrush.js +++ b/src/js/shot-generator/components/Three/Helpers/Brushes/SimpleBrush.js @@ -33,7 +33,6 @@ class SimpleBrush extends Brush { constructor(drawingCtx) { super(drawingCtx); - // this.drawingCtx.lineJoin = true; } draw(currentPos, brush) { @@ -67,7 +66,6 @@ class SimpleBrush extends Brush { p1 = getVector2FromBuffer(this.positionBuffer, i) p2 = getVector2FromBuffer(this.positionBuffer, i+1) } - this.drawingCtx.lineTo(p1.x, p1.y) this.drawingCtx.stroke(); } } From 7bbc6d5aa846663290881b56cd199b3d94db219e Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Wed, 15 Jul 2020 11:08:00 +0300 Subject: [PATCH 61/88] Refactored brushes and drawing materials --- .../components/Three/Helpers/Brushes/Brush.js | 6 +- .../Three/Helpers/Brushes/EraserBrush.js | 15 ++--- .../Three/Helpers/Brushes/PointBuffer.js | 8 +-- .../Three/Helpers/Brushes/SimpleBrush.js | 51 +++++++------- .../components/Three/Helpers/SimpleTexture.js | 66 +++++++++---------- .../Three/helpers/CubeTextureCreator.js | 10 +-- .../Three/helpers/DrawingTexture.js | 8 +-- .../Three/helpers/cubeMapDrawingTexture.js | 4 +- 8 files changed, 83 insertions(+), 85 deletions(-) diff --git a/src/js/shot-generator/components/Three/Helpers/Brushes/Brush.js b/src/js/shot-generator/components/Three/Helpers/Brushes/Brush.js index 730d0f89e8..6c845635bc 100644 --- a/src/js/shot-generator/components/Three/Helpers/Brushes/Brush.js +++ b/src/js/shot-generator/components/Three/Helpers/Brushes/Brush.js @@ -21,11 +21,11 @@ class Brush { } set DrawingContext(value) { - this.drawingCtx = value + this.drawingCtx = value; } resetMeshPos() { - this.prevPos = null + this.prevPos = null; } draw(currentPos, brush) { @@ -38,4 +38,4 @@ class Brush { } } } -export default Brush +export default Brush; diff --git a/src/js/shot-generator/components/Three/Helpers/Brushes/EraserBrush.js b/src/js/shot-generator/components/Three/Helpers/Brushes/EraserBrush.js index a25b8e05ea..d8448f4d42 100644 --- a/src/js/shot-generator/components/Three/Helpers/Brushes/EraserBrush.js +++ b/src/js/shot-generator/components/Three/Helpers/Brushes/EraserBrush.js @@ -7,8 +7,7 @@ class EraserBrush extends Brush { draw(currentPos, brush) { super.draw(currentPos, brush); this.drawingCtx.fillStyle = 'white'; - let prevX - let prevY + let prevX, prevY; if(this.positionBuffer.currentLength === 0) { prevX = currentPos.x; prevY = currentPos.y; @@ -20,24 +19,24 @@ class EraserBrush extends Brush { let circle = new Path2D(); let xOffset = currentPos.x - prevX; let yOffset = currentPos.y - prevY; - let length + let length; if(Math.abs(xOffset) < Math.abs(yOffset)) { - length = Math.abs(yOffset) + length = Math.abs(yOffset); } else { - length = Math.abs(xOffset) + length = Math.abs(xOffset); } xOffset /= length; yOffset /= length; - let size = this.brushSize + let size = this.brushSize; for(let i = 0; i < length; i++) { let x = xOffset * i; let y = yOffset * i; circle.moveTo(prevX + x, prevY + y); - circle.arc(prevX + x, prevY + y, size, 0, 2 * Math.PI) + circle.arc(prevX + x, prevY + y, size, 0, 2 * Math.PI); } this.drawingCtx.stroke(); - this.drawingCtx.fill(circle) + this.drawingCtx.fill(circle); this.positionBuffer.addElements(currentPos.x, currentPos.y); } } diff --git a/src/js/shot-generator/components/Three/Helpers/Brushes/PointBuffer.js b/src/js/shot-generator/components/Three/Helpers/Brushes/PointBuffer.js index 7e8939d75d..2a20f95097 100644 --- a/src/js/shot-generator/components/Three/Helpers/Brushes/PointBuffer.js +++ b/src/js/shot-generator/components/Three/Helpers/Brushes/PointBuffer.js @@ -6,12 +6,12 @@ class PointBuffer { } addElements(...elements) { - let predictedLength = this.currentLength * this.pointsAmount + elements.length + let predictedLength = this.currentLength * this.pointsAmount + elements.length; if(this.buffer.length < predictedLength) { let newLength = this.buffer.length * 2; while(newLength < predictedLength) { - newLength *= 2 + newLength *= 2; } let resizedBuffer = new Int16Array(newLength); resizedBuffer.set(this.buffer); @@ -22,8 +22,8 @@ class PointBuffer { } getElements(index) { - let offsetIndex = index * this.pointsAmount - return this.buffer.subarray(offsetIndex, offsetIndex + this.pointsAmount) + let offsetIndex = index * this.pointsAmount; + return this.buffer.subarray(offsetIndex, offsetIndex + this.pointsAmount); } flushArray() { diff --git a/src/js/shot-generator/components/Three/Helpers/Brushes/SimpleBrush.js b/src/js/shot-generator/components/Three/Helpers/Brushes/SimpleBrush.js index d35c3e79e3..144a665ed9 100644 --- a/src/js/shot-generator/components/Three/Helpers/Brushes/SimpleBrush.js +++ b/src/js/shot-generator/components/Three/Helpers/Brushes/SimpleBrush.js @@ -7,27 +7,27 @@ const midPointsBtw = (p1, p2) => { } const getDifferenceTo = (from, to) => { - return {x:from.x - to.x, y: from.y - to.y} + return {x:from.x - to.x, y: from.y - to.y}; } const getDistanceTo = (from, to) => { - const diff = getDifferenceTo(from, to) - return Math.sqrt(Math.pow(diff.x, 2) + Math.pow(diff.y, 2)) + const diff = getDifferenceTo(from, to); + return Math.sqrt(Math.pow(diff.x, 2) + Math.pow(diff.y, 2)); } const getAngleTo = (from, to) => { - const diff = getDifferenceTo(from, to) - return Math.atan2(diff.y, diff.x) + const diff = getDifferenceTo(from, to); + return Math.atan2(diff.y, diff.x); } const moveByAngle = (angle, distance, point) => { - const angleRotated = angle + (Math.PI / 2) - let x = point.x + (Math.sin(angleRotated) * distance) - let y = point.y - (Math.cos(angleRotated) * distance) - return {x, y} + const angleRotated = angle + (Math.PI / 2); + let x = point.x + (Math.sin(angleRotated) * distance); + let y = point.y - (Math.cos(angleRotated) * distance); + return {x, y}; } const getVector2FromBuffer = (buffer, index) => { let elements = buffer.getElements(index); - return { x:elements[0], y:elements[1] } + return { x:elements[0], y:elements[1] }; } class SimpleBrush extends Brush { @@ -40,31 +40,30 @@ class SimpleBrush extends Brush { this.drawingCtx.strokeStyle = brush.color; this.drawingCtx.fillStyle = brush.color; this.drawingCtx.lineWidth = this.brushSize * 2; - this.drawingCtx.lineJoin = 'round' - this.drawingCtx.lineCap = 'round' - let prevPos + this.drawingCtx.lineJoin = this.drawingCtx.lineCap = 'round'; + let prevPos; if(this.positionBuffer.currentLength === 0) { - prevPos = currentPos + prevPos = currentPos; } else { prevPos = getVector2FromBuffer(this.positionBuffer, this.positionBuffer.currentLength - 1); } if(this.positionBuffer.currentLength === 0) { this.positionBuffer.addElements(currentPos.x, currentPos.y); - return + return; } - this.drawingCtx.moveTo(currentPos.x, currentPos.y) - this.drawingCtx.beginPath() - let distance = getDistanceTo(currentPos, prevPos) - let angle = getAngleTo(currentPos, prevPos) - let newPos = moveByAngle(angle, distance, prevPos) + this.drawingCtx.moveTo(currentPos.x, currentPos.y); + this.drawingCtx.beginPath(); + let distance = getDistanceTo(currentPos, prevPos); + let angle = getAngleTo(currentPos, prevPos); + let newPos = moveByAngle(angle, distance, prevPos); this.positionBuffer.addElements(newPos.x, newPos.y); - let p1 = getVector2FromBuffer(this.positionBuffer, 0) - let p2 = getVector2FromBuffer(this.positionBuffer, 1) + let p1 = getVector2FromBuffer(this.positionBuffer, 0); + let p2 = getVector2FromBuffer(this.positionBuffer, 1); for(let i = 1, length = this.positionBuffer.currentLength; i < length; i++) { - let midpoint = midPointsBtw(p1, p2) - this.drawingCtx.quadraticCurveTo(p1.x, p1.y, midpoint.x, midpoint.y) - p1 = getVector2FromBuffer(this.positionBuffer, i) - p2 = getVector2FromBuffer(this.positionBuffer, i+1) + let midpoint = midPointsBtw(p1, p2); + this.drawingCtx.quadraticCurveTo(p1.x, p1.y, midpoint.x, midpoint.y); + p1 = getVector2FromBuffer(this.positionBuffer, i); + p2 = getVector2FromBuffer(this.positionBuffer, i+1); } this.drawingCtx.stroke(); } diff --git a/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js b/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js index 37dfa087db..a6f32dbd5e 100644 --- a/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js +++ b/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js @@ -11,7 +11,7 @@ const mouse = (position, gl) => { } class SimpleTexture extends DrawingTexture { - constructor(){ + constructor() { super(); this.material = null; let canvas = document.createElement('canvas'); @@ -34,7 +34,7 @@ class SimpleTexture extends DrawingTexture { } endDraw() { - super.endDraw() + super.endDraw(); } createMaterial(material) { @@ -61,51 +61,51 @@ class SimpleTexture extends DrawingTexture { this.material.needsUpdate = true; } - draw (mousePosition, object, camera, brush, gl){ - let intersection = super.draw(mousePosition, object, camera, brush) + draw (mousePosition, object, camera, brush, gl) { + let intersection = super.draw(mousePosition, object, camera, brush); // If we don't have a uv coordinates and as a last resort we trying to translate mouse into object coordinate // From object coordinate we can sort of simulate uv coordinate logic for plain object // NOTE() : This won't work for any object except plain object( image object ) if(!intersection) { - if(!this.isChanged) return - let worldPos = fromClipSpaceToWorldSpace(mousePosition, camera, object.position.z) - intersection = {uv: {}} + if(!this.isChanged) return; + let worldPos = fromClipSpaceToWorldSpace(mousePosition, camera, object.position.z); + intersection = {uv: {}}; //#region Clip space to world space method - let scale = object.scale.clone() + let scale = object.scale.clone(); scale.z = 0; - scale.y = -scale.y - let quaternion = object.worldQuaternion() - scale.applyQuaternion(quaternion) - scale.divideScalar(2) - let position = object.worldPosition() - let topPosition = position.clone().sub(scale) - let bottomPosition = position.clone().add(scale) - let top = fromWorldSpaceToClipSpace(topPosition, camera, gl) - let bottom = fromWorldSpaceToClipSpace(bottomPosition, camera, gl) - let topMouse = mouse(top, gl) - let bottomMouse = mouse(bottom, gl) - let worldTop = fromClipSpaceToWorldSpace(topMouse, camera, object.position.z) - let worldBottom = fromClipSpaceToWorldSpace(bottomMouse, camera, object.position.z) + scale.y = -scale.y; + let quaternion = object.worldQuaternion(); + scale.applyQuaternion(quaternion); + scale.divideScalar(2); + let position = object.worldPosition(); + let topPosition = position.clone().sub(scale); + let bottomPosition = position.clone().add(scale); + let top = fromWorldSpaceToClipSpace(topPosition, camera, gl); + let bottom = fromWorldSpaceToClipSpace(bottomPosition, camera, gl); + let topMouse = mouse(top, gl); + let bottomMouse = mouse(bottom, gl); + let worldTop = fromClipSpaceToWorldSpace(topMouse, camera, object.position.z); + let worldBottom = fromClipSpaceToWorldSpace(bottomMouse, camera, object.position.z); if(worldBottom.x > worldTop.x && worldBottom.y < worldTop.y) { - worldBottom.sub(worldTop) - worldPos.sub(worldTop) - worldPos.divide(worldBottom) - intersection.uv.x = worldPos.x - intersection.uv.y = 1 - worldPos.y + worldBottom.sub(worldTop); + worldPos.sub(worldTop); + worldPos.divide(worldBottom); + intersection.uv.x = worldPos.x; + intersection.uv.y = 1 - worldPos.y; } else { - worldTop.sub(worldBottom) - worldPos.sub(worldBottom) - worldPos.divide(worldTop) - intersection.uv.x = 1 - worldPos.x - intersection.uv.y = worldPos.y + worldTop.sub(worldBottom); + worldPos.sub(worldBottom); + worldPos.divide(worldTop); + intersection.uv.x = 1 - worldPos.x; + intersection.uv.y = worldPos.y; } //#endregion } - if(Number.isNaN(intersection.uv.x) || Number.isNaN(intersection.uv.y)) return + if(Number.isNaN(intersection.uv.x) || Number.isNaN(intersection.uv.y)) return; let screenX = (intersection.uv.x) * this.texture.image.width; let screenY = (1 - intersection.uv.y) * this.texture.image.height; - this.drawingBrush.draw({ x: screenX, y: screenY }, brush) + this.drawingBrush.draw({ x: screenX, y: screenY }, brush); this.texture.needsUpdate = true; } diff --git a/src/js/shot-generator/components/Three/helpers/CubeTextureCreator.js b/src/js/shot-generator/components/Three/helpers/CubeTextureCreator.js index 79a253b3c3..fab871734f 100644 --- a/src/js/shot-generator/components/Three/helpers/CubeTextureCreator.js +++ b/src/js/shot-generator/components/Three/helpers/CubeTextureCreator.js @@ -57,9 +57,9 @@ class CubeTextureCreator { for( let i = 0; i < this.imageElements.length; i++ ) { let element = this.imageElements[i]; let texture = this.crop(image, element.x, element.y, element.width, element.height, element.name, boardPath); - cubeTexture.images[i] = texture + cubeTexture.images[i] = texture; } - cubeTexture.needsUpdate = true + cubeTexture.needsUpdate = true; return cubeTexture } @@ -126,9 +126,9 @@ class CubeTextureCreator { croppedCanvas.width = width; croppedCanvas.height = height; croppedCtx.putImageData(imageData, 0, 0); - let texture = new THREE.Texture(croppedCtx.canvas) - return texture.image + let texture = new THREE.Texture(croppedCtx.canvas); + return texture.image; } } -export default CubeTextureCreator \ No newline at end of file +export default CubeTextureCreator; \ No newline at end of file diff --git a/src/js/shot-generator/components/Three/helpers/DrawingTexture.js b/src/js/shot-generator/components/Three/helpers/DrawingTexture.js index 799f4a6b0a..b520e8ab70 100644 --- a/src/js/shot-generator/components/Three/helpers/DrawingTexture.js +++ b/src/js/shot-generator/components/Three/helpers/DrawingTexture.js @@ -52,15 +52,15 @@ class DrawingTexture { for( let i = 0; i < this.drawingCanvases.length; i++){ images.push(this.drawingCanvases[i].toDataURL(mime)); } - return images + return images; } createMaterial(texture) { - this.texture = texture + this.texture = texture; } setTexture(texture) { - this.texture = texture + this.texture = texture; } intersectImage (x, y, object, camera) { @@ -76,7 +76,7 @@ class DrawingTexture { return; } this.isChanged = true; - return intersection + return intersection; } } diff --git a/src/js/shot-generator/components/Three/helpers/cubeMapDrawingTexture.js b/src/js/shot-generator/components/Three/helpers/cubeMapDrawingTexture.js index e77d19bf6e..b332e6e018 100644 --- a/src/js/shot-generator/components/Three/helpers/cubeMapDrawingTexture.js +++ b/src/js/shot-generator/components/Three/helpers/cubeMapDrawingTexture.js @@ -2,7 +2,7 @@ import * as THREE from 'three' import DrawingTexture from './DrawingTexture' class CubeMapDrawingTexture extends DrawingTexture { - constructor(){ + constructor() { super(); this.prevFaceIndex; } @@ -39,7 +39,7 @@ class CubeMapDrawingTexture extends DrawingTexture { this.texture.needsUpdate = true; } - draw (mousePosition, object, camera, brush){ + draw (mousePosition, object, camera, brush) { let intersection = super.draw(mousePosition, object, camera, brush) if(!intersection) return; From bc8ece6653d148c98c19ce6a547755c4af0a8c53 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Wed, 15 Jul 2020 11:27:52 +0300 Subject: [PATCH 62/88] Injected mouseToClip and saveToData url from SceneManager --- src/js/shot-generator/SceneManagerR3fLarge.js | 34 +++---------------- .../components/Three/Helpers/SimpleTexture.js | 12 ++----- .../Three/helpers/CubeTextureCreator.js | 2 +- .../helpers/saveDataURLtoFile.js | 20 ++++++++++- .../shot-generator/utils/mouseToClipSpace.js | 10 ++++++ 5 files changed, 37 insertions(+), 41 deletions(-) create mode 100644 src/js/shot-generator/utils/mouseToClipSpace.js diff --git a/src/js/shot-generator/SceneManagerR3fLarge.js b/src/js/shot-generator/SceneManagerR3fLarge.js index 2c06662ca6..fee7ecb2b3 100644 --- a/src/js/shot-generator/SceneManagerR3fLarge.js +++ b/src/js/shot-generator/SceneManagerR3fLarge.js @@ -45,34 +45,8 @@ import ObjectRotationControl from '../shared/IK/objects/ObjectRotationControl' import RemoteProvider from "./components/RemoteProvider" import RemoteClients from "./components/RemoteClients" import XRClient from "./components/Three/XRClient" -import path from 'path' -import fs from 'fs-extra' - -const mouse = (event, gl) => { - const rect = gl.domElement.getBoundingClientRect(); - let worldX = ( ( event.clientX - rect.left ) / rect.width ) * 2 - 1; - let worldY = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1; - return { x: worldX, y: worldY } -} - -let saveDataURLtoFile = (dataURL, boardPath, updateObject, object) => { - let imageData = dataURL.replace(/^data:image\/\w+;base64,/, '') - if(object.userData.tempImagePath) { - let tempImageFilePath = path.join(path.dirname(boardPath), 'models/images', object.userData.tempImagePath) - fs.remove(tempImageFilePath) - } - let tempFilename = `temp_${object.userData.id}-${Date.now()}-texture.png` - object.userData.tempImagePath = tempFilename - let imageFilePath = path.join(path.dirname(boardPath), 'models/images', tempFilename) - fs.writeFileSync(imageFilePath, imageData, 'base64') - let projectDir = path.dirname(boardPath) - let assetsDir = path.join(projectDir, 'models', 'images') - fs.ensureDirSync(assetsDir) - let dst = path.join(assetsDir, path.basename(imageFilePath)) - let id = path.relative(projectDir, dst) - updateObject(object.userData.id, {imageAttachmentIds: [id]}) - -} +import mouse from './utils/mouseToClipSpace' +import { saveDataURLtoTempFile } from './helpers/saveDataURLtoFile' const sceneObjectSelector = (state) => { const sceneObjects = getSceneObjects(state) @@ -206,7 +180,7 @@ const SceneManagerR3fLarge = connect( let raycaster = useRef(new THREE.Raycaster()) const draw = (event) => { let keys = Object.keys(drawingTextures.current) - let {x, y} = mouse(event, activeGL) + let {x, y} = mouse({x: event.clientX, y: event.clientY}, activeGL) raycaster.current.setFromCamera({x, y}, camera) let imageObjects = getImageObjects() let intersections = raycaster.current.intersectObjects(imageObjects, true) @@ -259,7 +233,7 @@ const SceneManagerR3fLarge = connect( let object = scene.__interaction.find((obj) => obj.userData.id === key) if(drawingTextures.current[key].isChanged) { drawingTextures.current[key].isChanged = false - saveDataURLtoFile(drawingTextures.current[key].getImage("image/png"), storyboarderFilePath, updateObject, object) + saveDataURLtoTempFile(drawingTextures.current[key].getImage("image/png"), storyboarderFilePath, updateObject, object) } } if( drawingSceneTexture.current.save && drawingSceneTexture.current.texture.isChanged) { diff --git a/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js b/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js index a6f32dbd5e..757762b1bd 100644 --- a/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js +++ b/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js @@ -2,13 +2,7 @@ import * as THREE from 'three' import DrawingTexture from './DrawingTexture' import fromWorldSpaceToClipSpace from "../../../utils/WorldSpaceToClipSpace" import fromClipSpaceToWorldSpace from "../../../utils/ClipSpaceToWorldSpace" - -const mouse = (position, gl) => { - const rect = gl.domElement.getBoundingClientRect(); - let worldX = ( ( position.x ) / rect.width ) * 2 - 1; - let worldY = - ( ( position.y ) / rect.height ) * 2 + 1; - return { x: worldX, y: worldY } - } +import mouse from '../../../utils/mouseToClipSpace' class SimpleTexture extends DrawingTexture { constructor() { @@ -83,8 +77,8 @@ class SimpleTexture extends DrawingTexture { let bottomPosition = position.clone().add(scale); let top = fromWorldSpaceToClipSpace(topPosition, camera, gl); let bottom = fromWorldSpaceToClipSpace(bottomPosition, camera, gl); - let topMouse = mouse(top, gl); - let bottomMouse = mouse(bottom, gl); + let topMouse = mouse(top, gl, false); + let bottomMouse = mouse(bottom, gl, false); let worldTop = fromClipSpaceToWorldSpace(topMouse, camera, object.position.z); let worldBottom = fromClipSpaceToWorldSpace(bottomMouse, camera, object.position.z); if(worldBottom.x > worldTop.x && worldBottom.y < worldTop.y) { diff --git a/src/js/shot-generator/components/Three/helpers/CubeTextureCreator.js b/src/js/shot-generator/components/Three/helpers/CubeTextureCreator.js index fab871734f..4f2a15b4fc 100644 --- a/src/js/shot-generator/components/Three/helpers/CubeTextureCreator.js +++ b/src/js/shot-generator/components/Three/helpers/CubeTextureCreator.js @@ -1,4 +1,4 @@ -import saveDataURLtoFile from '../../../helpers/saveDataURLtoFile' +import { saveDataURLtoFile } from '../../../helpers/saveDataURLtoFile' import path from 'path' import * as THREE from 'three' diff --git a/src/js/shot-generator/helpers/saveDataURLtoFile.js b/src/js/shot-generator/helpers/saveDataURLtoFile.js index 58dfa6718d..c22bf70a7a 100644 --- a/src/js/shot-generator/helpers/saveDataURLtoFile.js +++ b/src/js/shot-generator/helpers/saveDataURLtoFile.js @@ -10,5 +10,23 @@ const saveDataURLtoFile = (dataURL, filename, type, boardPath, async = false) => fs.writeFile(imageFilePath, imageData, 'base64') } +} +const saveDataURLtoTempFile = (dataURL, boardPath, updateObject, object) => { + let imageData = dataURL.replace(/^data:image\/\w+;base64,/, '') + if(object.userData.tempImagePath) { + let tempImageFilePath = path.join(path.dirname(boardPath), 'models/images', object.userData.tempImagePath) + fs.remove(tempImageFilePath) } -export default saveDataURLtoFile; \ No newline at end of file + let tempFilename = `temp_${object.userData.id}-${Date.now()}-texture.png` + object.userData.tempImagePath = tempFilename + let imageFilePath = path.join(path.dirname(boardPath), 'models/images', tempFilename) + fs.writeFileSync(imageFilePath, imageData, 'base64') + let projectDir = path.dirname(boardPath) + let assetsDir = path.join(projectDir, 'models', 'images') + fs.ensureDirSync(assetsDir) + let dst = path.join(assetsDir, path.basename(imageFilePath)) + let id = path.relative(projectDir, dst) + updateObject(object.userData.id, {imageAttachmentIds: [id]}) + +} +export { saveDataURLtoFile, saveDataURLtoTempFile } \ No newline at end of file diff --git a/src/js/shot-generator/utils/mouseToClipSpace.js b/src/js/shot-generator/utils/mouseToClipSpace.js new file mode 100644 index 0000000000..c7a021c5da --- /dev/null +++ b/src/js/shot-generator/utils/mouseToClipSpace.js @@ -0,0 +1,10 @@ +const mouse = (mousePos, gl, offsetTop = true) => { + const rect = gl.domElement.getBoundingClientRect(); + let offsetX = offsetTop ? rect.left : 0 + let offsetY = offsetTop ? rect.top : 0 + let worldX = ( ( mousePos.x - offsetX ) / rect.width ) * 2 - 1; + let worldY = - ( ( mousePos.y - offsetY ) / rect.height ) * 2 + 1; + return { x: worldX, y: worldY } + } + +export default mouse \ No newline at end of file From 3f69a996635e831df227b619d2f43cebdb7721f2 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Wed, 15 Jul 2020 12:08:51 +0300 Subject: [PATCH 63/88] Created hook use-draw-on-image for image drawing logic --- src/js/shot-generator/SceneManagerR3fLarge.js | 101 ++---------------- .../shot-generator/hooks/use-draw-on-image.js | 97 +++++++++++++++++ 2 files changed, 106 insertions(+), 92 deletions(-) create mode 100644 src/js/shot-generator/hooks/use-draw-on-image.js diff --git a/src/js/shot-generator/SceneManagerR3fLarge.js b/src/js/shot-generator/SceneManagerR3fLarge.js index fee7ecb2b3..b8d7d2dec0 100644 --- a/src/js/shot-generator/SceneManagerR3fLarge.js +++ b/src/js/shot-generator/SceneManagerR3fLarge.js @@ -42,11 +42,10 @@ import Group from './components/Three/Group' import CameraUpdate from './CameraUpdate' import deepEqualSelector from '../utils/deepEqualSelector' import ObjectRotationControl from '../shared/IK/objects/ObjectRotationControl' -import RemoteProvider from "./components/RemoteProvider" -import RemoteClients from "./components/RemoteClients" -import XRClient from "./components/Three/XRClient" -import mouse from './utils/mouseToClipSpace' -import { saveDataURLtoTempFile } from './helpers/saveDataURLtoFile' +import RemoteProvider from './components/RemoteProvider' +import RemoteClients from './components/RemoteClients' +import XRClient from './components/Three/XRClient' +import useDrawOnImage from './hooks/use-draw-on-image' const sceneObjectSelector = (state) => { const sceneObjects = getSceneObjects(state) @@ -120,9 +119,6 @@ const SceneManagerR3fLarge = connect( const ambientLightRef = useRef() const directionalLightRef = useRef() const selectedCharacters = useRef() - const isDrawStarted = useRef(false) - const drawingTextures = useRef({}) - const drawingSceneTexture = useRef({}) const objectRotationControl = useRef() const sceneObjectLength = Object.values(sceneObjects).length @@ -155,93 +151,14 @@ const SceneManagerR3fLarge = connect( const groupIds = useMemo(() => { return Object.values(sceneObjects).filter(o => o.type === 'group').map(o => o.id) }, [sceneObjectLength]) - + useEffect(() => { if(isDrawingMode) { - objectRotationControl.current.deselectObject(); - gl.domElement.addEventListener( 'mousedown', onKeyDown ) - window.addEventListener( 'mouseup', onKeyUp ) - } - return () => { - gl.domElement.removeEventListener( 'mousedown', onKeyDown ) - window.removeEventListener( 'mouseup', onKeyUp ) + objectRotationControl.deselectObject(); } - }, [isDrawingMode, drawingBrush]) - useEffect(() => { - if(!cleanImages || !cleanImages.length) return - for(let i = 0; i < cleanImages.length; i++) { - drawingTextures.current[cleanImages[i]].cleanImage() - } - }, [cleanImages]) - - let getImageObjects = () => scene.__interaction.filter(object => object.userData.type === "image") - let raycaster = useRef(new THREE.Raycaster()) - const draw = (event) => { - let keys = Object.keys(drawingTextures.current) - let {x, y} = mouse({x: event.clientX, y: event.clientY}, activeGL) - raycaster.current.setFromCamera({x, y}, camera) - let imageObjects = getImageObjects() - let intersections = raycaster.current.intersectObjects(imageObjects, true) - if(!intersections.length && drawingSceneTexture.current.draw) { - let texture = drawingSceneTexture.current - texture.draw({x, y}, camera, drawingBrush) - } - for(let i = 0; i < keys.length; i++) { - let key = keys[i] - let drawingTexture = drawingTextures.current[key]; - let object = drawingTexture.material.parent.parent; - if(!object || !object.visible) continue - drawingTexture.draw({x, y}, object, camera, drawingBrush, activeGL) - } - - } - - const onKeyDown = (event) => { - isDrawStarted.current = true; - let keys = Object.keys(drawingTextures.current) - for(let i = 0; i < keys.length; i++) { - let key = keys[i] - drawingTextures.current[key].prepareToDraw(); - } - if(drawingSceneTexture.current && drawingSceneTexture.current.texture) { - drawingSceneTexture.current.texture.prepareToDraw() - } - draw(event) - gl.domElement.addEventListener('mousemove', draw) - } - - useEffect(() => { - let keys = Object.keys(drawingTextures.current) - for(let i = 0; i < keys.length; i++) { - let key = keys[i] - drawingTextures.current[key].setMesh(drawingBrush.type) - } - if(drawingSceneTexture.current && drawingSceneTexture.current.texture) - drawingSceneTexture.current.texture.setMesh(drawingBrush.type) - }, [drawingBrush.type]) - - const onKeyUp = (event) => { - if(!isDrawStarted.current) return - gl.domElement.removeEventListener('mousemove', draw) - isDrawStarted.current = false; - let keys = Object.keys(drawingTextures.current) - for(let i = 0; i < keys.length; i++) { - let key = keys[i] - drawingTextures.current[key].endDraw(); - let object = scene.__interaction.find((obj) => obj.userData.id === key) - if(drawingTextures.current[key].isChanged) { - drawingTextures.current[key].isChanged = false - saveDataURLtoTempFile(drawingTextures.current[key].getImage("image/png"), storyboarderFilePath, updateObject, object) - } - } - if( drawingSceneTexture.current.save && drawingSceneTexture.current.texture.isChanged) { - drawingSceneTexture.current.texture.isChanged = false - drawingSceneTexture.current.texture.endDraw() - drawingSceneTexture.current.save() - } - } + const {drawingTextures, drawingSceneTexture} = useDrawOnImage(isDrawingMode, drawingBrush, cleanImages, storyboarderFilePath, updateObject) useEffect(() => { let sgIkHelper = SGIkHelper.getInstance() @@ -508,7 +425,7 @@ const SceneManagerR3fLarge = connect( isSelected={ selections.includes(id) } updateObject={ updateObject } objectRotationControl={ objectRotationControl.current } - drawTextures={ drawingTextures.current } + drawTextures={ drawingTextures } /> }) @@ -549,7 +466,7 @@ const SceneManagerR3fLarge = connect( world={world} storyboarderFilePath={ storyboarderFilePath } updateWorld={ updateWorld } - drawingSceneTexture={ drawingSceneTexture.current }/> + drawingSceneTexture={ drawingSceneTexture }/> } { roomTexture && { + const {gl, scene, camera} = useThree() + const isDrawStarted = useRef(false) + const raycaster = useRef(new THREE.Raycaster()) + const drawingTextures = useRef({}) + const drawingSceneTexture = useRef({}) + useEffect(() => { + if(isDrawingMode) { + gl.domElement.addEventListener( 'mousedown', onKeyDown ) + window.addEventListener( 'mouseup', onKeyUp ) + } + return () => { + gl.domElement.removeEventListener( 'mousedown', onKeyDown ) + window.removeEventListener( 'mouseup', onKeyUp ) + } + }, [isDrawingMode, drawingBrush]) + + useEffect(() => { + if(!cleanImages || !cleanImages.length) return + for(let i = 0; i < cleanImages.length; i++) { + drawingTexture.currents[cleanImages[i]].cleanImage() + } + }, [cleanImages]) + + useEffect(() => { + let keys = Object.keys(drawingTextures.current) + for(let i = 0; i < keys.length; i++) { + let key = keys[i] + drawingTextures.current[key].setMesh(drawingBrush.type) + } + if(drawingSceneTexture.current && drawingSceneTexture.current.texture) + drawingSceneTexture.current.texture.setMesh(drawingBrush.type) + }, [drawingBrush.type]) + + let getImageObjects = () => scene.__interaction.filter(object => object.userData.type === "image") + + const onKeyDown = (event) => { + isDrawStarted.current = true; + let keys = Object.keys(drawingTextures.current) + for(let i = 0; i < keys.length; i++) { + let key = keys[i] + drawingTextures.current[key].prepareToDraw(); + } + if(drawingSceneTexture.current && drawingSceneTexture.current.texture) { + drawingSceneTexture.current.texture.prepareToDraw() + } + draw(event) + gl.domElement.addEventListener('mousemove', draw) + } + + const draw = (event) => { + let keys = Object.keys(drawingTextures.current) + let {x, y} = mouse({x: event.clientX, y: event.clientY}, gl) + raycaster.current.setFromCamera({x, y}, camera) + let imageObjects = getImageObjects() + let intersections = raycaster.current.intersectObjects(imageObjects, true) + if(!intersections.length && drawingSceneTexture.current.draw) { + let texture = drawingSceneTexture.current + texture.draw({x, y}, camera, drawingBrush) + } + for(let i = 0; i < keys.length; i++) { + let key = keys[i] + let drawingTexture = drawingTextures.current[key]; + let object = drawingTexture.material.parent.parent; + if(!object || !object.visible) continue + drawingTexture.draw({x, y}, object, camera, drawingBrush, gl) + } + } + + const onKeyUp = (event) => { + if(!isDrawStarted.current) return + gl.domElement.removeEventListener('mousemove', draw) + isDrawStarted.current = false; + let keys = Object.keys(drawingTextures.current) + for(let i = 0; i < keys.length; i++) { + let key = keys[i] + drawingTextures.current[key].endDraw(); + let object = scene.__interaction.find((obj) => obj.userData.id === key) + if(drawingTextures.current[key].isChanged) { + drawingTextures.current[key].isChanged = false + saveDataURLtoTempFile(drawingTextures.current[key].getImage("image/png"), storyboarderFilePath, updateObject, object) + } + } + if( drawingSceneTexture.current.save && drawingSceneTexture.current.texture.isChanged) { + drawingSceneTexture.current.texture.isChanged = false + drawingSceneTexture.current.texture.endDraw() + drawingSceneTexture.current.save() + } + } + return {drawingTextures: drawingTextures.current, drawingSceneTexture: drawingSceneTexture.current} +} + +export default useDrawOnImage \ No newline at end of file From 5df30b350afdbaad8fc715eddd79bbc85f6c62b4 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Wed, 15 Jul 2020 12:12:45 +0300 Subject: [PATCH 64/88] Removed cleaning up of textureLoader and fixed deselectObject of undefined --- src/js/shot-generator/SceneManagerR3fLarge.js | 4 ++-- src/js/shot-generator/hooks/use-assets-manager.js | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/js/shot-generator/SceneManagerR3fLarge.js b/src/js/shot-generator/SceneManagerR3fLarge.js index b8d7d2dec0..c330c6b205 100644 --- a/src/js/shot-generator/SceneManagerR3fLarge.js +++ b/src/js/shot-generator/SceneManagerR3fLarge.js @@ -151,10 +151,10 @@ const SceneManagerR3fLarge = connect( const groupIds = useMemo(() => { return Object.values(sceneObjects).filter(o => o.type === 'group').map(o => o.id) }, [sceneObjectLength]) - + useEffect(() => { if(isDrawingMode) { - objectRotationControl.deselectObject(); + objectRotationControl.current.deselectObject(); } }, [isDrawingMode, drawingBrush]) diff --git a/src/js/shot-generator/hooks/use-assets-manager.js b/src/js/shot-generator/hooks/use-assets-manager.js index 322bb4b069..d28df46de0 100644 --- a/src/js/shot-generator/hooks/use-assets-manager.js +++ b/src/js/shot-generator/hooks/use-assets-manager.js @@ -9,7 +9,7 @@ import { gltfLoader } from '../utils/gltfLoader' */ export const cache = observable({}) -let textureLoader = new THREE.TextureLoader() +const textureLoader = new THREE.TextureLoader() const LOADING_MODE = { PENDING: 'PENDING', @@ -98,7 +98,6 @@ export const loadAsset = (path) => { export const cleanUpCache = () => { cache.set({}) - textureLoader = new THREE.TextureLoader() } export const removeAsset = (imagePath) => { From 4f78dfdf20e8a1efec43204ad187eaaa17666a98 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Thu, 16 Jul 2020 11:00:04 +0300 Subject: [PATCH 65/88] Created drawMode reducer --- src/js/shared/reducers/shot-generator.js | 52 ++++++++++++------- src/js/shot-generator/SceneManagerR3fLarge.js | 16 +++--- .../InspectedElement/BrushInspector/index.js | 44 +++++++--------- .../shot-generator/hooks/use-draw-on-image.js | 26 +++++----- src/js/windows/shot-generator/window.js | 5 +- 5 files changed, 73 insertions(+), 70 deletions(-) diff --git a/src/js/shared/reducers/shot-generator.js b/src/js/shared/reducers/shot-generator.js index cc83901a20..ad09a6299f 100644 --- a/src/js/shared/reducers/shot-generator.js +++ b/src/js/shared/reducers/shot-generator.js @@ -36,6 +36,7 @@ const getSelectedAttachable = state => state.undoable.present.selectedAttachable const getWorld = state => state.undoable.present.world +const getDrawMode = state => state.undoable.present.drawMode const getHash = state => hashify(JSON.stringify(getSerializedState(state))) @@ -48,7 +49,7 @@ const getSerializedState = state => { world: getWorld(state), sceneObjects: R.map(serializeSceneObject, getSceneObjects(state)), activeCamera: getActiveCamera(state), - drawingBrush: state.drawingBrush + drawingBrush: getDrawMode(state).brush } } @@ -663,11 +664,12 @@ const initialState = { board: {}, - drawingBrush: { color: '#000000', size: 2}, - isDrawingMode: false, - undoable: { world: initialScene.world, + drawMode: { + brush: { color: '#000000', size: 2 }, + isEnabled: false, + }, activeCamera: initialScene.activeCamera, sceneObjects: withDisplayNames(initialScene.sceneObjects), selections: [], @@ -1497,19 +1499,6 @@ const mainReducer = (state/* = initialState*/, action) => { delete draft.attachments[action.payload.id] return - case 'UPDATE_DRAWING_MESH': - if(!action.payload) return - draft.drawingBrush.color = action.payload.color ? action.payload.color : draft.drawingBrush.color - draft.drawingBrush.size = action.payload.size ? action.payload.size : draft.drawingBrush.size - draft.drawingBrush.type = action.payload.type ? action.payload.type : draft.drawingBrush.type - return - case 'ENABLE_DRAWING_MODE': - draft.isDrawingMode = action.payload - return - case 'SET_CLEAN_IMAGE': - draft.cleanImages = action.payload - return - case 'UNDO_GROUP_START': batchGroupBy.start(action.payload) return @@ -1524,6 +1513,29 @@ const mainReducer = (state/* = initialState*/, action) => { }) } +const drawModeReducer = (state = initialState.undoable.drawMode, action) => { + return produce(state, draft => { + switch(action.type) { + case 'UPDATE_DRAW_MODE': + if(action.payload.hasOwnProperty('isEnabled')) { + draft.isEnabled = action.payload.isEnabled + } + if(action.payload.hasOwnProperty('cleanImages')) { + draft.cleanImages = action.payload.cleanImages + } + if(action.payload.hasOwnProperty('brush')) { + draft.brush.color = action.payload.color ? action.payload.color : draft.brush.color + draft.brush.size = action.payload.size ? action.payload.size : draft.brush.size + draft.brush.type = action.payload.type ? action.payload.type : draft.brush.type + } + return + + default: + return + } + }) +} + const checksReducer = (state, action) => { return produce(state, draft => { switch (action.type) { @@ -1604,6 +1616,7 @@ const undoableReducers = combineReducers({ sceneObjects: sceneObjectsReducer, activeCamera: activeCameraReducer, world: worldReducer, + drawMode: drawModeReducer, selections: selectionsReducer, selectedBone: selectedBoneReducer, selectedAttachable: attachableSelectionsReducer @@ -1745,9 +1758,7 @@ module.exports = { deleteScenePreset: id => ({ type: 'DELETE_SCENE_PRESET', payload: { id } }), createCharacterPreset: payload => ({ type: 'CREATE_CHARACTER_PRESET', payload }), - updateDrawingBrush: payload => ({ type: 'UPDATE_DRAWING_MESH', payload}), - enableDrawMode: payload => ({ type: 'ENABLE_DRAWING_MODE', payload}), - setCleanImage: payload => ({ type: 'SET_CLEAN_IMAGE', payload}), + updateDrawMode: payload => ({ type: 'UPDATE_DRAW_MODE', payload: payload}), createPosePreset: payload => ({ type: 'CREATE_POSE_PRESET', payload }), createHandPosePreset: payload => ({ type: 'CREATE_HAND_POSE_PRESET', payload }), updatePosePreset: (id, values) => ({ type: 'UPDATE_POSE_PRESET', payload: { id, ...values} }), @@ -1778,6 +1789,7 @@ module.exports = { // selectors // getSceneObjects, + getDrawMode, getSelections, getActiveCamera, getSelectedBone, diff --git a/src/js/shot-generator/SceneManagerR3fLarge.js b/src/js/shot-generator/SceneManagerR3fLarge.js index ae01050330..cad6334a72 100644 --- a/src/js/shot-generator/SceneManagerR3fLarge.js +++ b/src/js/shot-generator/SceneManagerR3fLarge.js @@ -21,7 +21,8 @@ import { updateObjects, updateCharacterPoleTargets, deleteObjects, - updateWorld + updateWorld, + getDrawMode } from '../shared/reducers/shot-generator' import { useThree, useFrame } from 'react-three-fiber' @@ -78,6 +79,7 @@ const SceneManagerR3fLarge = connect( isDrawingMode: state.isDrawingMode, cleanImages: state.cleanImages, aspectRatio: state.aspectRatio, + drawMode: getDrawMode(state) }), { selectObject, @@ -104,9 +106,7 @@ const SceneManagerR3fLarge = connect( updateObjects, selectedBone, - drawingBrush, - isDrawingMode, - cleanImages, + drawMode, cameraShots, setLargeCanvasData, @@ -160,12 +160,12 @@ const SceneManagerR3fLarge = connect( }, [sceneObjectLength]) useEffect(() => { - if(isDrawingMode) { + if(drawMode.isEnabled) { objectRotationControl.current.deselectObject(); } - }, [isDrawingMode, drawingBrush]) + }, [drawMode.isEnabled, drawMode.brush]) - const {drawingTextures, drawingSceneTexture} = useDrawOnImage(isDrawingMode, drawingBrush, cleanImages, storyboarderFilePath, updateObject) + const {drawingTextures, drawingSceneTexture} = useDrawOnImage(drawMode, storyboarderFilePath, updateObject) useEffect(() => { let sgIkHelper = SGIkHelper.getInstance() @@ -345,7 +345,7 @@ const SceneManagerR3fLarge = connect( return - {!isDrawingMode && } + {!drawMode.isEnabled && } ({ - drawingBrush: state.drawingBrush, + drawMode: getDrawMode(state), selections: getSelections(state) }), { - updateDrawingBrush, - enableDrawMode, - setCleanImage + updateDrawMode } )( React.memo(({ - updateDrawingBrush, - enableDrawMode, - setCleanImage, - drawingBrush, + updateDrawMode, + drawMode, selections }) => { useEffect(() => { - enableDrawMode(true) + updateDrawMode({isEnabled: true}) return () => { - enableDrawMode(false) + updateDrawMode({isEnabled: false}) } }, []) const setSize = (value) => { - updateDrawingBrush({ size: value }) + updateDrawMode({ brush: { size: value }}) } const setColor = (value) => { - updateDrawingBrush({ color: value }) + updateDrawMode({ brush: { color: value }}) } const setType = (event) => { - updateDrawingBrush({ type: event.target.value }) + updateDrawMode({ brush: { type: event.target.value }}) } const cleanImage = () => { - setCleanImage([...selections]) + updateDrawMode({ cleanImages: [...selections]}) } return ( @@ -60,7 +52,7 @@ React.memo(({
Type
Date: Fri, 17 Jul 2020 15:46:13 +0300 Subject: [PATCH 69/88] Cleaned up code --- src/js/shot-generator/SceneManagerR3fLarge.js | 3 - .../components/Three/SaveShot.js | 196 ------------------ .../components/Three/SceneBackground.js | 5 +- .../Three/helpers/CubeTextureCreator.js | 8 +- src/js/xr/src/SceneManagerXR.js | 2 +- src/js/xr/src/helpers/CubeTextureCreator.js | 101 --------- 6 files changed, 7 insertions(+), 308 deletions(-) delete mode 100644 src/js/shot-generator/components/Three/SaveShot.js delete mode 100644 src/js/xr/src/helpers/CubeTextureCreator.js diff --git a/src/js/shot-generator/SceneManagerR3fLarge.js b/src/js/shot-generator/SceneManagerR3fLarge.js index cad6334a72..ba6eb8a3db 100644 --- a/src/js/shot-generator/SceneManagerR3fLarge.js +++ b/src/js/shot-generator/SceneManagerR3fLarge.js @@ -75,9 +75,6 @@ const SceneManagerR3fLarge = connect( selectedBone: getSelectedBone(state), cameraShots: state.cameraShots, selectedAttachable: getSelectedAttachable(state), - drawingBrush: state.drawingBrush, - isDrawingMode: state.isDrawingMode, - cleanImages: state.cleanImages, aspectRatio: state.aspectRatio, drawMode: getDrawMode(state) }), diff --git a/src/js/shot-generator/components/Three/SaveShot.js b/src/js/shot-generator/components/Three/SaveShot.js deleted file mode 100644 index e6cd4a4efc..0000000000 --- a/src/js/shot-generator/components/Three/SaveShot.js +++ /dev/null @@ -1,196 +0,0 @@ -import React, { useRef, useEffect, useCallback } from 'react' -import { connect } from 'react-redux' -import { - getSelections, - getSerializedState, - updateObject, - markSaved, - selectObject, - getSceneObjects, - updateWorld - } from '../../../shared/reducers/shot-generator' - import { ipcRenderer } from 'electron' -import { useThree } from 'react-three-fiber' -import { SHOT_LAYERS } from '../../utils/ShotLayers' -import { OutlineEffect } from '../../../vendor/OutlineEffect' -import { remote } from 'electron' -import path from 'path' -import fs from 'fs-extra' -import { cleanUpCache, removeAsset } from '../../hooks/use-assets-manager' - -const { dialog } = remote -const withState = (fn) => (dispatch, getState) => fn(dispatch, getState()) - -const SaveShot = connect( - state => ({ - storyboarderFilePath : state.meta.storyboarderFilePath, - aspectRatio: state.aspectRatio - }), - { - getSelections, - getSerializedState, - withState, - markSaved, - selectObject, - updateObject, - updateWorld, - saveScene: filepath => (dispatch, getState) => { - let state = getState() - let contents = getSerializedState(state) - fs.writeFileSync(filepath, JSON.stringify(contents, null, 2)) - dialog.showMessageBox(null, { message: 'Saved!' }) - }, - }) -( React.memo(({ - aspectRatio, - withState, - storyboarderFilePath, - markSaved, - isPlot = false, - selectObject, - updateObject, - updateWorld -}) => { - const { scene, camera } = useThree() - const imageRenderer = useRef() - const outlineEffect = useRef() - - useEffect(() => { - if (!imageRenderer.current) { - imageRenderer.current = new THREE.WebGLRenderer({ antialias: true }), { defaultThickness:0.008 } - } - outlineEffect.current = new OutlineEffect(imageRenderer.current, { defaultThickness: 0.015 }) - return () => { - imageRenderer.current = null - outlineEffect.current = null - } - }, []) - - const saveShot = () => { - - selectObject(null) - if(!isPlot) { - let cameraImage = renderImagesForBoard() - saveImages() - withState((dispatch, state) => { - ipcRenderer.send('saveShot', { - uid: state.board.uid, - data: getSerializedState(state), - images: { - 'camera': cameraImage, - } - }) - }) - } else { - let plotImage = renderImagesForBoard() - withState((dispatch, state) => { - ipcRenderer.send('saveShotPlot', { - plotImage: plotImage, - currentBoard: state.board.uid - }) - }) - } - } - - const insertShot = () => { - - selectObject(null) - if(!isPlot) { - let cameraImage = renderImagesForBoard() - // NOTE we do this first, since we get new data on insertShot complete - markSaved() - saveImages() - withState((dispatch, state) => { - ipcRenderer.send('insertShot', { - data: getSerializedState(state), - images: { - camera: cameraImage - }, - currentBoard: state.board - }) - }) - } else { - let plotImage = renderImagesForBoard() - setTimeout(() => { - withState((dispatch, state) => { - ipcRenderer.send('saveShotPlot', { - plotImage: plotImage, - currentBoard: state.board.uid - }) - }) - }, 100) - - } - } - - const saveImages = () => { - if(isPlot) return - let imageObjects - withState((dispatch, state) => { - imageObjects = Object.values(getSceneObjects(state)).filter(obj => obj.type === "image") - }) - for( let i = 0; i < imageObjects.length; i++ ) { - let image = imageObjects[i] - let imgComponent = scene.__interaction.find(obj => obj.userData.id === image.id) - let isImageExist = imgComponent.userData.tempImagePath - if(!isImageExist) continue - let tempImageFilePath = path.join(path.dirname(storyboarderFilePath), 'models/images', imgComponent.userData.tempImagePath) - let imageFilePath = path.join(path.dirname(storyboarderFilePath), 'models/images', `${image.id}-texture.png`) - let projectDir = path.dirname(storyboarderFilePath) - let assetsDir = path.join(projectDir, 'models', 'images') - fs.ensureDirSync(assetsDir) - let dst = path.join(assetsDir, path.basename(imageFilePath)) - let id = path.relative(projectDir, dst) - fs.copySync(tempImageFilePath, imageFilePath, {overwrite:true}) - fs.remove(tempImageFilePath) - removeAsset(imageFilePath) - imgComponent.userData.tempImagePath = null - updateObject(image.id, {imageAttachmentIds: [id]}) - } - if(scene.userData.tempPath) { - let tempImageFilePath = path.join(path.dirname(storyboarderFilePath), 'models/sceneTextures/', scene.userData.tempPath) - let imageFilePath = path.join(path.dirname(storyboarderFilePath), scene.userData.texturePath) - removeAsset(imageFilePath) - fs.copySync(tempImageFilePath, imageFilePath, {overwrite:true}) - fs.remove(tempImageFilePath) - updateWorld({sceneTexture: scene.userData.texturePath}) - scene.userData.tempPath = null - } - } - - // add handlers once, and use refs for callbacks - useEffect(() => { - ipcRenderer.on('requestSaveShot', saveShot) - return () => ipcRenderer.removeListener('requestSaveShot', saveShot) - }, [saveShot]) - - useEffect(() => { - ipcRenderer.on('requestInsertShot', insertShot) - return () => ipcRenderer.removeListener('requestInsertShot', insertShot) - }, [insertShot]) - - const renderImagesForBoard = () => { - let width = isPlot ? 900 : Math.ceil(900 * aspectRatio) - let imageRenderCamera = camera.clone() - imageRenderCamera.layers.set(SHOT_LAYERS) - // render the image - let savedBackground - if(isPlot) { - savedBackground = scene.background && scene.background.clone() - scene.background = new THREE.Color( "#FFFFFF" ) - } - outlineEffect.current.setSize(width, 900) - outlineEffect.current.render(scene, imageRenderCamera) - let cameraImage = outlineEffect.current.domElement.toDataURL() - if(isPlot) { - scene.background = savedBackground - } - - return cameraImage - } - - return null - }) -) - -export default SaveShot diff --git a/src/js/shot-generator/components/Three/SceneBackground.js b/src/js/shot-generator/components/Three/SceneBackground.js index e460a8cd43..0aeda73a24 100644 --- a/src/js/shot-generator/components/Three/SceneBackground.js +++ b/src/js/shot-generator/components/Three/SceneBackground.js @@ -92,7 +92,10 @@ const SceneBackground = React.memo(({ imagePath, world, storyboarderFilePath, up cleanUpTempFile() let tempFileName = `temp_scenetexture-${Date.now()}.jpg` if(world.textureType === SceneTextureType.CubeMap) { - cubeTextureCreator.current.saveCubeMapTexture(imagePath[0], scene.background, tempFileName) + let dataUrl = cubeTextureCreator.current.combineImages(scene.background) + let {dir, ext, name} = path.parse(imagePath[0]); + let properName = tempFileName ? tempFileName : name + ext; + saveDataURLtoFile(dataUrl, `${properName}`, 'models/sceneTextures', storyboarderFilePath); } else if(world.textureType === SceneTextureType.Image) { let imageData = drawingSceneTexture.texture.getImage("image/jpg") let imageFilePath = path.join(path.dirname(storyboarderFilePath), 'models/sceneTextures', tempFileName) diff --git a/src/js/shot-generator/components/Three/helpers/CubeTextureCreator.js b/src/js/shot-generator/components/Three/helpers/CubeTextureCreator.js index 4f2a15b4fc..9d53314ec1 100644 --- a/src/js/shot-generator/components/Three/helpers/CubeTextureCreator.js +++ b/src/js/shot-generator/components/Three/helpers/CubeTextureCreator.js @@ -1,5 +1,3 @@ -import { saveDataURLtoFile } from '../../../helpers/saveDataURLtoFile' -import path from 'path' import * as THREE from 'three' class CubeTextureCreator { @@ -17,7 +15,7 @@ class CubeTextureCreator { // Saves cube map changes back to texture // It takes same elements positions which were initialized in getCubeMapTexture // Require getCubeMapTexture to be launch first - saveCubeMapTexture( imagePath, texture, filename = null ) { + combineImages( texture) { if( !this.imageElements.length || !this.gltf ) return; let image = this.gltf.image; this.drawingCanvas.width = image.width; @@ -29,10 +27,8 @@ class CubeTextureCreator { let croppedImage = texture.image[i]; this.saveFace(croppedImage, element.x, element.y, element.width, element.height, element.name, this.boardPath ); } - let {dir, ext, name} = path.parse(imagePath); let dataUrl = this.drawingCtx.canvas.toDataURL("image/jpeg"); - let properName = filename ? filename : name + ext; - saveDataURLtoFile(dataUrl, `${properName}`, 'models/sceneTextures', this.boardPath); + return dataUrl } // Draw the specific mesh on drawingContext which contains original texture image diff --git a/src/js/xr/src/SceneManagerXR.js b/src/js/xr/src/SceneManagerXR.js index 98f54c5b27..5f1324a461 100644 --- a/src/js/xr/src/SceneManagerXR.js +++ b/src/js/xr/src/SceneManagerXR.js @@ -66,7 +66,7 @@ const Boards = require('./components/ui/Boards') const BonesHelper = require('./three/BonesHelper') const Voicer = require('./three/Voicer') -const CubeTextureCreator = require('./helpers/CubeTextureCreator').default +const CubeTextureCreator = require('../../shot-generator/components/Three/Helpers/CubeTextureCreator').default const musicSystem = require('./music-system') diff --git a/src/js/xr/src/helpers/CubeTextureCreator.js b/src/js/xr/src/helpers/CubeTextureCreator.js deleted file mode 100644 index 1de6a54f6a..0000000000 --- a/src/js/xr/src/helpers/CubeTextureCreator.js +++ /dev/null @@ -1,101 +0,0 @@ -const THREE = require('three') - -class CubeTextureCreator { - constructor() { - this.drawingCanvas = document.createElement('canvas'); - this.croppedCanvas = document.createElement('canvas'); - this.drawingCtx = this.drawingCanvas.getContext('2d'); - this.croppedCtx = this.croppedCanvas.getContext('2d'); - this.faces = ['px', 'nx', 'py', 'ny', 'pz', 'nz']; - this.imageElements = []; - this.gltf; - this.boardPath; - } - - // Parses/crops passed cube texture and loading cube texture from them - // Require Cube map to be one of the recognizable patterns see "recognizeTexturePattern" - getCubeMapTexture( gltf, boardPath ) { - this.gltf = gltf; - this.boardPath = boardPath; - let image = gltf.image; - this.drawingCanvas.width = image.width; - this.drawingCanvas.height = image.height; - - this.recognizeTexturePattern(image); - let cubeTexture = new THREE.CubeTexture(); - if( !this.imageElements.length ) return; - for( let i = 0; i < this.imageElements.length; i++ ) { - let element = this.imageElements[i]; - let texture = this.crop(image, element.x, element.y, element.width, element.height, element.name, boardPath); - cubeTexture.images[i] = texture - } - cubeTexture.needsUpdate = true - - return cubeTexture - } - - // Tries to recognize image(Cube Texture) pattern if pattern is recognized it creates image element - // image element knows it's position and size plus knows name for future use in "getCubeMapTexture" and "saveCubeMapTexture" - // In order to recognize pattern image should have square elements (element should have same width and height) - // and Texture shouldn't have any leftover pixels. e.g. image is 4 columns by 3 rows, element size is 64 by 64(pixels) then image size - // supposed to be 256 by 192(pixels) - recognizeTexturePattern( image ) { - this.imageElements = []; - // 4 by 3 pattern when cubetexture has 4 columns and 3 rows - // - py - - - // nx pz px nz - // - ny - - - if( image.width / 4 === image.height / 3 ) { - let elementSize = image.width / 4; - this.croppedCanvas.width = elementSize; - this.croppedCanvas.height = elementSize; - this.imageElements.push({x:elementSize * 2, y: elementSize, width: elementSize, height:elementSize, name:"px"}); - this.imageElements.push({x:0, y: elementSize, width: elementSize, height:elementSize, name:"nx"}); - this.imageElements.push({x:elementSize, y: 0, width: elementSize, height:elementSize, name:"py"}); - this.imageElements.push({x:elementSize, y: elementSize * 2, width: elementSize, height:elementSize, name:"ny"}); - this.imageElements.push({x:elementSize, y: elementSize, width: elementSize, height:elementSize, name:"pz"}); - this.imageElements.push({x:elementSize * 3, y: elementSize, width: elementSize, height:elementSize, name:"nz"}); - } - // 3 by 4 pattern when cubetexture has 3 columns and 4 rows - // - py - - // pz px nz - // - ny - - // - nx - - if( image.width / 3 === image.height / 4 ) { - let elementSize = image.width / 3; - this.croppedCanvas.width = elementSize; - this.croppedCanvas.height = elementSize; - this.imageElements.push({x:elementSize, y: elementSize, width: elementSize, height:elementSize, name:"px"}); - this.imageElements.push({x:elementSize, y: elementSize * 3, width: elementSize, height:elementSize, name:"nx"}); - this.imageElements.push({x:elementSize, y: 0, width: elementSize, height:elementSize, name:"py"}); - this.imageElements.push({x:elementSize, y: elementSize * 2, width: elementSize, height:elementSize, name:"ny"}); - this.imageElements.push({x:0, y: elementSize, width: elementSize, height:elementSize, name:"pz"}); - this.imageElements.push({x:elementSize * 2, y: elementSize, width: elementSize, height:elementSize, name:"nz"}); - } - - let row = image.width / 6 === image.height; - let column = image.width === image.height / 6; - // 1 by 6 pattern when either we have 1 column or 1 row - // px nx py ny pz nz - if( row || column ) { - let elementSize = row ? image.height : image.width; - this.croppedCanvas.width = elementSize; - this.croppedCanvas.height = elementSize; - for( let i = 0; i < this.faces.length; i++ ) { - this.imageElements.push({x:elementSize * row * i, y: elementSize * i * column, width: elementSize, height:elementSize, name:this.faces[i]}); - } - } - } - - // Crops a segment from images and saves it with passed name - crop( image, x, y, width, height, name, boardPath ) { - this.drawingCtx.drawImage(image, 0, 0, image.width, image.height); - let imageData = this.drawingCtx.getImageData(x, y, width, height); - this.croppedCtx.putImageData(imageData, 0, 0); - let htmlImage = new Image(); - htmlImage.src = this.croppedCtx.canvas.toDataURL(); - return htmlImage - } -} - -export default CubeTextureCreator \ No newline at end of file From 2feb4341b73d98382b9287833677909797721bdb Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Tue, 21 Jul 2020 09:37:45 +0300 Subject: [PATCH 70/88] Renamed clean to clear and added hiding of clear button if image wasn't selected --- .../components/InspectedElement/BrushInspector/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/js/shot-generator/components/InspectedElement/BrushInspector/index.js b/src/js/shot-generator/components/InspectedElement/BrushInspector/index.js index d1caf53899..12cacc30cc 100644 --- a/src/js/shot-generator/components/InspectedElement/BrushInspector/index.js +++ b/src/js/shot-generator/components/InspectedElement/BrushInspector/index.js @@ -74,9 +74,9 @@ React.memo(({ label="mesh color" value={ drawMode.brush.color} onSetValue={setColor}/> } -
-
Clean Selected Image
-
+ { selections.length > 0 &&
+
Clear Selected Image
+
} ) From a5af1d26e0270d3507aaa9910777e8ba653a034d Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Tue, 21 Jul 2020 12:27:00 +0300 Subject: [PATCH 71/88] Fixed saving temp image throws error when image folder doesn't exist --- package-lock.json | 123 +++++------------- .../helpers/saveDataURLtoFile.js | 4 +- 2 files changed, 36 insertions(+), 91 deletions(-) diff --git a/package-lock.json b/package-lock.json index b3920d170c..34c95af6c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2735,8 +2735,7 @@ "version": "2.1.1", "resolved": false, "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -2760,15 +2759,13 @@ "version": "1.0.0", "resolved": false, "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "resolved": false, "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2785,22 +2782,19 @@ "version": "1.1.0", "resolved": false, "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "resolved": false, "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "resolved": false, "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -2931,8 +2925,7 @@ "version": "2.0.3", "resolved": false, "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -2946,7 +2939,6 @@ "resolved": false, "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -2963,7 +2955,6 @@ "resolved": false, "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -2972,15 +2963,13 @@ "version": "0.0.8", "resolved": false, "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.3.5", "resolved": false, "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -3001,7 +2990,6 @@ "resolved": false, "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -3090,8 +3078,7 @@ "version": "1.0.1", "resolved": false, "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -3105,7 +3092,6 @@ "resolved": false, "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -3201,8 +3187,7 @@ "version": "5.1.2", "resolved": false, "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -3244,7 +3229,6 @@ "resolved": false, "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -3266,7 +3250,6 @@ "resolved": false, "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -3315,15 +3298,13 @@ "version": "1.0.2", "resolved": false, "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.3", "resolved": false, "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", - "dev": true, - "optional": true + "dev": true } } }, @@ -4569,8 +4550,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -4594,15 +4574,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4619,22 +4597,19 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -4765,8 +4740,7 @@ "version": "2.0.3", "resolved": false, "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -4780,7 +4754,6 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -4797,7 +4770,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -4806,15 +4778,13 @@ "version": "0.0.8", "resolved": false, "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.3.5", "resolved": false, "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -4835,7 +4805,6 @@ "resolved": false, "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -4924,8 +4893,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -4939,7 +4907,6 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -5035,8 +5002,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -5078,7 +5044,6 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -5100,7 +5065,6 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -5149,15 +5113,13 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.3", "resolved": false, "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", - "dev": true, - "optional": true + "dev": true } } }, @@ -18494,8 +18456,7 @@ "version": "2.1.1", "resolved": false, "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -18519,15 +18480,13 @@ "version": "1.0.0", "resolved": false, "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "resolved": false, "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -18544,22 +18503,19 @@ "version": "1.1.0", "resolved": false, "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "resolved": false, "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "resolved": false, "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -18690,8 +18646,7 @@ "version": "2.0.3", "resolved": false, "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -18705,7 +18660,6 @@ "resolved": false, "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -18722,7 +18676,6 @@ "resolved": false, "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -18731,15 +18684,13 @@ "version": "0.0.8", "resolved": false, "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.3.5", "resolved": false, "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -18760,7 +18711,6 @@ "resolved": false, "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -18849,8 +18799,7 @@ "version": "1.0.1", "resolved": false, "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -18864,7 +18813,6 @@ "resolved": false, "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -18960,8 +18908,7 @@ "version": "5.1.2", "resolved": false, "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -19003,7 +18950,6 @@ "resolved": false, "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -19025,7 +18971,6 @@ "resolved": false, "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -19074,15 +19019,13 @@ "version": "1.0.2", "resolved": false, "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.3", "resolved": false, "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", - "dev": true, - "optional": true + "dev": true } } }, diff --git a/src/js/shot-generator/helpers/saveDataURLtoFile.js b/src/js/shot-generator/helpers/saveDataURLtoFile.js index c22bf70a7a..844e9b6877 100644 --- a/src/js/shot-generator/helpers/saveDataURLtoFile.js +++ b/src/js/shot-generator/helpers/saveDataURLtoFile.js @@ -19,7 +19,9 @@ const saveDataURLtoTempFile = (dataURL, boardPath, updateObject, object) => { } let tempFilename = `temp_${object.userData.id}-${Date.now()}-texture.png` object.userData.tempImagePath = tempFilename - let imageFilePath = path.join(path.dirname(boardPath), 'models/images', tempFilename) + let filePath = path.join(path.dirname(boardPath), 'models/images') + let imageFilePath = path.join(filePath, tempFilename) + fs.ensureDirSync(filePath) fs.writeFileSync(imageFilePath, imageData, 'base64') let projectDir = path.dirname(boardPath) let assetsDir = path.join(projectDir, 'models', 'images') From 3e19eef647a0af6ebc0d59f52e29e641bf044fd8 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Tue, 21 Jul 2020 12:58:36 +0300 Subject: [PATCH 72/88] Fixed cubemaps saving --- .../shot-generator/components/Editor/index.js | 21 ++++++++++++++----- .../components/Three/SceneBackground.js | 2 +- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/js/shot-generator/components/Editor/index.js b/src/js/shot-generator/components/Editor/index.js index 13b2ab60da..9de011ce69 100644 --- a/src/js/shot-generator/components/Editor/index.js +++ b/src/js/shot-generator/components/Editor/index.js @@ -27,7 +27,8 @@ import { setMainViewCamera, getIsSceneDirty, getSceneObjects, - updateObject + updateObject, + updateWorld } from './../../../shared/reducers/shot-generator' import notifications from './../../../window/notifications' @@ -195,15 +196,16 @@ const Editor = React.memo(({ const saveImages = () => { let imageObjects withState((dispatch, state) => { - imageObjects = Object.values(getSceneObjects(state)).filter(obj => obj.type === "image") + let storyboarderFilePath = state.meta.storyboarderFilePath + imageObjects = Object.values(getSceneObjects(state)).filter(obj => obj.type === "image") for( let i = 0; i < imageObjects.length; i++ ) { let image = imageObjects[i] let imgComponent = largeCanvasData.current.scene.__interaction.find(obj => obj.userData.id === image.id) let isImageExist = imgComponent.userData.tempImagePath if(!isImageExist) continue - let tempImageFilePath = path.join(path.dirname(state.meta.storyboarderFilePath), 'models/images', imgComponent.userData.tempImagePath) - let imageFilePath = path.join(path.dirname(state.meta.storyboarderFilePath), 'models/images', `${image.id}-texture.png`) - let projectDir = path.dirname(state.meta.storyboarderFilePath) + let tempImageFilePath = path.join(path.dirname(storyboarderFilePath), 'models/images', imgComponent.userData.tempImagePath) + let imageFilePath = path.join(path.dirname(storyboarderFilePath), 'models/images', `${image.id}-texture.png`) + let projectDir = path.dirname(storyboarderFilePath) let assetsDir = path.join(projectDir, 'models', 'images') fs.ensureDirSync(assetsDir) let dst = path.join(assetsDir, path.basename(imageFilePath)) @@ -214,6 +216,15 @@ const Editor = React.memo(({ imgComponent.userData.tempImagePath = null dispatch(updateObject(image.id, {imageAttachmentIds: [id]})) } + if(largeCanvasData.current.scene.userData.tempPath) { + let tempImageFilePath = path.join(path.dirname(storyboarderFilePath), 'models/sceneTextures/', largeCanvasData.current.scene.userData.tempPath) + let imageFilePath = path.join(path.dirname(storyboarderFilePath), largeCanvasData.current.scene.userData.texturePath) + removeAsset(imageFilePath) + fs.copySync(tempImageFilePath, imageFilePath, {overwrite:true}) + fs.remove(tempImageFilePath) + dispatch(updateWorld({sceneTexture: largeCanvasData.current.scene.userData.texturePath})) + largeCanvasData.current.scene.userData.tempPath = null + } }) } const { insertNewShot, saveCurrentShot } = useSaveToStoryboarder( diff --git a/src/js/shot-generator/components/Three/SceneBackground.js b/src/js/shot-generator/components/Three/SceneBackground.js index 0aeda73a24..501df70fe8 100644 --- a/src/js/shot-generator/components/Three/SceneBackground.js +++ b/src/js/shot-generator/components/Three/SceneBackground.js @@ -8,7 +8,7 @@ import CubeTextureCreator from './helpers/CubeTextureCreator' import fs from 'fs-extra' import path from 'path' import SceneTextureType from '../InspectedWorld/SceneTextureType' - +import { saveDataURLtoFile } from '../../helpers/saveDataURLtoFile' const SceneBackground = React.memo(({ imagePath, world, storyboarderFilePath, updateWorld, drawingSceneTexture }) => { const texturePath = useRef() const { scene, camera, gl } = useThree() From 5f34f7248d9ff970114dfa2f08cc9fdd977d6105 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Mon, 28 Sep 2020 14:30:54 +0300 Subject: [PATCH 73/88] Refactored ddrawing textures --- src/js/shot-generator/SceneManagerR3fLarge.js | 4 +- .../InspectedWorld/DrawingTextureType.js | 6 + .../InspectedWorld/SceneTextureType.js | 6 - .../components/InspectedWorld/index.js | 10 +- .../Three/Helpers/DrawingTextureContainer.js | 51 +++++++++ .../components/Three/Helpers/SimpleTexture.js | 1 - .../shot-generator/components/Three/Image.js | 19 ++- .../components/Three/SceneBackground.js | 108 ++++++++++-------- .../shot-generator/hooks/use-draw-on-image.js | 81 ++++++------- 9 files changed, 173 insertions(+), 113 deletions(-) create mode 100644 src/js/shot-generator/components/InspectedWorld/DrawingTextureType.js delete mode 100644 src/js/shot-generator/components/InspectedWorld/SceneTextureType.js create mode 100644 src/js/shot-generator/components/Three/Helpers/DrawingTextureContainer.js diff --git a/src/js/shot-generator/SceneManagerR3fLarge.js b/src/js/shot-generator/SceneManagerR3fLarge.js index ba6eb8a3db..26d7b5168c 100644 --- a/src/js/shot-generator/SceneManagerR3fLarge.js +++ b/src/js/shot-generator/SceneManagerR3fLarge.js @@ -162,7 +162,7 @@ const SceneManagerR3fLarge = connect( } }, [drawMode.isEnabled, drawMode.brush]) - const {drawingTextures, drawingSceneTexture} = useDrawOnImage(drawMode, storyboarderFilePath, updateObject) + const drawingTextures = useDrawOnImage(drawMode, storyboarderFilePath, updateObject) useEffect(() => { let sgIkHelper = SGIkHelper.getInstance() @@ -490,7 +490,7 @@ const SceneManagerR3fLarge = connect( world={world} storyboarderFilePath={ storyboarderFilePath } updateWorld={ updateWorld } - drawingSceneTexture={ drawingSceneTexture }/> + drawingTextures={ drawingTextures }/> } { roomTexture && { @@ -59,11 +59,11 @@ const InspectedWorld = React.memo(({updateObject, updateWorld, updateWorldRoom, }, []) const setSceneTextureFile = useCallback((event) => { - setWorldTexture(SceneTextureType.Image, event) + setWorldTexture(DrawingTextureType.Simple, event) }, []) const setSceneCubeMap = useCallback((event) => { - setWorldTexture(SceneTextureType.CubeMap, event) + setWorldTexture(DrawingTextureType.Cubemap, event) }, []) const setGrayscale = useCallback(() => updateWorldEnvironment({grayscale: !world.environment.grayscale}), [world.environment.grayscale]) @@ -124,7 +124,7 @@ const InspectedWorld = React.memo(({updateObject, updateWorld, updateWorldRoom, onSetValue={setBackground} />
} - {(!world.textureType || world.textureType === SceneTextureType.CubeMap) && } - {(!world.textureType || world.textureType === SceneTextureType.Image) && texture.draw(...arg) + if(!save) save = () => {} + + this.textures[id] = { type: textureObjectType, draw: draw, save: save, texture: texture } + return texture + } + + getTextures() { + return this.textures + } + + getTexturesByObjectType(type) { + return Object.values(this.textures).filter(obj => obj.type === type) + } + + getTextureById(id) { + return this.textures[id] + } + + removeTexture(id) { + delete this.textures[id] + } + +} + +export { + DrawingTextureContainer, + TextureObjectType +} \ No newline at end of file diff --git a/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js b/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js index 757762b1bd..f388c2b434 100644 --- a/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js +++ b/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js @@ -17,7 +17,6 @@ class SimpleTexture extends DrawingTexture { getImage(mime) { return super.getImage(mime)[0]; - } setMesh(type) { diff --git a/src/js/shot-generator/components/Three/Image.js b/src/js/shot-generator/components/Three/Image.js index bac30a8abf..efd3bba609 100644 --- a/src/js/shot-generator/components/Three/Image.js +++ b/src/js/shot-generator/components/Three/Image.js @@ -5,8 +5,10 @@ import { useAsset } from '../../hooks/use-assets-manager' import { SHOT_LAYERS } from '../../utils/ShotLayers' import RoundedBoxGeometryCreator from './../../../vendor/three-rounded-box' import { axis } from '../../../shared/IK/utils/TransformControls' -import SimpleTexture from './Helpers/SimpleTexture' +import DrawingTextureType from '../InspectedWorld/DrawingTextureType' +import { TextureObjectType} from './Helpers/DrawingTextureContainer' import createRoundedPlane from './Helpers/create-rounded-plane' +import { saveDataURLtoTempFile } from '../../helpers/saveDataURLtoFile' const RoundedBoxGeometry = RoundedBoxGeometryCreator(THREE) import fs from 'fs-extra' import path from 'path' @@ -17,15 +19,19 @@ const Image = React.memo(({ sceneObject, isSelected, imagesPaths, ...props }) => const aspect = useRef(1) const ref = useRef() const material = useMemo(() => { - props.drawTextures[sceneObject.id] = new SimpleTexture() + let texture = props.drawTextures.createTexture(sceneObject.id, DrawingTextureType.Simple, TextureObjectType.Image) let material = new THREE.MeshToonMaterial({ transparent: true, side: THREE.DoubleSide }); - props.drawTextures[sceneObject.id].createMaterial(material) + texture.createMaterial(material) return material }, []) + const save = () => { + saveDataURLtoTempFile( props.drawTextures.getTextureById(sceneObject.id).texture.getImage("image/png"), props.storyboarderFilePath, props.updateObject, ref.current) + } + useEffect(() => { return () => { - delete props.drawTextures[sceneObject.id] + delete props.drawTextures.removeTexture(sceneObject.id) } }, []) @@ -38,7 +44,9 @@ const Image = React.memo(({ sceneObject, isSelected, imagesPaths, ...props }) => const { width, height } = texture.image aspect.current = width / height if (material) { - props.drawTextures[sceneObject.id].setTexture(texture) + let textureObject = props.drawTextures.getTextureById(sceneObject.id) + textureObject.texture.setTexture(texture) + textureObject.save = () => save() material.needsUpdate = true } }, [texture]) @@ -89,6 +97,7 @@ const Image = React.memo(({ sceneObject, isSelected, imagesPaths, ...props }) => const { x, y, z, visible, height, rotation, locked } = sceneObject + useEffect(() => { if(!props.objectRotationControl || !isSelected) return props.objectRotationControl.IsEnabled = !locked diff --git a/src/js/shot-generator/components/Three/SceneBackground.js b/src/js/shot-generator/components/Three/SceneBackground.js index 501df70fe8..a95fb0a87e 100644 --- a/src/js/shot-generator/components/Three/SceneBackground.js +++ b/src/js/shot-generator/components/Three/SceneBackground.js @@ -2,20 +2,20 @@ import * as THREE from 'three' import React, { useEffect, useRef } from 'react' import { useThree } from 'react-three-fiber' import { useAsset } from '../../hooks/use-assets-manager' -import CubeMapDrawingTexture from './helpers/cubeMapDrawingTexture' -import SimpleTexture from './helpers/SimpleTexture' import CubeTextureCreator from './helpers/CubeTextureCreator' import fs from 'fs-extra' import path from 'path' -import SceneTextureType from '../InspectedWorld/SceneTextureType' +import DrawingTextureType from '../InspectedWorld/DrawingTextureType' import { saveDataURLtoFile } from '../../helpers/saveDataURLtoFile' -const SceneBackground = React.memo(({ imagePath, world, storyboarderFilePath, updateWorld, drawingSceneTexture }) => { +import { TextureObjectType } from './Helpers/DrawingTextureContainer' +const SceneBackground = React.memo(({ imagePath, world, storyboarderFilePath, updateWorld, drawingTextures }) => { const texturePath = useRef() const { scene, camera, gl } = useThree() const { asset: texture } = useAsset( !imagePath[0] || !scene.userData.tempPath ? imagePath[0] : imagePath[0].includes(scene.userData.tempPath ) ? null : imagePath[0]) const intersectionBox = useRef() const intersectionCamera = useRef() const cubeTextureCreator = useRef( new CubeTextureCreator()) + const id = useRef() useEffect(() => { return () => { @@ -36,48 +36,18 @@ const SceneBackground = React.memo(({ imagePath, world, storyboarderFilePath, up } }, [imagePath[0]]) - useEffect(() => { - if(world.textureType === SceneTextureType.CubeMap) { - drawingSceneTexture.texture = new CubeMapDrawingTexture() - let geometry = new THREE.BoxBufferGeometry(1, 1, 1) - let material = new THREE.MeshBasicMaterial({ side: THREE.DoubleSide}) - intersectionBox.current = new THREE.Mesh(geometry, material) - intersectionCamera.current = camera.clone() - - } else if(world.textureType === SceneTextureType.Image) { - drawingSceneTexture.texture = new SimpleTexture() - let geometry = new THREE.PlaneBufferGeometry(1, 1) - let material = new THREE.MeshBasicMaterial({ side: THREE.DoubleSide}) - intersectionBox.current = new THREE.Mesh(geometry, material) - intersectionBox.current.position.set(0, 0, -1) - intersectionBox.current.updateMatrixWorld(true) - intersectionCamera.current = new THREE.OrthographicCamera(-1, 1, 1, 1, 1, 1000) - intersectionCamera.current.add(intersectionBox.current) - } - return () => { - if(intersectionBox.current) { - intersectionBox.current.geometry.dispose() - intersectionBox.current.material.dispose() - } - intersectionCamera.current = null - intersectionBox.current = null - } - }, [world.textureType]) - - useEffect(() => { - scene.background = new THREE.Color(world.backgroundColor) - }, [world.backgroundColor]) const draw = (mousePos, camera, drawingBrush) => { - if(world.textureType === SceneTextureType.CubeMap) { - drawingSceneTexture.texture.createMaterial(scene.background); + let textureObject = drawingTextures.getTextureById(id.current) + if(world.textureType === DrawingTextureType.Cubemap) { + textureObject.texture.createMaterial(scene.background); intersectionCamera.current.copy(camera) intersectionCamera.current.position.set(0, 0, 0) } intersectionCamera.current.quaternion.copy(camera.worldQuaternion()) intersectionCamera.current.updateMatrixWorld(true) - drawingSceneTexture.texture.draw(mousePos, intersectionBox.current, intersectionCamera.current, drawingBrush) + textureObject.texture.draw(mousePos, intersectionBox.current, intersectionCamera.current, drawingBrush) } const cleanUpTempFile = () => { @@ -91,34 +61,72 @@ const SceneBackground = React.memo(({ imagePath, world, storyboarderFilePath, up const save = () => { cleanUpTempFile() let tempFileName = `temp_scenetexture-${Date.now()}.jpg` - if(world.textureType === SceneTextureType.CubeMap) { + if(world.textureType === DrawingTextureType.Cubemap) { let dataUrl = cubeTextureCreator.current.combineImages(scene.background) let {dir, ext, name} = path.parse(imagePath[0]); let properName = tempFileName ? tempFileName : name + ext; saveDataURLtoFile(dataUrl, `${properName}`, 'models/sceneTextures', storyboarderFilePath); - } else if(world.textureType === SceneTextureType.Image) { - let imageData = drawingSceneTexture.texture.getImage("image/jpg") - let imageFilePath = path.join(path.dirname(storyboarderFilePath), 'models/sceneTextures', tempFileName) + } else if(world.textureType === DrawingTextureType.Simple) { + let imageData = drawingTextures.getTextureById(id.current).texture.getImage('image/jpg').replace(/^data:image\/\w+;base64,/, '') + let dirpath = path.join(path.dirname(storyboarderFilePath), 'models', 'sceneTextures') + let imageFilePath = path.join(dirpath, tempFileName) + fs.ensureDirSync(dirpath) fs.writeFileSync(imageFilePath, imageData, 'base64') } - updateWorld({sceneTexture: 'models/sceneTextures/' + tempFileName}) + updateWorld({sceneTexture: path.join('models', 'sceneTextures', tempFileName) }) scene.userData.tempPath = tempFileName texturePath.current = tempFileName } + + useEffect(() => { + id.current = THREE.MathUtils.generateUUID() + if(world.textureType === DrawingTextureType.Cubemap) { + drawingTextures.createTexture(id.current, DrawingTextureType.Cubemap, TextureObjectType.Background, save, draw) + let geometry = new THREE.BoxBufferGeometry(1, 1, 1) + let material = new THREE.MeshBasicMaterial({ side: THREE.DoubleSide}) + intersectionBox.current = new THREE.Mesh(geometry, material) + intersectionCamera.current = camera.clone() + + } else if(world.textureType === DrawingTextureType.Simple) { + drawingTextures.createTexture(id.current, DrawingTextureType.Simple, TextureObjectType.Background, save, draw) + let geometry = new THREE.PlaneBufferGeometry(1, 1) + let material = new THREE.MeshBasicMaterial({ side: THREE.DoubleSide}) + intersectionBox.current = new THREE.Mesh(geometry, material) + intersectionBox.current.position.set(0, 0, -1) + intersectionBox.current.updateMatrixWorld(true) + intersectionCamera.current = new THREE.OrthographicCamera(-1, 1, 1, 1, 1, 1000) + intersectionCamera.current.add(intersectionBox.current) + } + return () => { + if(intersectionBox.current) { + intersectionBox.current.geometry.dispose() + intersectionBox.current.material.dispose() + } + drawingTextures.removeTexture(id.current) + intersectionCamera.current = null + intersectionBox.current = null + } + }, [world.textureType]) + + useEffect(() => { + scene.background = new THREE.Color(world.backgroundColor) + }, [world.backgroundColor]) + useEffect(() => { if(!texture ) return cleanUpTempFile() let backgroundTexture - if(world.textureType === SceneTextureType.CubeMap) { + let textureObject = drawingTextures.getTextureById(id.current) + if(world.textureType === DrawingTextureType.Cubemap) { backgroundTexture = cubeTextureCreator.current.getCubeMapTexture(texture, storyboarderFilePath); if(backgroundTexture) { - drawingSceneTexture.save = save + textureObject.save = () => save() } else { updateWorld({ sceneTexture: null, textureType: null }) return } - } else if(world.textureType === SceneTextureType.Image) { + } else if(world.textureType === DrawingTextureType.Simple) { texture.wrapS = texture.wrapT = THREE.RepeatWrapping texture.offset.set(0, 0) texture.repeat.set(1, 1) @@ -131,13 +139,13 @@ const SceneBackground = React.memo(({ imagePath, world, storyboarderFilePath, up intersectionCamera.current.updateProjectionMatrix() intersectionBox.current.scale.set(1 * aspect, 1, 1) intersectionBox.current.updateMatrixWorld(true) - backgroundTexture = drawingSceneTexture.texture.createMaterial({map: texture}).map - drawingSceneTexture.texture.setTexture(texture) - drawingSceneTexture.save = save + backgroundTexture = textureObject.texture.createMaterial({map: texture}).map + textureObject.texture.setTexture(texture) + textureObject.save = () => save() } scene.userData.texturePath = world.sceneTexture scene.background = backgroundTexture; - drawingSceneTexture.draw = draw + textureObject.draw = (...args) => draw(...args) }, [texture]) diff --git a/src/js/shot-generator/hooks/use-draw-on-image.js b/src/js/shot-generator/hooks/use-draw-on-image.js index a8030972d2..a3e67bd81d 100644 --- a/src/js/shot-generator/hooks/use-draw-on-image.js +++ b/src/js/shot-generator/hooks/use-draw-on-image.js @@ -1,13 +1,20 @@ import React, {useEffect, useRef } from "react" import { useThree } from "react-three-fiber"; import mouse from '../utils/mouseToClipSpace' -import { saveDataURLtoTempFile } from '../helpers/saveDataURLtoFile' -const useDrawOnImage = (drawMode, storyboarderFilePath, updateObject ) => { +import { DrawingTextureContainer, TextureObjectType } from '../components/Three/Helpers/DrawingTextureContainer' + +const useDrawOnImage = (drawMode) => { const {gl, scene, camera} = useThree() const isDrawStarted = useRef(false) const raycaster = useRef(new THREE.Raycaster()) - const drawingTextures = useRef({}) - const drawingSceneTexture = useRef({}) + const drawingTextures = useRef(null) + const getDrawingTextures = () => { + if(drawingTextures.current === null) { + drawingTextures.current = new DrawingTextureContainer() + } + return drawingTextures.current + } + useEffect(() => { if(drawMode.isEnabled) { gl.domElement.addEventListener( 'mousedown', onKeyDown ) @@ -22,52 +29,44 @@ const useDrawOnImage = (drawMode, storyboarderFilePath, updateObject ) => { useEffect(() => { if(!drawMode.cleanImages || !drawMode.cleanImages.length) return for(let i = 0; i < drawMode.cleanImages.length; i++) { - drawingTextures.current[drawMode.cleanImages[i]].cleanImage() + getDrawingTextures().getTextures()[drawMode.cleanImages[i]].cleanImage() } }, [drawMode.cleanImages]) useEffect(() => { - let keys = Object.keys(drawingTextures.current) - for(let i = 0; i < keys.length; i++) { - let key = keys[i] - drawingTextures.current[key].setMesh(drawMode.brush.type) + let values = Object.values(getDrawingTextures().getTextures()) + for(let i = 0; i < values.length; i++) { + values[i].texture.setMesh(drawMode.brush.type) } - if(drawingSceneTexture.current && drawingSceneTexture.current.texture) - drawingSceneTexture.current.texture.setMesh(drawMode.brush.type) }, [drawMode.brush.type]) - let getImageObjects = () => scene.__interaction.filter(object => object.userData.type === "image") - const onKeyDown = (event) => { - isDrawStarted.current = true; - let keys = Object.keys(drawingTextures.current) - for(let i = 0; i < keys.length; i++) { - let key = keys[i] - drawingTextures.current[key].prepareToDraw(); - } - if(drawingSceneTexture.current && drawingSceneTexture.current.texture) { - drawingSceneTexture.current.texture.prepareToDraw() + isDrawStarted.current = true + let values = Object.values(getDrawingTextures().getTextures()) + for(let i = 0; i < values.length; i++) { + values[i].texture.setMesh(drawMode.brush.type) + values[i].texture.prepareToDraw() } draw(event) gl.domElement.addEventListener('mousemove', draw) } const draw = (event) => { - let keys = Object.keys(drawingTextures.current) + let values = Object.values(getDrawingTextures().getTexturesByObjectType(TextureObjectType.Image)) let {x, y} = mouse({x: event.clientX, y: event.clientY}, gl) raycaster.current.setFromCamera({x, y}, camera) - let imageObjects = getImageObjects() + let imageObjects = scene.__interaction.filter(object => object.userData.type === "image") let intersections = raycaster.current.intersectObjects(imageObjects, true) - if(!intersections.length && drawingSceneTexture.current.draw) { - let texture = drawingSceneTexture.current + let backgroundTexture = getDrawingTextures().getTexturesByObjectType(TextureObjectType.Background) + if(!intersections.length && backgroundTexture.length) { + let texture = backgroundTexture[0] texture.draw({x, y}, camera, drawMode.brush) } - for(let i = 0; i < keys.length; i++) { - let key = keys[i] - let drawingTexture = drawingTextures.current[key]; - let object = drawingTexture.material.parent.parent; + for(let i = 0; i < values.length; i++) { + let drawingTexture = values[i].texture + let object = drawingTexture.material.parent.parent if(!object || !object.visible) continue - drawingTexture.draw({x, y}, object, camera, drawMode.brush, gl) + values[i].draw({x, y}, object, camera, drawMode.brush, gl) } } @@ -75,23 +74,17 @@ const useDrawOnImage = (drawMode, storyboarderFilePath, updateObject ) => { if(!isDrawStarted.current) return gl.domElement.removeEventListener('mousemove', draw) isDrawStarted.current = false; - let keys = Object.keys(drawingTextures.current) - for(let i = 0; i < keys.length; i++) { - let key = keys[i] - drawingTextures.current[key].endDraw(); - let object = scene.__interaction.find((obj) => obj.userData.id === key) - if(drawingTextures.current[key].isChanged) { - drawingTextures.current[key].isChanged = false - saveDataURLtoTempFile(drawingTextures.current[key].getImage("image/png"), storyboarderFilePath, updateObject, object) + let values = Object.values(getDrawingTextures().getTextures()) + for(let i = 0; i < values.length; i++) { + let texture = values[i].texture + texture.endDraw() + if(texture.isChanged) { + texture.isChanged = false + values[i].save() } } - if( drawingSceneTexture.current.save && drawingSceneTexture.current.texture.isChanged) { - drawingSceneTexture.current.texture.isChanged = false - drawingSceneTexture.current.texture.endDraw() - drawingSceneTexture.current.save() - } } - return {drawingTextures: drawingTextures.current, drawingSceneTexture: drawingSceneTexture.current} + return getDrawingTextures() } export default useDrawOnImage \ No newline at end of file From a9f7e3809da861d35fc3d229efd00ce63e0191af Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Mon, 28 Sep 2020 15:46:26 +0300 Subject: [PATCH 74/88] Fixed eraser cannot draw on top of simple brush --- .../components/Three/Helpers/Brushes/Brush.js | 12 ++++++++++-- .../Three/Helpers/Brushes/EraserBrush.js | 4 +++- .../Three/Helpers/Brushes/SimpleBrush.js | 2 +- .../components/Three/Helpers/SimpleTexture.js | 2 +- .../components/Three/helpers/DrawingTexture.js | 7 ++++--- .../Three/helpers/cubeMapDrawingTexture.js | 2 +- src/js/shot-generator/hooks/use-draw-on-image.js | 14 +++++++------- 7 files changed, 27 insertions(+), 16 deletions(-) diff --git a/src/js/shot-generator/components/Three/Helpers/Brushes/Brush.js b/src/js/shot-generator/components/Three/Helpers/Brushes/Brush.js index 6c845635bc..18f8339873 100644 --- a/src/js/shot-generator/components/Three/Helpers/Brushes/Brush.js +++ b/src/js/shot-generator/components/Three/Helpers/Brushes/Brush.js @@ -16,6 +16,7 @@ class Brush { } stopDrawing() { + this.drawingCtx.save() this.isDrawing = false; this.positionBuffer.flushArray(); } @@ -28,9 +29,16 @@ class Brush { this.prevPos = null; } - draw(currentPos, brush) { + cleanUp() { + this.positionBuffer.flushArray(); + this.positionBuffer = null; + this.drawingCtx = null; + this.resetMeshPos() + } + + draw(brush) { this.brushSize = brush.size; - let { width, height } = this.drawingCtx.canvas; + let { width, height } = this.drawingCtx.canvas; if(this.percentageBasedSize) { let smallerSide = width > height ? height : width; let sizePercent = brush.size / this.defaultHeight; diff --git a/src/js/shot-generator/components/Three/Helpers/Brushes/EraserBrush.js b/src/js/shot-generator/components/Three/Helpers/Brushes/EraserBrush.js index d8448f4d42..99463b7024 100644 --- a/src/js/shot-generator/components/Three/Helpers/Brushes/EraserBrush.js +++ b/src/js/shot-generator/components/Three/Helpers/Brushes/EraserBrush.js @@ -5,8 +5,9 @@ class EraserBrush extends Brush { } draw(currentPos, brush) { - super.draw(currentPos, brush); + super.draw(brush); this.drawingCtx.fillStyle = 'white'; + this.drawingCtx.strokeStyle = 'white'; let prevX, prevY; if(this.positionBuffer.currentLength === 0) { prevX = currentPos.x; @@ -29,6 +30,7 @@ class EraserBrush extends Brush { xOffset /= length; yOffset /= length; let size = this.brushSize; + this.drawingCtx.beginPath() for(let i = 0; i < length; i++) { let x = xOffset * i; let y = yOffset * i; diff --git a/src/js/shot-generator/components/Three/Helpers/Brushes/SimpleBrush.js b/src/js/shot-generator/components/Three/Helpers/Brushes/SimpleBrush.js index 144a665ed9..ba21bb9e27 100644 --- a/src/js/shot-generator/components/Three/Helpers/Brushes/SimpleBrush.js +++ b/src/js/shot-generator/components/Three/Helpers/Brushes/SimpleBrush.js @@ -36,7 +36,7 @@ class SimpleBrush extends Brush { } draw(currentPos, brush) { - super.draw(currentPos, brush); + super.draw(brush); this.drawingCtx.strokeStyle = brush.color; this.drawingCtx.fillStyle = brush.color; this.drawingCtx.lineWidth = this.brushSize * 2; diff --git a/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js b/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js index f388c2b434..16cb2ada3c 100644 --- a/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js +++ b/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js @@ -55,7 +55,7 @@ class SimpleTexture extends DrawingTexture { } draw (mousePosition, object, camera, brush, gl) { - let intersection = super.draw(mousePosition, object, camera, brush); + let intersection = super.draw(mousePosition, object, camera); // If we don't have a uv coordinates and as a last resort we trying to translate mouse into object coordinate // From object coordinate we can sort of simulate uv coordinate logic for plain object // NOTE() : This won't work for any object except plain object( image object ) diff --git a/src/js/shot-generator/components/Three/helpers/DrawingTexture.js b/src/js/shot-generator/components/Three/helpers/DrawingTexture.js index b520e8ab70..e3c8cead47 100644 --- a/src/js/shot-generator/components/Three/helpers/DrawingTexture.js +++ b/src/js/shot-generator/components/Three/helpers/DrawingTexture.js @@ -22,6 +22,7 @@ class DrawingTexture { } setMesh(type) { + if(this.drawingBrush) this.drawingBrush.cleanUp() switch(type) { case "Simple": this.drawingBrush = new SimpleBrush(); @@ -39,8 +40,8 @@ class DrawingTexture { for(let i = 0; i < this.drawingCtxes.length; i++) { canvas = this.drawingCanvases[i]; context = this.drawingCtxes[i]; - width = this.drawingCanvases[i].width; - height = this.drawingCanvases[i].height; + width = canvas.width; + height = canvas.height; context.fillStyle = "#ffffff"; context.fillRect(0, 0, width, height); } @@ -69,7 +70,7 @@ class DrawingTexture { return intersects.length && intersects[0]; } - draw (mousePosition, object, camera, brush) { + draw (mousePosition, object, camera) { let intersection = this.intersectImage(mousePosition.x, mousePosition.y, object, camera); if(intersection.uv === null) { diff --git a/src/js/shot-generator/components/Three/helpers/cubeMapDrawingTexture.js b/src/js/shot-generator/components/Three/helpers/cubeMapDrawingTexture.js index b332e6e018..ad49928165 100644 --- a/src/js/shot-generator/components/Three/helpers/cubeMapDrawingTexture.js +++ b/src/js/shot-generator/components/Three/helpers/cubeMapDrawingTexture.js @@ -41,7 +41,7 @@ class CubeMapDrawingTexture extends DrawingTexture { draw (mousePosition, object, camera, brush) { - let intersection = super.draw(mousePosition, object, camera, brush) + let intersection = super.draw(mousePosition, object, camera) if(!intersection) return; let index; if(intersection.face.normal.x) { diff --git a/src/js/shot-generator/hooks/use-draw-on-image.js b/src/js/shot-generator/hooks/use-draw-on-image.js index a3e67bd81d..e06d93d12e 100644 --- a/src/js/shot-generator/hooks/use-draw-on-image.js +++ b/src/js/shot-generator/hooks/use-draw-on-image.js @@ -17,12 +17,12 @@ const useDrawOnImage = (drawMode) => { useEffect(() => { if(drawMode.isEnabled) { - gl.domElement.addEventListener( 'mousedown', onKeyDown ) - window.addEventListener( 'mouseup', onKeyUp ) + gl.domElement.addEventListener( 'mousedown', onMouseDown ) + window.addEventListener( 'mouseup', onMouseUp ) } return () => { - gl.domElement.removeEventListener( 'mousedown', onKeyDown ) - window.removeEventListener( 'mouseup', onKeyUp ) + gl.domElement.removeEventListener( 'mousedown', onMouseDown ) + window.removeEventListener( 'mouseup', onMouseUp ) } }, [drawMode.isEnabled, drawMode.brush]) @@ -40,7 +40,7 @@ const useDrawOnImage = (drawMode) => { } }, [drawMode.brush.type]) - const onKeyDown = (event) => { + const onMouseDown = (event) => { isDrawStarted.current = true let values = Object.values(getDrawingTextures().getTextures()) for(let i = 0; i < values.length; i++) { @@ -70,15 +70,15 @@ const useDrawOnImage = (drawMode) => { } } - const onKeyUp = (event) => { + const onMouseUp = (event) => { if(!isDrawStarted.current) return gl.domElement.removeEventListener('mousemove', draw) isDrawStarted.current = false; let values = Object.values(getDrawingTextures().getTextures()) for(let i = 0; i < values.length; i++) { let texture = values[i].texture - texture.endDraw() if(texture.isChanged) { + texture.endDraw() texture.isChanged = false values[i].save() } From 2102a77816cd2d6100d93e5a5afc553cf57cf321 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Tue, 29 Sep 2020 15:56:06 +0300 Subject: [PATCH 75/88] Added CurveBrush with Catmull rom spline algorithm --- .../components/Three/Helpers/Brushes/Brush.js | 1 - .../Three/Helpers/Brushes/CurveBrush.js | 80 +++++++++++++++++++ .../Three/Helpers/Brushes/EraserBrush.js | 2 +- .../Three/Helpers/Brushes/PointBuffer.js | 17 +++- .../Three/Helpers/Brushes/SimpleBrush.js | 4 +- .../Three/helpers/DrawingTexture.js | 8 +- 6 files changed, 102 insertions(+), 10 deletions(-) create mode 100644 src/js/shot-generator/components/Three/Helpers/Brushes/CurveBrush.js diff --git a/src/js/shot-generator/components/Three/Helpers/Brushes/Brush.js b/src/js/shot-generator/components/Three/Helpers/Brushes/Brush.js index 18f8339873..be481a0b01 100644 --- a/src/js/shot-generator/components/Three/Helpers/Brushes/Brush.js +++ b/src/js/shot-generator/components/Three/Helpers/Brushes/Brush.js @@ -16,7 +16,6 @@ class Brush { } stopDrawing() { - this.drawingCtx.save() this.isDrawing = false; this.positionBuffer.flushArray(); } diff --git a/src/js/shot-generator/components/Three/Helpers/Brushes/CurveBrush.js b/src/js/shot-generator/components/Three/Helpers/Brushes/CurveBrush.js new file mode 100644 index 0000000000..ccb4754009 --- /dev/null +++ b/src/js/shot-generator/components/Three/Helpers/Brushes/CurveBrush.js @@ -0,0 +1,80 @@ +import Brush from './Brush' +const getVector2FromBuffer = (buffer, index) => { + let elements = buffer.getElements(index); + return { x:elements[0], y:elements[1] }; +} + +const getRangeVector2FromBuffer = (buffer, from, to) => { + let points = []; + for(let i = from; i < to; i ++) { + let elements = buffer.getElements(i); + points.push(elements); + } + + return points; +} + +const getMatrix = (arr) => { + return arr.map(p => { + if(p !== undefined) { return { x: p[0], y: p[1] }} + }) +} + +class CurveBrush extends Brush { + + constructor(drawingCtx) { + super(drawingCtx); + } + + draw(currentPos, brush) { + super.draw(brush); + + this.drawingCtx.strokeStyle = brush.color; + this.drawingCtx.fillStyle = brush.color; + this.drawingCtx.lineWidth = this.brushSize * 2; + this.positionBuffer.addElements(currentPos); + this.drawingCtx.beginPath() + + if(this.positionBuffer.currentLength > 3) + { + let multiplier = 12 + let tension = 0.5 * multiplier + const pointList = [...getRangeVector2FromBuffer(this.positionBuffer, this.positionBuffer.getLength() - 3, this.positionBuffer.getLength() )] + const floats = pointList.map(x => x.map( x => parseFloat(x))) + this.drawingCtx.moveTo(floats[0][0], floats[0][1]) + const matrixPoints = floats.map((point, i, arr) => { + if(i == 0) { + return getMatrix([arr[i], arr[i], arr[i+1], arr[i+2]]) + } else if(i === arr.length - 2) { + return getMatrix([arr[i - 1], arr[i], arr[i+1], arr[i+1]]) + } else { + return getMatrix([arr[i-1], arr[i], arr[i+1], arr[i+2]]) + } + }).filter(mx => mx[3] !== undefined) + + const matrixMathToBezier = matrixPoints.map( p => { + return [ + { x: p[1].x, y: p[1].y }, + { x: (-p[0].x + tension * p[1].x + p[2].x) / tension, y: (-p[0].y + tension * p[1].y + p[2].y) / tension }, + { x: (p[1].x + tension * p[2].x - p[3].x) / tension, y: (p[1].y + tension * p[2].y - p[3].y) / tension }, + { x: p[2].x, y: p[2].y } + ] + }) + for(let i = 0; i < matrixMathToBezier.length; i ++) { + let bp = matrixMathToBezier[i] + this.drawingCtx.bezierCurveTo(bp[1].x, bp[1].y, bp[2].x, bp[2].y, bp[3].x, bp[3].y) + } + + this.positionBuffer.setElements(Object.values(matrixMathToBezier[0][1]), this.positionBuffer.getLength() - 3) + this.positionBuffer.setElements(Object.values(matrixMathToBezier[0][2]), this.positionBuffer.getLength() - 2) + this.positionBuffer.setElements(Object.values(matrixMathToBezier[0][2]), this.positionBuffer.getLength() - 1) + + } else { + this.drawingCtx.moveTo(floats[0][0], floats[0][1]) + } + this.drawingCtx.stroke(); + + } +} + +export default CurveBrush; \ No newline at end of file diff --git a/src/js/shot-generator/components/Three/Helpers/Brushes/EraserBrush.js b/src/js/shot-generator/components/Three/Helpers/Brushes/EraserBrush.js index 99463b7024..d6ef31e379 100644 --- a/src/js/shot-generator/components/Three/Helpers/Brushes/EraserBrush.js +++ b/src/js/shot-generator/components/Three/Helpers/Brushes/EraserBrush.js @@ -39,7 +39,7 @@ class EraserBrush extends Brush { } this.drawingCtx.stroke(); this.drawingCtx.fill(circle); - this.positionBuffer.addElements(currentPos.x, currentPos.y); + this.positionBuffer.addElements(currentPos); } } diff --git a/src/js/shot-generator/components/Three/Helpers/Brushes/PointBuffer.js b/src/js/shot-generator/components/Three/Helpers/Brushes/PointBuffer.js index 2a20f95097..1097b9806d 100644 --- a/src/js/shot-generator/components/Three/Helpers/Brushes/PointBuffer.js +++ b/src/js/shot-generator/components/Three/Helpers/Brushes/PointBuffer.js @@ -1,3 +1,7 @@ +const parseElements = (elements) => { + return elements.flatMap(p => Object.values(p).map(s => s)) +} + class PointBuffer { constructor(pointsAmount) { this.defaultBufferSize = 20; @@ -6,7 +10,8 @@ class PointBuffer { } addElements(...elements) { - let predictedLength = this.currentLength * this.pointsAmount + elements.length; + let parsedElements = parseElements(elements) + let predictedLength = this.currentLength * this.pointsAmount + parsedElements.length; if(this.buffer.length < predictedLength) { let newLength = this.buffer.length * 2; @@ -17,8 +22,14 @@ class PointBuffer { resizedBuffer.set(this.buffer); this.buffer = resizedBuffer; } - this.buffer.set(elements, this.currentLength * this.pointsAmount); - this.currentLength += elements.length / this.pointsAmount; + this.buffer.set(parsedElements, this.currentLength * this.pointsAmount); + this.currentLength += parsedElements.length / this.pointsAmount; + } + + setElements(elements, index) { + let parsedElements = parseElements(elements) + if(index + parsedElements.length > this.currentLength) return + this.buffer.set(parsedElements, index); } getElements(index) { diff --git a/src/js/shot-generator/components/Three/Helpers/Brushes/SimpleBrush.js b/src/js/shot-generator/components/Three/Helpers/Brushes/SimpleBrush.js index ba21bb9e27..9f1188585e 100644 --- a/src/js/shot-generator/components/Three/Helpers/Brushes/SimpleBrush.js +++ b/src/js/shot-generator/components/Three/Helpers/Brushes/SimpleBrush.js @@ -48,7 +48,7 @@ class SimpleBrush extends Brush { prevPos = getVector2FromBuffer(this.positionBuffer, this.positionBuffer.currentLength - 1); } if(this.positionBuffer.currentLength === 0) { - this.positionBuffer.addElements(currentPos.x, currentPos.y); + this.positionBuffer.addElements(currentPos); return; } this.drawingCtx.moveTo(currentPos.x, currentPos.y); @@ -56,7 +56,7 @@ class SimpleBrush extends Brush { let distance = getDistanceTo(currentPos, prevPos); let angle = getAngleTo(currentPos, prevPos); let newPos = moveByAngle(angle, distance, prevPos); - this.positionBuffer.addElements(newPos.x, newPos.y); + this.positionBuffer.addElements(newPos); let p1 = getVector2FromBuffer(this.positionBuffer, 0); let p2 = getVector2FromBuffer(this.positionBuffer, 1); for(let i = 1, length = this.positionBuffer.currentLength; i < length; i++) { diff --git a/src/js/shot-generator/components/Three/helpers/DrawingTexture.js b/src/js/shot-generator/components/Three/helpers/DrawingTexture.js index e3c8cead47..a6c6c84ec5 100644 --- a/src/js/shot-generator/components/Three/helpers/DrawingTexture.js +++ b/src/js/shot-generator/components/Three/helpers/DrawingTexture.js @@ -1,7 +1,9 @@ import * as THREE from 'three' import SimpleBrush from './Brushes/SimpleBrush' +import CurveBrush from './Brushes/CurveBrush' import EraserBrush from './Brushes/EraserBrush' +import BrushType from '../Helpers/Brushes/TextureBrushTypes' class DrawingTexture { constructor() { this.drawingCanvases = []; @@ -24,10 +26,10 @@ class DrawingTexture { setMesh(type) { if(this.drawingBrush) this.drawingBrush.cleanUp() switch(type) { - case "Simple": - this.drawingBrush = new SimpleBrush(); + case BrushType.SIMPLE: + this.drawingBrush = new CurveBrush(); break; - case "Eraser": + case BrushType.ERASER: this.drawingBrush = new EraserBrush(); break; default: From 8f8d30a909b6b811d4af1b0fb0271c14a0d91974 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Tue, 29 Sep 2020 16:42:25 +0300 Subject: [PATCH 76/88] Fixed camera resets if moved on hotkeys after fov changes --- .../Three/CameraControlsComponet.js | 22 ++++++++----------- .../components/Three/InteractionManager.js | 2 -- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/js/shot-generator/components/Three/CameraControlsComponet.js b/src/js/shot-generator/components/Three/CameraControlsComponet.js index 6046fa2dbb..b3b01e37fe 100644 --- a/src/js/shot-generator/components/Three/CameraControlsComponet.js +++ b/src/js/shot-generator/components/Three/CameraControlsComponet.js @@ -7,7 +7,8 @@ import { undoGroupStart, undoGroupEnd, - + getSceneObjects, + getActiveCamera, getSelections, } from '../../../shared/reducers/shot-generator' import isUserModel from '../../helpers/isUserModel' @@ -15,7 +16,8 @@ import CameraControls from '../../CameraControls' const CameraControlComponent = connect( state => ({ - selections: getSelections(state) + selections: getSelections(state), + activeCamera: getSceneObjects(state)[getActiveCamera(state)], }), { updateObject, @@ -27,7 +29,6 @@ const CameraControlComponent = connect( pointerUpEvent, activeGL, isCameraControlsEnabled, - takeSceneObjects, updateObject, selections, @@ -36,9 +37,8 @@ const CameraControlComponent = connect( const { scene, camera } = useThree() const cameraControlsView = useRef() - const setCameraControlTarget = (selections) => { - if(selections.length === 1 && selections[0] === activeCamera) return + if(selections.length === 1 && selections[0] === activeCamera.id) return let selectedObjects = scene.__interaction.filter(object => object.userData.type !== 'camera' && object.userData.type !== 'volume' && selections.includes(object.userData.id) ) if(!selectedObjects.length) { @@ -97,16 +97,13 @@ const CameraControlComponent = connect( } useEffect(() => { - if(!activeCamera) return + if(!activeCamera.id) return if(cameraControlsView.current) { - let sceneObjects = takeSceneObjects() - cameraControlsView.current.object = CameraControls.objectFromCameraState(sceneObjects[activeCamera]) + cameraControlsView.current.object = CameraControls.objectFromCameraState(activeCamera) return } - let sceneObjects = takeSceneObjects() - cameraControlsView.current = new CameraControls( - CameraControls.objectFromCameraState(sceneObjects[activeCamera]), + CameraControls.objectFromCameraState(activeCamera), activeGL.domElement, { undoGroupStart, @@ -119,8 +116,7 @@ const CameraControlComponent = connect( useEffect(() => { if(!pointerDownEvent) return - let sceneObjects = takeSceneObjects() - cameraControlsView.current.object = CameraControls.objectFromCameraState(sceneObjects[activeCamera]) + cameraControlsView.current.object = CameraControls.objectFromCameraState(activeCamera) cameraControlsView.current.onPointerDown(pointerDownEvent) }, [pointerDownEvent]) diff --git a/src/js/shot-generator/components/Three/InteractionManager.js b/src/js/shot-generator/components/Three/InteractionManager.js index 7a2a754408..333dcad7e0 100644 --- a/src/js/shot-generator/components/Three/InteractionManager.js +++ b/src/js/shot-generator/components/Three/InteractionManager.js @@ -446,8 +446,6 @@ const InteractionManager = connect( pointerUpEvent={ pointerUpEvent } activeGL={ activeGL } isCameraControlsEnabled={ isCameraControlsEnabled } - takeSceneObjects={ takeSceneObjects } - activeCamera={ activeCamera } /> })) From 569079f91bf5f94398e883e4890c29672137104a Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Wed, 30 Sep 2020 13:21:28 +0300 Subject: [PATCH 77/88] Cleaned up sceneBackground's save function --- .../components/Three/SceneBackground.js | 15 +++++---------- .../shot-generator/helpers/saveDataURLtoFile.js | 4 ++-- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/js/shot-generator/components/Three/SceneBackground.js b/src/js/shot-generator/components/Three/SceneBackground.js index a95fb0a87e..9c7f702c16 100644 --- a/src/js/shot-generator/components/Three/SceneBackground.js +++ b/src/js/shot-generator/components/Three/SceneBackground.js @@ -61,18 +61,13 @@ const SceneBackground = React.memo(({ imagePath, world, storyboarderFilePath, up const save = () => { cleanUpTempFile() let tempFileName = `temp_scenetexture-${Date.now()}.jpg` + let dataUrl if(world.textureType === DrawingTextureType.Cubemap) { - let dataUrl = cubeTextureCreator.current.combineImages(scene.background) - let {dir, ext, name} = path.parse(imagePath[0]); - let properName = tempFileName ? tempFileName : name + ext; - saveDataURLtoFile(dataUrl, `${properName}`, 'models/sceneTextures', storyboarderFilePath); - } else if(world.textureType === DrawingTextureType.Simple) { - let imageData = drawingTextures.getTextureById(id.current).texture.getImage('image/jpg').replace(/^data:image\/\w+;base64,/, '') - let dirpath = path.join(path.dirname(storyboarderFilePath), 'models', 'sceneTextures') - let imageFilePath = path.join(dirpath, tempFileName) - fs.ensureDirSync(dirpath) - fs.writeFileSync(imageFilePath, imageData, 'base64') + dataUrl = cubeTextureCreator.current.combineImages(scene.background) + } else { + dataUrl = drawingTextures.getTextureById(id.current).texture.getImage('image/jpg') } + saveDataURLtoFile(dataUrl, tempFileName, path.join('models', 'sceneTextures'), storyboarderFilePath); updateWorld({sceneTexture: path.join('models', 'sceneTextures', tempFileName) }) scene.userData.tempPath = tempFileName texturePath.current = tempFileName diff --git a/src/js/shot-generator/helpers/saveDataURLtoFile.js b/src/js/shot-generator/helpers/saveDataURLtoFile.js index 844e9b6877..1fb2a6ce18 100644 --- a/src/js/shot-generator/helpers/saveDataURLtoFile.js +++ b/src/js/shot-generator/helpers/saveDataURLtoFile.js @@ -14,12 +14,12 @@ const saveDataURLtoFile = (dataURL, filename, type, boardPath, async = false) => const saveDataURLtoTempFile = (dataURL, boardPath, updateObject, object) => { let imageData = dataURL.replace(/^data:image\/\w+;base64,/, '') if(object.userData.tempImagePath) { - let tempImageFilePath = path.join(path.dirname(boardPath), 'models/images', object.userData.tempImagePath) + let tempImageFilePath = path.join(path.dirname(boardPath), 'models', 'images', object.userData.tempImagePath) fs.remove(tempImageFilePath) } let tempFilename = `temp_${object.userData.id}-${Date.now()}-texture.png` object.userData.tempImagePath = tempFilename - let filePath = path.join(path.dirname(boardPath), 'models/images') + let filePath = path.join(path.dirname(boardPath), 'models','images') let imageFilePath = path.join(filePath, tempFilename) fs.ensureDirSync(filePath) fs.writeFileSync(imageFilePath, imageData, 'base64') From 495030205f99a5dfe764bcde55b41c4cb236383d Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Wed, 30 Sep 2020 15:43:41 +0300 Subject: [PATCH 78/88] Fixed image cached when background type changed --- .../shot-generator/components/InspectedWorld/index.js | 11 +++++++---- .../components/Three/SceneBackground.js | 8 +++++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/js/shot-generator/components/InspectedWorld/index.js b/src/js/shot-generator/components/InspectedWorld/index.js index 6d931f881c..0ef35557ff 100644 --- a/src/js/shot-generator/components/InspectedWorld/index.js +++ b/src/js/shot-generator/components/InspectedWorld/index.js @@ -24,6 +24,7 @@ import { import deepEqualSelector from './../../../utils/deepEqualSelector' import CopyFile from '../../utils/CopyFile' import DrawingTextureType from './DrawingTextureType' +import ModelLoader from '../../../services/model-loader' const imageFilters = ["jpg", "jpeg", "png", "gif", "dds"] const InspectedWorld = React.memo(({updateObject, updateWorld, updateWorldRoom, updateWorldEnvironment, updateWorldFog, world, storyboarderFilePath}) => { @@ -52,19 +53,21 @@ const InspectedWorld = React.memo(({updateObject, updateWorld, updateWorldRoom, const setWorldTexture = useCallback((type, event) => { if (event.file) { - updateWorld({textureType: type, sceneTexture: CopyFile(storyboarderFilePath, event.file, 'sceneTexture')}) + if(!world.sceneTexture || path.basename(world.sceneTexture) !== path.basename(event.file)) { + updateWorld({textureType: type, sceneTexture: CopyFile(storyboarderFilePath, event.file, 'sceneTexture')}) + } } else { updateWorld({textureType:null, sceneTexture: null}) } - }, []) + }, [world]) const setSceneTextureFile = useCallback((event) => { setWorldTexture(DrawingTextureType.Simple, event) - }, []) + }, [setWorldTexture]) const setSceneCubeMap = useCallback((event) => { setWorldTexture(DrawingTextureType.Cubemap, event) - }, []) + }, [setWorldTexture]) const setGrayscale = useCallback(() => updateWorldEnvironment({grayscale: !world.environment.grayscale}), [world.environment.grayscale]) diff --git a/src/js/shot-generator/components/Three/SceneBackground.js b/src/js/shot-generator/components/Three/SceneBackground.js index 9c7f702c16..ac0f00a18a 100644 --- a/src/js/shot-generator/components/Three/SceneBackground.js +++ b/src/js/shot-generator/components/Three/SceneBackground.js @@ -1,7 +1,7 @@ import * as THREE from 'three' import React, { useEffect, useRef } from 'react' import { useThree } from 'react-three-fiber' -import { useAsset } from '../../hooks/use-assets-manager' +import { useAsset, removeAsset } from '../../hooks/use-assets-manager' import CubeTextureCreator from './helpers/CubeTextureCreator' import fs from 'fs-extra' import path from 'path' @@ -9,7 +9,6 @@ import DrawingTextureType from '../InspectedWorld/DrawingTextureType' import { saveDataURLtoFile } from '../../helpers/saveDataURLtoFile' import { TextureObjectType } from './Helpers/DrawingTextureContainer' const SceneBackground = React.memo(({ imagePath, world, storyboarderFilePath, updateWorld, drawingTextures }) => { - const texturePath = useRef() const { scene, camera, gl } = useThree() const { asset: texture } = useAsset( !imagePath[0] || !scene.userData.tempPath ? imagePath[0] : imagePath[0].includes(scene.userData.tempPath ) ? null : imagePath[0]) const intersectionBox = useRef() @@ -29,11 +28,15 @@ const SceneBackground = React.memo(({ imagePath, world, storyboarderFilePath, up useEffect(() => { if(!imagePath[0]) { if(scene.background instanceof THREE.Texture) { + console.log("clean up background") scene.background.dispose() scene.background = new THREE.Color(world.backgroundColor) cleanUpTempFile() } } + return () => { + removeAsset(imagePath[0]) + } }, [imagePath[0]]) @@ -70,7 +73,6 @@ const SceneBackground = React.memo(({ imagePath, world, storyboarderFilePath, up saveDataURLtoFile(dataUrl, tempFileName, path.join('models', 'sceneTextures'), storyboarderFilePath); updateWorld({sceneTexture: path.join('models', 'sceneTextures', tempFileName) }) scene.userData.tempPath = tempFileName - texturePath.current = tempFileName } From 2621d84d9dbbd3458432384eeb4e6828b8db0cd9 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Wed, 30 Sep 2020 15:44:38 +0300 Subject: [PATCH 79/88] Removed console log --- src/js/shot-generator/components/Three/SceneBackground.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/js/shot-generator/components/Three/SceneBackground.js b/src/js/shot-generator/components/Three/SceneBackground.js index ac0f00a18a..f33b1ad510 100644 --- a/src/js/shot-generator/components/Three/SceneBackground.js +++ b/src/js/shot-generator/components/Three/SceneBackground.js @@ -28,7 +28,6 @@ const SceneBackground = React.memo(({ imagePath, world, storyboarderFilePath, up useEffect(() => { if(!imagePath[0]) { if(scene.background instanceof THREE.Texture) { - console.log("clean up background") scene.background.dispose() scene.background = new THREE.Color(world.backgroundColor) cleanUpTempFile() From 653a0a602979a98a9215e800e06bfca688e03628 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Thu, 1 Oct 2020 15:33:53 +0300 Subject: [PATCH 80/88] Implemented drawing only on unobstructed images --- .../components/Three/Helpers/Brushes/Brush.js | 1 + .../Three/Helpers/Brushes/CurveBrush.js | 2 +- .../components/Three/Helpers/SimpleTexture.js | 97 ++++++++++++------- .../Three/helpers/DrawingTexture.js | 12 ++- .../shot-generator/hooks/use-draw-on-image.js | 32 ++++-- 5 files changed, 97 insertions(+), 47 deletions(-) diff --git a/src/js/shot-generator/components/Three/Helpers/Brushes/Brush.js b/src/js/shot-generator/components/Three/Helpers/Brushes/Brush.js index be481a0b01..aa3f227f93 100644 --- a/src/js/shot-generator/components/Three/Helpers/Brushes/Brush.js +++ b/src/js/shot-generator/components/Three/Helpers/Brushes/Brush.js @@ -18,6 +18,7 @@ class Brush { stopDrawing() { this.isDrawing = false; this.positionBuffer.flushArray(); + this.drawingCtx && this.drawingCtx.closePath(); } set DrawingContext(value) { diff --git a/src/js/shot-generator/components/Three/Helpers/Brushes/CurveBrush.js b/src/js/shot-generator/components/Three/Helpers/Brushes/CurveBrush.js index ccb4754009..c1af8df4ed 100644 --- a/src/js/shot-generator/components/Three/Helpers/Brushes/CurveBrush.js +++ b/src/js/shot-generator/components/Three/Helpers/Brushes/CurveBrush.js @@ -70,7 +70,7 @@ class CurveBrush extends Brush { this.positionBuffer.setElements(Object.values(matrixMathToBezier[0][2]), this.positionBuffer.getLength() - 1) } else { - this.drawingCtx.moveTo(floats[0][0], floats[0][1]) + this.drawingCtx.moveTo(currentPos.x, currentPos.y) } this.drawingCtx.stroke(); diff --git a/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js b/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js index 16cb2ada3c..52829751ce 100644 --- a/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js +++ b/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js @@ -4,6 +4,27 @@ import fromWorldSpaceToClipSpace from "../../../utils/WorldSpaceToClipSpace" import fromClipSpaceToWorldSpace from "../../../utils/ClipSpaceToWorldSpace" import mouse from '../../../utils/mouseToClipSpace' +const findIntersection = (raycaster, camera, prevPos, currentPos, currentObject, objects, limit = 0.0001) => { + let mediumPos = { + x: prevPos.x + (currentPos.x - prevPos.x) / 2, + y: prevPos.y + (currentPos.y - prevPos.y) / 2, + } + raycaster.setFromCamera(mediumPos, camera); + let intersections = raycaster.intersectObjects(objects, true); + + let distance = Math.sqrt(Math.pow(currentPos.x - prevPos.x, 2) + Math.pow(currentPos.y - prevPos.y, 2)) + if(intersections.length && intersections[0].object.parent === currentObject) { + let result = null + //Check if there further intersections before exiting + if( distance > limit ) { + result = findIntersection(raycaster, camera, mediumPos, currentPos, currentObject, objects) + } + return result ? result : intersections[0] + } else { + return distance > limit ? findIntersection(raycaster, camera, prevPos, mediumPos, currentObject, objects) : null + } +} + class SimpleTexture extends DrawingTexture { constructor() { super(); @@ -54,52 +75,58 @@ class SimpleTexture extends DrawingTexture { this.material.needsUpdate = true; } - draw (mousePosition, object, camera, brush, gl) { - let intersection = super.draw(mousePosition, object, camera); + draw (mousePosition, object, camera, brush, gl, objects, onlyContinuousDrawing) { + let intersection = super.draw(mousePosition, object, camera, onlyContinuousDrawing); // If we don't have a uv coordinates and as a last resort we trying to translate mouse into object coordinate // From object coordinate we can sort of simulate uv coordinate logic for plain object // NOTE() : This won't work for any object except plain object( image object ) - + console.log("this", this.prevMousePosition, intersection, this.isChanged) if(!intersection) { - if(!this.isChanged) return; - let worldPos = fromClipSpaceToWorldSpace(mousePosition, camera, object.position.z); - intersection = {uv: {}}; + if(!this.isChanged || !this.prevMousePosition ) return; + console.log("prev position", this.prevMousePosition) + console.log("current position", mousePosition) + intersection = findIntersection(this.raycaster, camera, this.prevMousePosition, mousePosition, object, objects); //#region Clip space to world space method - let scale = object.scale.clone(); - scale.z = 0; - scale.y = -scale.y; - let quaternion = object.worldQuaternion(); - scale.applyQuaternion(quaternion); - scale.divideScalar(2); - let position = object.worldPosition(); - let topPosition = position.clone().sub(scale); - let bottomPosition = position.clone().add(scale); - let top = fromWorldSpaceToClipSpace(topPosition, camera, gl); - let bottom = fromWorldSpaceToClipSpace(bottomPosition, camera, gl); - let topMouse = mouse(top, gl, false); - let bottomMouse = mouse(bottom, gl, false); - let worldTop = fromClipSpaceToWorldSpace(topMouse, camera, object.position.z); - let worldBottom = fromClipSpaceToWorldSpace(bottomMouse, camera, object.position.z); - if(worldBottom.x > worldTop.x && worldBottom.y < worldTop.y) { - worldBottom.sub(worldTop); - worldPos.sub(worldTop); - worldPos.divide(worldBottom); - intersection.uv.x = worldPos.x; - intersection.uv.y = 1 - worldPos.y; - } else { - worldTop.sub(worldBottom); - worldPos.sub(worldBottom); - worldPos.divide(worldTop); - intersection.uv.x = 1 - worldPos.x; - intersection.uv.y = worldPos.y; + if(!intersection) { + let worldPos = fromClipSpaceToWorldSpace(mousePosition, camera, object.position.z); + intersection = {uv: {}}; + let scale = object.scale.clone(); + scale.z = 0; + scale.y = -scale.y; + let quaternion = object.worldQuaternion(); + scale.applyQuaternion(quaternion); + scale.divideScalar(2); + let position = object.worldPosition(); + let topPosition = position.clone().sub(scale); + let bottomPosition = position.clone().add(scale); + let top = fromWorldSpaceToClipSpace(topPosition, camera, gl); + let bottom = fromWorldSpaceToClipSpace(bottomPosition, camera, gl); + let topMouse = mouse(top, gl, false); + let bottomMouse = mouse(bottom, gl, false); + let worldTop = fromClipSpaceToWorldSpace(topMouse, camera, object.position.z); + let worldBottom = fromClipSpaceToWorldSpace(bottomMouse, camera, object.position.z); + if(worldBottom.x > worldTop.x && worldBottom.y < worldTop.y) { + worldBottom.sub(worldTop); + worldPos.sub(worldTop); + worldPos.divide(worldBottom); + intersection.uv.x = worldPos.x; + intersection.uv.y = 1 - worldPos.y; + } else { + worldTop.sub(worldBottom); + worldPos.sub(worldBottom); + worldPos.divide(worldTop); + intersection.uv.x = 1 - worldPos.x; + intersection.uv.y = worldPos.y; + } } //#endregion } if(Number.isNaN(intersection.uv.x) || Number.isNaN(intersection.uv.y)) return; let screenX = (intersection.uv.x) * this.texture.image.width; let screenY = (1 - intersection.uv.y) * this.texture.image.height; - this.drawingBrush.draw({ x: screenX, y: screenY }, brush); - + console.log(this.texture.image.width, this.drawingCtxes, this.drawingCanvases) + this.drawingBrush.draw({ x: Math.round(screenX), y: Math.round(screenY) }, brush); + console.log("Last elements", this.drawingBrush.positionBuffer.getElements(this.drawingBrush.positionBuffer.getLength() -1)) this.texture.needsUpdate = true; } } diff --git a/src/js/shot-generator/components/Three/helpers/DrawingTexture.js b/src/js/shot-generator/components/Three/helpers/DrawingTexture.js index a6c6c84ec5..de87725409 100644 --- a/src/js/shot-generator/components/Three/helpers/DrawingTexture.js +++ b/src/js/shot-generator/components/Three/helpers/DrawingTexture.js @@ -13,6 +13,7 @@ class DrawingTexture { this.drawingBrush = null; this.isChanged = false; this.setMesh(); + this.prevMousePosition; } prepareToDraw() { @@ -21,6 +22,7 @@ class DrawingTexture { endDraw() { this.drawingBrush.stopDrawing(); + this.isChanged = false; } setMesh(type) { @@ -33,7 +35,7 @@ class DrawingTexture { this.drawingBrush = new EraserBrush(); break; default: - this.drawingBrush = new SimpleBrush(); + this.drawingBrush = new CurveBrush(); } } @@ -72,13 +74,15 @@ class DrawingTexture { return intersects.length && intersects[0]; } - draw (mousePosition, object, camera) { + draw (mousePosition, object, camera, onlyContinuousDrawing) { - let intersection = this.intersectImage(mousePosition.x, mousePosition.y, object, camera); - if(intersection.uv === null) { + let intersection = onlyContinuousDrawing ? false : this.intersectImage(mousePosition.x, mousePosition.y, object, camera); + if(!intersection || intersection.uv === null) { return; } this.isChanged = true; + this.isNeedSaving = true; + this.prevMousePosition = mousePosition; return intersection; } diff --git a/src/js/shot-generator/hooks/use-draw-on-image.js b/src/js/shot-generator/hooks/use-draw-on-image.js index e06d93d12e..25ece36950 100644 --- a/src/js/shot-generator/hooks/use-draw-on-image.js +++ b/src/js/shot-generator/hooks/use-draw-on-image.js @@ -55,18 +55,36 @@ const useDrawOnImage = (drawMode) => { let values = Object.values(getDrawingTextures().getTexturesByObjectType(TextureObjectType.Image)) let {x, y} = mouse({x: event.clientX, y: event.clientY}, gl) raycaster.current.setFromCamera({x, y}, camera) - let imageObjects = scene.__interaction.filter(object => object.userData.type === "image") + let imageObjects = scene.children[0].children.filter(object => object.visible === true)//.filter(object => object.userData.type !== "image") let intersections = raycaster.current.intersectObjects(imageObjects, true) let backgroundTexture = getDrawingTextures().getTexturesByObjectType(TextureObjectType.Background) - if(!intersections.length && backgroundTexture.length) { + if(backgroundTexture.length) { let texture = backgroundTexture[0] - texture.draw({x, y}, camera, drawMode.brush) + if(!intersections.length) { + texture.draw({x, y}, camera, drawMode.brush) + } else { + texture.texture.endDraw() + texture.texture.prepareToDraw() + } } for(let i = 0; i < values.length; i++) { let drawingTexture = values[i].texture let object = drawingTexture.material.parent.parent - if(!object || !object.visible) continue - values[i].draw({x, y}, object, camera, drawMode.brush, gl) + + // Hack to avoid drawing on image when there's object in front of them + let onlyContinuousDrawing = true + //console.log( intersections[0].object) + if(intersections.length && intersections[0].object.parent === object) { + onlyContinuousDrawing = false + } + + if(!object || !object.visible ) continue + values[i].draw({x, y}, object, camera, drawMode.brush, gl, imageObjects, onlyContinuousDrawing) + if(onlyContinuousDrawing) { + values[i].texture.endDraw() + values[i].texture.prepareToDraw() + } + } } @@ -77,9 +95,9 @@ const useDrawOnImage = (drawMode) => { let values = Object.values(getDrawingTextures().getTextures()) for(let i = 0; i < values.length; i++) { let texture = values[i].texture - if(texture.isChanged) { + if(texture.isNeedSaving) { texture.endDraw() - texture.isChanged = false + texture.isNeedSaving = false values[i].save() } } From 20d4da2762f5f0364667ea504c25231c63d86876 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Thu, 1 Oct 2020 15:48:09 +0300 Subject: [PATCH 81/88] Cleaned up --- .../components/Three/Helpers/Brushes/CurveBrush.js | 1 + .../components/Three/Helpers/SimpleTexture.js | 7 +------ .../components/Three/helpers/DrawingTexture.js | 1 - src/js/shot-generator/hooks/use-draw-on-image.js | 3 +-- 4 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/js/shot-generator/components/Three/Helpers/Brushes/CurveBrush.js b/src/js/shot-generator/components/Three/Helpers/Brushes/CurveBrush.js index c1af8df4ed..cde3fa18c0 100644 --- a/src/js/shot-generator/components/Three/Helpers/Brushes/CurveBrush.js +++ b/src/js/shot-generator/components/Three/Helpers/Brushes/CurveBrush.js @@ -32,6 +32,7 @@ class CurveBrush extends Brush { this.drawingCtx.strokeStyle = brush.color; this.drawingCtx.fillStyle = brush.color; this.drawingCtx.lineWidth = this.brushSize * 2; + this.drawingCtx.lineJoin = this.drawingCtx.lineCap = 'round'; this.positionBuffer.addElements(currentPos); this.drawingCtx.beginPath() diff --git a/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js b/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js index 52829751ce..026b9c5789 100644 --- a/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js +++ b/src/js/shot-generator/components/Three/Helpers/SimpleTexture.js @@ -4,7 +4,7 @@ import fromWorldSpaceToClipSpace from "../../../utils/WorldSpaceToClipSpace" import fromClipSpaceToWorldSpace from "../../../utils/ClipSpaceToWorldSpace" import mouse from '../../../utils/mouseToClipSpace' -const findIntersection = (raycaster, camera, prevPos, currentPos, currentObject, objects, limit = 0.0001) => { +const findIntersection = (raycaster, camera, prevPos, currentPos, currentObject, objects, limit = 0.005) => { let mediumPos = { x: prevPos.x + (currentPos.x - prevPos.x) / 2, y: prevPos.y + (currentPos.y - prevPos.y) / 2, @@ -80,11 +80,8 @@ class SimpleTexture extends DrawingTexture { // If we don't have a uv coordinates and as a last resort we trying to translate mouse into object coordinate // From object coordinate we can sort of simulate uv coordinate logic for plain object // NOTE() : This won't work for any object except plain object( image object ) - console.log("this", this.prevMousePosition, intersection, this.isChanged) if(!intersection) { if(!this.isChanged || !this.prevMousePosition ) return; - console.log("prev position", this.prevMousePosition) - console.log("current position", mousePosition) intersection = findIntersection(this.raycaster, camera, this.prevMousePosition, mousePosition, object, objects); //#region Clip space to world space method if(!intersection) { @@ -124,9 +121,7 @@ class SimpleTexture extends DrawingTexture { if(Number.isNaN(intersection.uv.x) || Number.isNaN(intersection.uv.y)) return; let screenX = (intersection.uv.x) * this.texture.image.width; let screenY = (1 - intersection.uv.y) * this.texture.image.height; - console.log(this.texture.image.width, this.drawingCtxes, this.drawingCanvases) this.drawingBrush.draw({ x: Math.round(screenX), y: Math.round(screenY) }, brush); - console.log("Last elements", this.drawingBrush.positionBuffer.getElements(this.drawingBrush.positionBuffer.getLength() -1)) this.texture.needsUpdate = true; } } diff --git a/src/js/shot-generator/components/Three/helpers/DrawingTexture.js b/src/js/shot-generator/components/Three/helpers/DrawingTexture.js index de87725409..80b985dff6 100644 --- a/src/js/shot-generator/components/Three/helpers/DrawingTexture.js +++ b/src/js/shot-generator/components/Three/helpers/DrawingTexture.js @@ -1,6 +1,5 @@ import * as THREE from 'three' -import SimpleBrush from './Brushes/SimpleBrush' import CurveBrush from './Brushes/CurveBrush' import EraserBrush from './Brushes/EraserBrush' import BrushType from '../Helpers/Brushes/TextureBrushTypes' diff --git a/src/js/shot-generator/hooks/use-draw-on-image.js b/src/js/shot-generator/hooks/use-draw-on-image.js index 25ece36950..e714951cee 100644 --- a/src/js/shot-generator/hooks/use-draw-on-image.js +++ b/src/js/shot-generator/hooks/use-draw-on-image.js @@ -55,7 +55,7 @@ const useDrawOnImage = (drawMode) => { let values = Object.values(getDrawingTextures().getTexturesByObjectType(TextureObjectType.Image)) let {x, y} = mouse({x: event.clientX, y: event.clientY}, gl) raycaster.current.setFromCamera({x, y}, camera) - let imageObjects = scene.children[0].children.filter(object => object.visible === true)//.filter(object => object.userData.type !== "image") + let imageObjects = scene.children[0].children.filter(object => object.visible === true) let intersections = raycaster.current.intersectObjects(imageObjects, true) let backgroundTexture = getDrawingTextures().getTexturesByObjectType(TextureObjectType.Background) if(backgroundTexture.length) { @@ -73,7 +73,6 @@ const useDrawOnImage = (drawMode) => { // Hack to avoid drawing on image when there's object in front of them let onlyContinuousDrawing = true - //console.log( intersections[0].object) if(intersections.length && intersections[0].object.parent === object) { onlyContinuousDrawing = false } From 363ca8d446d68a06739a0b699f6aa1e5bed81326 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Fri, 2 Oct 2020 12:02:26 +0300 Subject: [PATCH 82/88] Fixed cannot draw on image if volume is in front --- src/js/shot-generator/hooks/use-draw-on-image.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/shot-generator/hooks/use-draw-on-image.js b/src/js/shot-generator/hooks/use-draw-on-image.js index e714951cee..fbae7634c7 100644 --- a/src/js/shot-generator/hooks/use-draw-on-image.js +++ b/src/js/shot-generator/hooks/use-draw-on-image.js @@ -55,7 +55,7 @@ const useDrawOnImage = (drawMode) => { let values = Object.values(getDrawingTextures().getTexturesByObjectType(TextureObjectType.Image)) let {x, y} = mouse({x: event.clientX, y: event.clientY}, gl) raycaster.current.setFromCamera({x, y}, camera) - let imageObjects = scene.children[0].children.filter(object => object.visible === true) + let imageObjects = scene.children[0].children.filter(object => object.visible === true && object.userData.type !== "volume" && object.userData.type !== "character") let intersections = raycaster.current.intersectObjects(imageObjects, true) let backgroundTexture = getDrawingTextures().getTexturesByObjectType(TextureObjectType.Background) if(backgroundTexture.length) { From 416a0672e3e3958e372e5283f0b9eb8d776562a0 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Fri, 2 Oct 2020 14:24:31 +0300 Subject: [PATCH 83/88] Implemented not supported cubemap notification --- .../shot-generator/components/Editor/index.js | 2 +- .../components/ElementsPanel/Inspector.js | 5 ++-- .../components/ElementsPanel/index.js | 6 ++-- .../components/InspectedElement/index.js | 6 ++-- .../components/InspectedWorld/index.js | 28 ++++++++++++++++--- .../components/Three/SceneBackground.js | 4 ++- .../Three/helpers/CubeTextureCreator.js | 11 ++++---- src/js/xr/src/SceneManagerXR.js | 2 +- 8 files changed, 45 insertions(+), 19 deletions(-) diff --git a/src/js/shot-generator/components/Editor/index.js b/src/js/shot-generator/components/Editor/index.js index 6023f76a2c..42750ca1eb 100644 --- a/src/js/shot-generator/components/Editor/index.js +++ b/src/js/shot-generator/components/Editor/index.js @@ -290,7 +290,7 @@ const Editor = React.memo(({
- +
diff --git a/src/js/shot-generator/components/ElementsPanel/Inspector.js b/src/js/shot-generator/components/ElementsPanel/Inspector.js index 258ffe8925..b99d5ec75d 100644 --- a/src/js/shot-generator/components/ElementsPanel/Inspector.js +++ b/src/js/shot-generator/components/ElementsPanel/Inspector.js @@ -5,7 +5,8 @@ import MultiSelectionInspector from '../MultiSelectionInspector' const Inspector = ({ kind, data, - selections + selections, + notifications }) => { let sceneObject = data let isGroup = sceneObject && sceneObject.type === "group" @@ -14,7 +15,7 @@ const Inspector = ({ return
{(selectedCount > 1) ? - : + : }
} diff --git a/src/js/shot-generator/components/ElementsPanel/index.js b/src/js/shot-generator/components/ElementsPanel/index.js index 3614d7a42a..ec549c9bc5 100644 --- a/src/js/shot-generator/components/ElementsPanel/index.js +++ b/src/js/shot-generator/components/ElementsPanel/index.js @@ -58,7 +58,7 @@ const ElementsPanel = connect( undoGroupEnd } )( - React.memo(({ world, sceneObjects, models, selections, selectObject, selectObjectToggle, updateObject, deleteObjects, selectedBone, activeCamera, setActiveCamera, selectBone, updateCharacterSkeleton, updateWorld, updateWorldRoom, updateWorldEnvironment, updateWorldFog, storyboarderFilePath }) => { + React.memo(({ world, sceneObjects, models, selections, selectObject, selectObjectToggle, updateObject, deleteObjects, selectedBone, activeCamera, setActiveCamera, selectBone, updateCharacterSkeleton, updateWorld, updateWorldRoom, updateWorldEnvironment, updateWorldFog, storyboarderFilePath, notifications}) => { let kind = sceneObjects[selections[0]] && sceneObjects[selections[0]].type let data = sceneObjects[selections[0]] @@ -87,8 +87,10 @@ const ElementsPanel = connect( storyboarderFilePath, - selections + selections, + }} + notifications={ notifications} />
) diff --git a/src/js/shot-generator/components/InspectedElement/index.js b/src/js/shot-generator/components/InspectedElement/index.js index 6c8cce37e4..af6bf91d30 100644 --- a/src/js/shot-generator/components/InspectedElement/index.js +++ b/src/js/shot-generator/components/InspectedElement/index.js @@ -26,7 +26,7 @@ const isObj = (type) => type === 'object' const isImage = (type) => type === 'image' || !type const nullTab = {tab: null, panel: null} -const Inspector = React.memo(({id, selectedName, selectedType, updateObject, isInspectedWorld}) => { +const Inspector = React.memo(({id, selectedName, selectedType, updateObject, isInspectedWorld, notifications}) => { const [isModalShown, showModal] = useState(false) const [changedName, changeNameTo] = useState(false) const handPoseTab = useMemo(() => { @@ -72,7 +72,7 @@ const Inspector = React.memo(({id, selectedName, selectedType, updateObject, isI panel: } }, [selectedType]) - + console.log("notifications", notifications) return ( { isModalShown && showModal(false)}> @@ -111,7 +111,7 @@ const Inspector = React.memo(({id, selectedName, selectedType, updateObject, isI
- {isInspectedWorld ? : } + {isInspectedWorld ? : } {handPoseTab.panel} {charPoseTab.panel} {modelTab.panel} diff --git a/src/js/shot-generator/components/InspectedWorld/index.js b/src/js/shot-generator/components/InspectedWorld/index.js index 0ef35557ff..4b023726d4 100644 --- a/src/js/shot-generator/components/InspectedWorld/index.js +++ b/src/js/shot-generator/components/InspectedWorld/index.js @@ -1,6 +1,6 @@ -import React, {useCallback, useMemo} from 'react' +import React, {useCallback, useMemo, useState} from 'react' import {Math as _Math} from 'three' - +import electron from 'electron' import {connect} from 'react-redux' import path from 'path' @@ -25,9 +25,11 @@ import deepEqualSelector from './../../../utils/deepEqualSelector' import CopyFile from '../../utils/CopyFile' import DrawingTextureType from './DrawingTextureType' import ModelLoader from '../../../services/model-loader' +import { isSupportedCubeMap } from '../Three/Helpers/CubeTextureCreator' +const cubemapWiki = 'https://github.com/wonderunit/storyboarder/wiki' const imageFilters = ["jpg", "jpeg", "png", "gif", "dds"] -const InspectedWorld = React.memo(({updateObject, updateWorld, updateWorldRoom, updateWorldEnvironment, updateWorldFog, world, storyboarderFilePath}) => { +const InspectedWorld = React.memo(({updateObject, updateWorld, updateWorldRoom, updateWorldEnvironment, updateWorldFog, world, storyboarderFilePath, notifications}) => { const setGround = useCallback(() => updateWorld({ground: !world.ground}), [world.ground]) const setRoomVisible = useCallback(() => updateWorldRoom({visible: !world.room.visible}), [world.room.visible]) const setEnvVisible = useCallback(() => updateWorldEnvironment({visible: !world.environment.visible}), [world.environment.visible]) @@ -66,7 +68,25 @@ const InspectedWorld = React.memo(({updateObject, updateWorld, updateWorldRoom, }, [setWorldTexture]) const setSceneCubeMap = useCallback((event) => { - setWorldTexture(DrawingTextureType.Cubemap, event) + if(!event.file) { + setWorldTexture(DrawingTextureType.Cubemap, event) + return + } + let image = new Image() + image.addEventListener('load', () => { + if(isSupportedCubeMap(image)) { + setWorldTexture(DrawingTextureType.Cubemap, event) + } else { + notifications.notify({ + message: + `Whoops cubemap format isn't supported check the` + + ` Supported formats for details.`, + timing: 30, + onClick: () => electron.shell.openExternal(cubemapWiki) + }) + } + }) + image.src = event.file }, [setWorldTexture]) const setGrayscale = useCallback(() => updateWorldEnvironment({grayscale: !world.environment.grayscale}), [world.environment.grayscale]) diff --git a/src/js/shot-generator/components/Three/SceneBackground.js b/src/js/shot-generator/components/Three/SceneBackground.js index f33b1ad510..fb42d504b4 100644 --- a/src/js/shot-generator/components/Three/SceneBackground.js +++ b/src/js/shot-generator/components/Three/SceneBackground.js @@ -2,7 +2,7 @@ import * as THREE from 'three' import React, { useEffect, useRef } from 'react' import { useThree } from 'react-three-fiber' import { useAsset, removeAsset } from '../../hooks/use-assets-manager' -import CubeTextureCreator from './helpers/CubeTextureCreator' +import { CubeTextureCreator } from './Helpers/CubeTextureCreator' import fs from 'fs-extra' import path from 'path' import DrawingTextureType from '../InspectedWorld/DrawingTextureType' @@ -116,9 +116,11 @@ const SceneBackground = React.memo(({ imagePath, world, storyboarderFilePath, up let textureObject = drawingTextures.getTextureById(id.current) if(world.textureType === DrawingTextureType.Cubemap) { backgroundTexture = cubeTextureCreator.current.getCubeMapTexture(texture, storyboarderFilePath); + // Checks if image is appropriate format if(backgroundTexture) { textureObject.save = () => save() } else { + //...removes it if doesn't updateWorld({ sceneTexture: null, textureType: null }) return } diff --git a/src/js/shot-generator/components/Three/helpers/CubeTextureCreator.js b/src/js/shot-generator/components/Three/helpers/CubeTextureCreator.js index 9d53314ec1..9b1fb66b56 100644 --- a/src/js/shot-generator/components/Three/helpers/CubeTextureCreator.js +++ b/src/js/shot-generator/components/Three/helpers/CubeTextureCreator.js @@ -1,6 +1,9 @@ import * as THREE from 'three' - -class CubeTextureCreator { +export const isSupportedCubeMap = ( image ) => { + return image.width / 4 === image.height / 3 || image.width / 3 === image.height / 4 + || image.width / 6 === image.height || image.width === image.height / 6 +} +export class CubeTextureCreator { constructor() { this.drawingCanvas = document.createElement('canvas'); this.croppedCanvas = document.createElement('canvas'); @@ -125,6 +128,4 @@ class CubeTextureCreator { let texture = new THREE.Texture(croppedCtx.canvas); return texture.image; } -} - -export default CubeTextureCreator; \ No newline at end of file +} \ No newline at end of file diff --git a/src/js/xr/src/SceneManagerXR.js b/src/js/xr/src/SceneManagerXR.js index 5f1324a461..fa667584ef 100644 --- a/src/js/xr/src/SceneManagerXR.js +++ b/src/js/xr/src/SceneManagerXR.js @@ -66,7 +66,7 @@ const Boards = require('./components/ui/Boards') const BonesHelper = require('./three/BonesHelper') const Voicer = require('./three/Voicer') -const CubeTextureCreator = require('../../shot-generator/components/Three/Helpers/CubeTextureCreator').default +const { CubeTextureCreator } = require('../../shot-generator/components/Three/Helpers/CubeTextureCreator').default const musicSystem = require('./music-system') From e77cc312be268b8f084f6a8abbc9444f1194c6c3 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Fri, 2 Oct 2020 14:35:15 +0300 Subject: [PATCH 84/88] Fixed xr cubeTextureCreator import --- src/js/xr/src/SceneManagerXR.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/xr/src/SceneManagerXR.js b/src/js/xr/src/SceneManagerXR.js index fa667584ef..5c46ef6c96 100644 --- a/src/js/xr/src/SceneManagerXR.js +++ b/src/js/xr/src/SceneManagerXR.js @@ -66,7 +66,7 @@ const Boards = require('./components/ui/Boards') const BonesHelper = require('./three/BonesHelper') const Voicer = require('./three/Voicer') -const { CubeTextureCreator } = require('../../shot-generator/components/Three/Helpers/CubeTextureCreator').default +const { CubeTextureCreator } = require('../../shot-generator/components/Three/Helpers/CubeTextureCreator') const musicSystem = require('./music-system') From c5e28069e7a664fb615f2a9f90f01fdd7c9c63f0 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Fri, 2 Oct 2020 15:26:23 +0300 Subject: [PATCH 85/88] Localized sg-image-drawing --- src/js/locales/en-US.json | 6 ++++-- src/js/locales/ru-RU.json | 5 ++++- src/js/shot-generator/components/InspectedWorld/index.js | 7 +++---- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/js/locales/en-US.json b/src/js/locales/en-US.json index 7ed22410b8..95d77c872b 100644 --- a/src/js/locales/en-US.json +++ b/src/js/locales/en-US.json @@ -402,8 +402,10 @@ "directional-light":"Directional light", "fog":"Fog", "grayscale": "Grayscale", - "shading-mode": "Shading Mode" - + "shading-mode": "Shading Mode", + "cubemap-not-supported": "Whoops cubemap format isn't supported check the {{link}} for details.", + "scene-cubemap":"Scene Cube map", + "scene-texture":"Scene Texture" }, "common":{ "object-creation-help":"How to Create 3D Models for Custom Objects", diff --git a/src/js/locales/ru-RU.json b/src/js/locales/ru-RU.json index 8472885b4a..dd2a33f785 100644 --- a/src/js/locales/ru-RU.json +++ b/src/js/locales/ru-RU.json @@ -402,7 +402,10 @@ "directional-light": "Направленое освещение", "fog": "Туман", "grayscale": "Оттенки серого", - "shading-mode": "Режим затенения" + "shading-mode": "Режим затенения", + "cubemap-not-supported": "Упс, формат кубической текстуры неподерживается перейдите в {{link}} чтобы узнать детали.", + "scene-cubemap":"Кубическая текстура", + "scene-texture":"Текстура сцены" }, "common": { "object-creation-help": "Как создавать 3D модели для Кастомных Объектов", diff --git a/src/js/shot-generator/components/InspectedWorld/index.js b/src/js/shot-generator/components/InspectedWorld/index.js index 9f896d1b17..d663545fbd 100644 --- a/src/js/shot-generator/components/InspectedWorld/index.js +++ b/src/js/shot-generator/components/InspectedWorld/index.js @@ -81,8 +81,7 @@ const InspectedWorld = React.memo(({updateObject, updateWorld, updateWorldRoom, } else { notifications.notify({ message: - `Whoops cubemap format isn't supported check the` + - ` Supported formats for details.`, + t("shot-generator.inspector.inspected-world.cubemap-not-supported", {link:`Supported formats `}), timing: 30, onClick: () => electron.shell.openExternal(cubemapWiki) }) @@ -151,7 +150,7 @@ const InspectedWorld = React.memo(({updateObject, updateWorld, updateWorldRoom,
} {(!world.textureType || world.textureType === DrawingTextureType.Cubemap) && Date: Mon, 12 Oct 2020 10:32:18 +0300 Subject: [PATCH 86/88] Localized the sg-image-drawing --- src/js/locales/en-US.json | 6 +++++- src/js/locales/ru-RU.json | 10 +++++++--- .../InspectedElement/BrushInspector/index.js | 11 ++++++----- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/js/locales/en-US.json b/src/js/locales/en-US.json index 95d77c872b..1b803f8b1b 100644 --- a/src/js/locales/en-US.json +++ b/src/js/locales/en-US.json @@ -436,7 +436,11 @@ "are-you-sure":"Are you sure?" }, "inspected-element":{ - "properties":"Properties:" + "properties":"Properties:", + "brush-color":"Brush color", + "type":"Type", + "simple": "Brush", + "eraser": "Eraser" }, "camera":{ "fov":"F.O.V." diff --git a/src/js/locales/ru-RU.json b/src/js/locales/ru-RU.json index dd2a33f785..0ca550135f 100644 --- a/src/js/locales/ru-RU.json +++ b/src/js/locales/ru-RU.json @@ -107,7 +107,7 @@ "pencil": "Карандаш", "pen": "Ручка", "note-pen": "Ручка для записи", - "eraser": "Стирачка", + "eraser": "Ластик", "clear-all-layers": "Очистить все слои", "clear-layer": "Очистить слой", "smaller-brush": "Маленькая кисть", @@ -167,7 +167,7 @@ "description": "Персмотрите доски с этим иснтрументом. Есть что записать? Рисуйте прям на доске. Этот инструмент рисует на слою Notes, sпоэтому вы может легко это очистить." }, "eraser": { - "title": "Страчка", + "title": "Ластик", "description": "Надо что-то стиреть? Используйте стирачку. Если зажата кнопка option (или alt), тогда стирачка будет чистить всё на выбраном слою. В противном случае будет очищать все слои." } }, @@ -436,7 +436,11 @@ "are-you-sure": "Вы уверены?" }, "inspected-element": { - "properties": "Свойства:" + "properties": "Свойства:", + "brush-color":"Цвет кисти", + "type":"Тип", + "simple": "Кисть", + "eraser": "Ластик" }, "camera": { "fov": "F.O.V." diff --git a/src/js/shot-generator/components/InspectedElement/BrushInspector/index.js b/src/js/shot-generator/components/InspectedElement/BrushInspector/index.js index 12cacc30cc..cfbd21fe09 100644 --- a/src/js/shot-generator/components/InspectedElement/BrushInspector/index.js +++ b/src/js/shot-generator/components/InspectedElement/BrushInspector/index.js @@ -8,6 +8,7 @@ import { } from '../../../../shared/reducers/shot-generator' import {NumberSlider, textConstraints} from '../../NumberSlider' import BrushType from '../../Three/Helpers/Brushes/TextureBrushTypes' +import { useTranslation } from 'react-i18next' const BrushInspector = connect((state) => ({ drawMode: getDrawMode(state), @@ -22,7 +23,7 @@ React.memo(({ drawMode, selections }) => { - + const { t } = useTranslation() useEffect(() => { updateDrawMode({isEnabled: true}) @@ -50,7 +51,7 @@ React.memo(({ return (
-
Type
+
{t("shot-generator.inspector.inspected-element.type")}
{drawMode.brush.type !== BrushType.ERASER && } { selections.length > 0 &&
From 2ac01c32b0419bc0bd903ffa728235814f716dd9 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Mon, 9 Nov 2020 11:03:03 +0200 Subject: [PATCH 87/88] Fixed drawing is avaibale only of one half of image --- src/js/shot-generator/hooks/use-draw-on-image.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/shot-generator/hooks/use-draw-on-image.js b/src/js/shot-generator/hooks/use-draw-on-image.js index fbae7634c7..bb21d4875e 100644 --- a/src/js/shot-generator/hooks/use-draw-on-image.js +++ b/src/js/shot-generator/hooks/use-draw-on-image.js @@ -55,7 +55,7 @@ const useDrawOnImage = (drawMode) => { let values = Object.values(getDrawingTextures().getTexturesByObjectType(TextureObjectType.Image)) let {x, y} = mouse({x: event.clientX, y: event.clientY}, gl) raycaster.current.setFromCamera({x, y}, camera) - let imageObjects = scene.children[0].children.filter(object => object.visible === true && object.userData.type !== "volume" && object.userData.type !== "character") + let imageObjects = scene.children[0].children.filter(object => object.visible === true && object.userData.type !== "volume" && object.userData.type !== "character" && object.userData.type !== "controlTarget") let intersections = raycaster.current.intersectObjects(imageObjects, true) let backgroundTexture = getDrawingTextures().getTexturesByObjectType(TextureObjectType.Background) if(backgroundTexture.length) { From 23e1ebad2c07c0807f46537c97918b5555af0fe4 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Wed, 30 Dec 2020 11:21:09 +0200 Subject: [PATCH 88/88] Fixed merge issues --- src/js/xr/src/SceneManagerXR.js | 1 - src/js/xr/src/hooks/use-assets-manager.js | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/js/xr/src/SceneManagerXR.js b/src/js/xr/src/SceneManagerXR.js index 215cd5d340..c1ac2182a4 100644 --- a/src/js/xr/src/SceneManagerXR.js +++ b/src/js/xr/src/SceneManagerXR.js @@ -76,7 +76,6 @@ const { CubeTextureCreator } = require('../../shot-generator/components/Three/H const musicSystem = require('./music-system') -const { createSelector } = require('reselect') const getSceneTextureFilePath = (world, prevTexture, removeAsset) => { if(!world.sceneTexture) return false diff --git a/src/js/xr/src/hooks/use-assets-manager.js b/src/js/xr/src/hooks/use-assets-manager.js index 753950db55..bd64e28c66 100644 --- a/src/js/xr/src/hooks/use-assets-manager.js +++ b/src/js/xr/src/hooks/use-assets-manager.js @@ -94,7 +94,7 @@ const useAssetsManager = (SGConnection) => { .filter(([_, o]) => o.status === 'NotAsked') .filter(([id]) => id !== false) .forEach(([id]) => { - if (!id.includes('/images/') && !id.includes('/emotions/')) { + if (!id.includes('/images/') && !id.includes('/emotions/') && !id.includes('/sceneTextures/')) { SGConnection.getResource('gltf', id) .then(({data}) => { onGLTFBufferLoad(data) @@ -109,7 +109,7 @@ const useAssetsManager = (SGConnection) => { }) dispatch({ type: 'LOAD', payload: { id } }) } else { - SGConnection.getResource('image', `${id}?ts=` + Date.now()) + SGConnection.getResource('image', id) .then(({type, filePath, data}) => { onImageBufferLoad(id, data) .then((texture) => {