mirror of
https://github.com/Doodle3D/Doodle3D-Core.git
synced 2024-12-22 11:03:48 +01:00
first test
This commit is contained in:
parent
da7cdfe350
commit
a60f1970d5
16
index.js
16
index.js
@ -57,6 +57,22 @@ window.addEventListener('dragover', (event) => {
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
import * as CAL from 'cal';
|
||||
store.dispatch(actions.addObject({
|
||||
type: 'STAR',
|
||||
fill: true,
|
||||
solid: false,
|
||||
star: { innerRadius: 10, outerRadius: 20, rays: 5 },
|
||||
transform: new CAL.Matrix({ x: -20, y: 0 })
|
||||
}));
|
||||
store.dispatch(actions.addObject({
|
||||
type: 'RECT',
|
||||
fill: true,
|
||||
rectSize: new CAL.Vector(20, 20),
|
||||
height: 40,
|
||||
transform: new CAL.Matrix({ x: -10, y: -10 })
|
||||
}));
|
||||
|
||||
|
||||
// render dom
|
||||
import React from 'react';
|
||||
|
@ -403,6 +403,18 @@ style.innerHTML = `
|
||||
width: 33px;
|
||||
height: 41px;
|
||||
}
|
||||
#hole-toggle-solid, #hole-toggle-solid-menu {
|
||||
background-image: url('../img/contextmenu/btnShapeFill.png');
|
||||
background-size: 33px auto;
|
||||
width: 33px;
|
||||
height: 41px;
|
||||
}
|
||||
#hole-toggle-hole, #hole-toggle-hole-menu {
|
||||
background-image: url('../img/contextmenu/btnShapeOutline.png');
|
||||
background-size: 33px auto;
|
||||
width: 33px;
|
||||
height: 41px;
|
||||
}
|
||||
#align-right-menu, #align-horizontal-menu, #align-left-menu,
|
||||
#align-top-menu, #align-vertical-menu, #align-bottom-menu {
|
||||
background-image: url('../img/contextmenu/btnAlignHorizontal.png');
|
||||
|
@ -4,6 +4,7 @@ export const COLOR_PICKER = 'color-picker-tool';
|
||||
export const ERASER_SIZE = 'eraser-size-tool';
|
||||
export const BRUSH_SIZE = 'brush-size-tool';
|
||||
export const FILL_TOGGLE = 'fill-toggle-tool';
|
||||
export const HOLE_TOGGLE = 'hole-toggle-tool';
|
||||
export const ALIGN = 'align-tool';
|
||||
export const ADVANCED = 'advanced-tool';
|
||||
|
||||
@ -63,6 +64,14 @@ export const FILL_TOGGLE_TOOLS = [
|
||||
FILL_TOGGLE_OUTLINE
|
||||
];
|
||||
|
||||
export const HOLE_TOGGLE_HOLE = 'hole-toggle-hole';
|
||||
export const HOLE_TOGGLE_SOLID = 'hole-toggle-solid';
|
||||
|
||||
export const HOLE_TOGGLE_TOOLS = [
|
||||
HOLE_TOGGLE_HOLE,
|
||||
HOLE_TOGGLE_SOLID
|
||||
];
|
||||
|
||||
export const ALIGN_LEFT = 'align-left';
|
||||
export const ALIGN_HORIZONTAL = 'align-horizontal';
|
||||
export const ALIGN_RIGHT = 'align-right';
|
||||
|
@ -80,6 +80,11 @@ const context = {
|
||||
selected: contextTools.FILL_TOGGLE_FILL,
|
||||
children: contextTools.FILL_TOGGLE_TOOLS.map(value => ({ value })),
|
||||
...toggleBehavior
|
||||
}, {
|
||||
value: contextTools.HOLE_TOGGLE,
|
||||
selected: contextTools.HOLE_TOGGLE_SOLID,
|
||||
children: contextTools.HOLE_TOGGLE_TOOLS.map(value => ({ value })),
|
||||
...toggleBehavior
|
||||
}, {
|
||||
value: contextTools.ALIGN,
|
||||
selected: contextTools.ALIGN_HORIZONTAL,
|
||||
|
@ -20,7 +20,8 @@ const defaultProperties = {
|
||||
z: 0.0,
|
||||
sculpt: [{ pos: 0.0, scale: 1.0 }, { pos: 1.0, scale: 1.0 }],
|
||||
twist: 0.0,
|
||||
fill : false
|
||||
fill: false,
|
||||
solid: true
|
||||
};
|
||||
|
||||
export const SHAPE_TYPE_PROPERTIES = {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { shapeDataToShape } from '../shape/shapeDataUtils.js';
|
||||
import { shapeDataToShape, determineActiveShape } from '../shape/shapeDataUtils.js';
|
||||
// import R from 'ramda';
|
||||
|
||||
export default class ShapesManager {
|
||||
@ -14,10 +14,8 @@ export default class ShapesManager {
|
||||
update(state) {
|
||||
const needRender = { active: false, inactive: false };
|
||||
|
||||
const selectedObjects = state.selection.objects.map(({ id }) => id);
|
||||
// determine if shape is "active", meaning it will be updated frequently
|
||||
const activeShapes = Object.keys(state.objectsById)
|
||||
.filter(id => state.d2.activeShape === id || selectedObjects.indexOf(id) !== -1);
|
||||
const activeShapes = determineActiveShape(state);
|
||||
|
||||
const { objectsById } = state;
|
||||
|
||||
@ -45,7 +43,7 @@ export default class ShapesManager {
|
||||
const newInactiveObjectUIDs = [];
|
||||
|
||||
for (const UID of spaceObjectIds) {
|
||||
const active = activeShapes.indexOf(UID) !== -1;
|
||||
const active = activeShapes[UID];
|
||||
if (active) {
|
||||
newActiveObjectUIDs.push(UID);
|
||||
} else {
|
||||
|
95
src/d3/ShapeMesh.js
vendored
95
src/d3/ShapeMesh.js
vendored
@ -4,6 +4,9 @@ import { shapeToPointsCornered } from '../shape/shapeToPoints.js';
|
||||
import * as THREE from 'three';
|
||||
import { getPointsBounds, shapeChanged } from '../shape/shapeDataUtils.js';
|
||||
import { DESELECT_TRANSPARENCY, LEGACY_HEIGHT_STEP } from '../constants/d3Constants.js';
|
||||
import ThreeBSP from 'three-js-csg';
|
||||
|
||||
const THREE_BSP = ThreeBSP(THREE);
|
||||
|
||||
const MAX_HEIGHT_BASE = 5;
|
||||
// Legacy compensation. Compensating for the fact that we
|
||||
@ -12,8 +15,11 @@ const MAX_HEIGHT_BASE = 5;
|
||||
// and converting old files on open once
|
||||
const isValidNumber = (num) => typeof num === 'number' && !isNaN(num);
|
||||
|
||||
class ShapeMesh extends THREE.Mesh {
|
||||
constructor(shapeData, toonShader) {
|
||||
class ShapeMesh extends THREE.Object3D {
|
||||
constructor(shapeData, holes, toonShader, active) {
|
||||
super();
|
||||
this.name = shapeData.UID;
|
||||
|
||||
const { sculpt, rotate, twist, height, type, transform, z, color, fill } = shapeData;
|
||||
|
||||
let material;
|
||||
@ -30,12 +36,16 @@ class ShapeMesh extends THREE.Mesh {
|
||||
});
|
||||
}
|
||||
|
||||
super(new THREE.BufferGeometry(), material);
|
||||
this._mesh = new THREE.Mesh(new THREE.BufferGeometry(), material);
|
||||
this._mesh.name = shapeData.UID;
|
||||
this.add(this._mesh);
|
||||
|
||||
this._holeMesh = new THREE.Mesh(new THREE.Geometry, material);
|
||||
this._holeMesh.name = shapeData.UID;
|
||||
this.add(this._holeMesh);
|
||||
|
||||
this._toonShader = toonShader;
|
||||
|
||||
this.name = shapeData.UID;
|
||||
|
||||
this._shapes = [];
|
||||
this._shapesMap = [];
|
||||
|
||||
@ -52,10 +62,49 @@ class ShapeMesh extends THREE.Mesh {
|
||||
this._color = color;
|
||||
this._fill = fill;
|
||||
|
||||
this.visible = shapeData.solid;
|
||||
|
||||
this.updatePoints(shapeData);
|
||||
|
||||
this._checkHoles(holes, active);
|
||||
}
|
||||
|
||||
update(shapeData) {
|
||||
_checkHoles(holes, active) {
|
||||
if (active) {
|
||||
this._holeMesh.visible = false;
|
||||
this._mesh.visible = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
holes = holes.map(hole => hole.mesh._mesh);
|
||||
const objectBoundingBox = new THREE.Box3().setFromObject(this._mesh);
|
||||
holes = holes.filter(hole => {
|
||||
const holeBoundingBox = new THREE.Box3().setFromObject(hole);
|
||||
return holeBoundingBox.intersectsBox(objectBoundingBox);
|
||||
});
|
||||
|
||||
if (holes.length === 0) {
|
||||
this._holeMesh.visible = false;
|
||||
this._mesh.visible = true;
|
||||
return false;
|
||||
}
|
||||
// is coliding with holes
|
||||
|
||||
const objectGeometry = new THREE.Geometry().fromBufferGeometry(this._mesh.geometry);
|
||||
let objectBSP = new THREE_BSP(objectGeometry);
|
||||
for (const hole of holes) {
|
||||
const holeGeometry = new THREE.Geometry().fromBufferGeometry(hole.geometry);
|
||||
const holeBSP = new THREE_BSP(holeGeometry)
|
||||
objectBSP = objectBSP.subtract(holeBSP)
|
||||
}
|
||||
this._holeMesh.geometry = objectBSP.toMesh().geometry;
|
||||
|
||||
this._holeMesh.visible = true;
|
||||
this._mesh.visible = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
update(shapeData, holes, active) {
|
||||
let changed = false;
|
||||
|
||||
if (shapeChanged(this._shapeData, shapeData)) {
|
||||
@ -88,17 +137,27 @@ class ShapeMesh extends THREE.Mesh {
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!shapeData.solid) {
|
||||
this.visible = shapeData.solid || active;
|
||||
this.setOpaque(true);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (this._checkHoles(holes, active)) {
|
||||
changed = true;
|
||||
}
|
||||
|
||||
this._shapeData = shapeData;
|
||||
return changed;
|
||||
}
|
||||
|
||||
setOpaque(opaque) {
|
||||
this.material.opacity = opaque ? 1.0 : 1.0 - DESELECT_TRANSPARENCY;
|
||||
this.material.transparent = !opaque;
|
||||
this._mesh.material.opacity = opaque ? 1.0 : 1.0 - DESELECT_TRANSPARENCY;
|
||||
this._mesh.material.transparent = !opaque;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.geometry.dispose();
|
||||
this._mesh.geometry.dispose();
|
||||
}
|
||||
|
||||
updatePoints(shapeData) {
|
||||
@ -173,7 +232,7 @@ class ShapeMesh extends THREE.Mesh {
|
||||
throw new Error(`Cannot update object ${this.name}: color is an invalid value.`);
|
||||
}
|
||||
|
||||
this.material.color.setHex(color);
|
||||
this._mesh.material.color.setHex(color);
|
||||
this._color = color;
|
||||
}
|
||||
|
||||
@ -247,10 +306,10 @@ class ShapeMesh extends THREE.Mesh {
|
||||
|
||||
this._vertexBuffer.needsUpdate = true;
|
||||
|
||||
this.geometry.boundingBox = null;
|
||||
this.geometry.boundingSphere = null;
|
||||
this.geometry.computeFaceNormals();
|
||||
this.geometry.computeVertexNormals();
|
||||
this._mesh.geometry.boundingBox = null;
|
||||
this._mesh.geometry.boundingSphere = null;
|
||||
this._mesh.geometry.computeFaceNormals();
|
||||
this._mesh.geometry.computeVertexNormals();
|
||||
}
|
||||
_updateSide() {
|
||||
// TODO use higher precision for export mesh
|
||||
@ -295,8 +354,8 @@ class ShapeMesh extends THREE.Mesh {
|
||||
|
||||
const numHeightSteps = this._heightSteps.length;
|
||||
|
||||
this.geometry.dispose();
|
||||
this.geometry = new THREE.BufferGeometry();
|
||||
this._mesh.geometry.dispose();
|
||||
this._mesh.geometry = new THREE.BufferGeometry();
|
||||
|
||||
// store total number of indexes and vertices needed
|
||||
let indexBufferLength = 0;
|
||||
@ -370,7 +429,7 @@ class ShapeMesh extends THREE.Mesh {
|
||||
|
||||
const indexes = new Uint32Array(indexBufferLength);
|
||||
const indexBuffer = new THREE.BufferAttribute(indexes, 1);
|
||||
this.geometry.setIndex(indexBuffer);
|
||||
this._mesh.geometry.setIndex(indexBuffer);
|
||||
|
||||
let indexCounter = 0;
|
||||
for (let i = 0; i < this._shapes.length; i ++) {
|
||||
@ -407,7 +466,7 @@ class ShapeMesh extends THREE.Mesh {
|
||||
|
||||
this._vertices = new Float32Array(vertexBufferLength);
|
||||
this._vertexBuffer = new THREE.BufferAttribute(this._vertices, 3);
|
||||
this.geometry.addAttribute('position', this._vertexBuffer);
|
||||
this._mesh.geometry.addAttribute('position', this._vertexBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
27
src/d3/ShapesManager.js
vendored
27
src/d3/ShapesManager.js
vendored
@ -1,3 +1,4 @@
|
||||
import { determineActiveShape } from '../shape/shapeDataUtils.js';
|
||||
import { SHAPE_TYPE_PROPERTIES } from '../constants/shapeTypeProperties.js';
|
||||
import * as THREE from 'three';
|
||||
import ShapeMesh from './ShapeMesh.js';
|
||||
@ -45,23 +46,35 @@ export default class ShapesManager extends THREE.Object3D {
|
||||
}
|
||||
}
|
||||
|
||||
for (const id in state.objectsById) {
|
||||
// const shapeData = this._state.objectsById[id];
|
||||
const activeShapes = determineActiveShape(state);
|
||||
|
||||
const ids = Object.keys(state.objectsById).sort((a, b) => {
|
||||
const solidA = state.objectsById[a].solid;
|
||||
const solidB = state.objectsById[b].solid;
|
||||
|
||||
if (solidA === solidB) return 0;
|
||||
return solidA ? 1 : -1;
|
||||
});
|
||||
|
||||
const holes = [];
|
||||
for (let i = 0; i < ids.length; i ++) {
|
||||
const id = ids[i];
|
||||
const newShapeData = state.objectsById[id];
|
||||
const active = activeShapes[id];
|
||||
|
||||
if (!SHAPE_TYPE_PROPERTIES[newShapeData.type].D3Visible) continue;
|
||||
// add new shapes
|
||||
if (!this._state || !this._state.objectsById[id]) {
|
||||
this._handleShapeAdded(newShapeData);
|
||||
this._handleShapeAdded(newShapeData, holes, active);
|
||||
render = true;
|
||||
} else {
|
||||
const { mesh } = this._meshes[id];
|
||||
if (mesh.update(newShapeData)) {
|
||||
if (mesh.update(newShapeData, holes, active)) {
|
||||
render = true;
|
||||
}
|
||||
}
|
||||
if (!newShapeData.solid && !active) holes.push(this._meshes[id]);
|
||||
}
|
||||
|
||||
this._state = state;
|
||||
|
||||
return render;
|
||||
@ -90,10 +103,10 @@ export default class ShapesManager extends THREE.Object3D {
|
||||
this._spaces[space].remove(mesh);
|
||||
}
|
||||
|
||||
_handleShapeAdded(shapeData) {
|
||||
_handleShapeAdded(shapeData, holes) {
|
||||
if (!SHAPE_TYPE_PROPERTIES[shapeData.type].D3Visible) return;
|
||||
const { space } = shapeData;
|
||||
const mesh = new ShapeMesh(shapeData, this._toonShader);
|
||||
const mesh = new ShapeMesh(shapeData, holes, this._toonShader);
|
||||
this._meshes[shapeData.UID] = { mesh, space };
|
||||
|
||||
this._spaces[space].add(mesh);
|
||||
|
@ -22,6 +22,10 @@ export default function (state, action) {
|
||||
const fill = fillBool ? contextTools.FILL_TOGGLE_FILL : contextTools.FILL_TOGGLE_OUTLINE;
|
||||
menus = select(menus, fill);
|
||||
|
||||
const solidBool = firstSelected && state.objectsById[firstSelected.id].solid;
|
||||
const solid = solidBool ? contextTools.HOLE_TOGGLE_SOLID : contextTools.HOLE_TOGGLE_HOLE;
|
||||
menus = select(menus, solid);
|
||||
|
||||
return update(state, { menus: { $set: menus } });
|
||||
}
|
||||
|
||||
@ -104,6 +108,18 @@ export default function (state, action) {
|
||||
});
|
||||
}
|
||||
|
||||
case contextTools.HOLE_TOGGLE_HOLE:
|
||||
case contextTools.HOLE_TOGGLE_SOLID: {
|
||||
const solid = action.tool === contextTools.HOLE_TOGGLE_SOLID;
|
||||
|
||||
return update(state, {
|
||||
objectsById: state.selection.objects.reduce((updateObject, { id }) => {
|
||||
updateObject[id] = { solid: { $set: solid } };
|
||||
return updateObject;
|
||||
}, {})
|
||||
});
|
||||
}
|
||||
|
||||
case contextTools.ALIGN_LEFT:
|
||||
case contextTools.ALIGN_HORIZONTAL:
|
||||
case contextTools.ALIGN_RIGHT:
|
||||
|
@ -69,3 +69,17 @@ function shapeDataToShapeRaw(shapeData) {
|
||||
return new Shape(shapeData);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO can maybe be memoized
|
||||
export const determineActiveShape = (state) => {
|
||||
const selectedObjects = state.selection.objects.map(({ id }) => id);
|
||||
const activeShapes = {};
|
||||
for (const id in state.objectsById) {
|
||||
if (state.d2.activeShape === id) {
|
||||
activeShapes[id] = true;
|
||||
continue;
|
||||
}
|
||||
activeShapes[id] = state.d2.transform.active && selectedObjects.includes(id);
|
||||
}
|
||||
return activeShapes;
|
||||
};
|
||||
|
@ -92,13 +92,13 @@ export function generateExportMesh(state, options = {}) {
|
||||
materials.push(material);
|
||||
}
|
||||
|
||||
if (unionGeometry) objectGeometry = new THREE_BSP(objectGeometry, materials.length);
|
||||
if (unionGeometry) objectGeometry = new THREE_BSP(objectGeometry, materialIndex);
|
||||
|
||||
if (exportGeometry) {
|
||||
if (unionGeometry) {
|
||||
exportGeometry = exportGeometry.union(objectGeometry);
|
||||
} else {
|
||||
exportGeometry = exportGeometry.merge(objectGeometry, undefined, materials.length);
|
||||
exportGeometry.merge(objectGeometry, undefined, materialIndex);
|
||||
}
|
||||
} else {
|
||||
exportGeometry = objectGeometry;
|
||||
|
Loading…
Reference in New Issue
Block a user