diff --git a/app/assets/styles/app.css b/app/assets/styles/app.css index 7a8a961..bb7fa7e 100755 --- a/app/assets/styles/app.css +++ b/app/assets/styles/app.css @@ -137,7 +137,7 @@ menu[eicmenu] [menuitem] > a > button, menu[eicmenu] [menuitem] > .nolink button [eicapp] .app-workspace .window > header .controls button.shrink { display: none; } [eicapp] .app-workspace .window > section { cursor: default; - overflow: auto; + overflow: hidden; transition: all 0.5s; flex: 1 1 auto; width: 100%; @@ -271,6 +271,25 @@ article[eiccard][media] > header { padding: var(--eicui-base-spacing-l) var(--eicui-base-spacing-m) var(--eicui-base-spacing-s) var(--eicui-base-spacing-m); } +[eicapp] select { + width: 100%; + appearance: none; + -webkit-appearance: none; + -moz-appearance: none; + color: #DDD; + border: 1px solid #444; + border-radius: 2rem; + padding: .3rem 2rem .3rem 1rem; + font-size: 15px; + cursor: pointer; + margin: 0.5rem 0 0.5rem 0; + background: + url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpolyline points='4 8 12 16 20 8' stroke='green' stroke-width='3' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") + no-repeat right 12px center / 12px 12px, + linear-gradient(0deg, #353, #222) no-repeat padding-box; + +} + .eic-session { padding: 0; display: grid; diff --git a/app/controllers/editors/EditorsController.js b/app/controllers/editors/EditorsController.js index cc62ccd..6d5205c 100644 --- a/app/controllers/editors/EditorsController.js +++ b/app/controllers/editors/EditorsController.js @@ -16,24 +16,6 @@ class EditorsController extends WindozController { agents : new AgentsModel('/agents') } - // const ttb = new app.LoadedModules.Threetobus({ - // eventsMapping: this.eventsMapping, - // sceneSize: this.arenaConfig.arenaSize, - // }) - // ttb.initScene({ - // axes: true, - // grid: true, - // }) - - // this.agentDefs = await models.agents.getSprites('Basic 3D') - // const a1 = ttb.agentFromJSON('agent42', this.agentDefs.nocode1) - // const a2 = ttb.agentFromJSON('agent42', this.agentDefs.nocode2) - // ttb.scene.add(a1) - // ttb.scene.add(a2) - - //TODO: eventsMapping: address child by suffix in assignations - - this.loadWindow( 'editors/KeyframeView', { @@ -45,8 +27,6 @@ class EditorsController extends WindozController { }, { models: models, - agentDefs: this.agentDefs, - //ttb: ttb, } ) diff --git a/app/controllers/editors/EditorsController.json b/app/controllers/editors/EditorsController.json index ffc0e74..865a1a3 100644 --- a/app/controllers/editors/EditorsController.json +++ b/app/controllers/editors/EditorsController.json @@ -23,16 +23,16 @@ "AgentsModel" ], "views": [ - "editors/KeyframeView" + {"view":"editors/KeyframeView", "dependencies": ["editors/modules/agentPreview.module"]} ], "controllerDependencies": [ "/helpers/basicDialogs", "/helpers/validators", - "/helpers/activeAttributes", - "/thirdparty/Threetobus/three.module", - "/thirdparty/Threetobus/OrbitControls.module", - "/thirdparty/Threetobus/tween.module", - "/thirdparty/Threetobus/threetobus.module" + "/helpers/activeAttributes", + "/helpers/helpers3D.module", + "/thirdparty/Three/three.module", + "/thirdparty/Three/OrbitControls.module", + "/thirdparty/Three/tween.module" ], "assets": { "styles": [ diff --git a/app/controllers/live/DashboardsController.js b/app/controllers/live/DashboardsController.js index fe57416..ca398a4 100644 --- a/app/controllers/live/DashboardsController.js +++ b/app/controllers/live/DashboardsController.js @@ -3,7 +3,6 @@ class DashboardsController extends WindozController { constructor(params) { super(params) this.arenaConfig = app.Assets.Store.json.arenaConfig - this.agentDefs = app.Assets.Store.json.agentDefs this.eventsMapping = app.Assets.Store.json.eventsMapping } @@ -28,8 +27,8 @@ class DashboardsController extends WindozController { }) this.agentDefs = await models.agents.getSprites('Basic 3D') - const a1 = ttb.agentFromJSON('agent42', this.agentDefs.nocode1) - const a2 = ttb.agentFromJSON('agent42', this.agentDefs.nocode2) + const a1 = ttb.createAgent('agent42', this.agentDefs.nocode1) + const a2 = ttb.createAgent('agent42', this.agentDefs.nocode2) ttb.scene.add(a1) ttb.scene.add(a2) diff --git a/app/controllers/live/DashboardsController.json b/app/controllers/live/DashboardsController.json index 500e59b..f4bf03c 100644 --- a/app/controllers/live/DashboardsController.json +++ b/app/controllers/live/DashboardsController.json @@ -21,10 +21,11 @@ "controllerDependencies": [ "/helpers/basicDialogs", "/helpers/validators", - "/helpers/activeAttributes", - "/thirdparty/Threetobus/three.module", - "/thirdparty/Threetobus/OrbitControls.module", - "/thirdparty/Threetobus/tween.module", + "/helpers/activeAttributes", + "/helpers/helpers3D.module", + "/thirdparty/Three/three.module", + "/thirdparty/Three/OrbitControls.module", + "/thirdparty/Three/tween.module", "/thirdparty/Threetobus/threetobus.module" ], "assets": { @@ -35,7 +36,7 @@ ], "json": [ {"id":"arenaConfig", "name": "arena/arenaConfig1.json"}, - {"id":"eventsMapping", "name": "threetobus/eventsMapping.json"} + {"id":"eventsMapping", "name": "threetobus/eventsMapping.json"} ] } } \ No newline at end of file diff --git a/app/helpers/helpers3D.module.js b/app/helpers/helpers3D.module.js new file mode 100644 index 0000000..876bd1f --- /dev/null +++ b/app/helpers/helpers3D.module.js @@ -0,0 +1,73 @@ +import * as THREE from '/app/thirdparty/Three/three.module.js' + +if(!app.helpers) app.helpers = {} +/** + * Mixing add-in methods to your view instance. + * All of this should not be a helper, but inherited this from WindozDomContent, but not my framework anymore. + */ +app.helpers.helpers3D = { + + agentFromJSON(id, desc){ + let obj + if(desc.type === 'Mesh') { + const geom = new THREE[desc.geometry.type](...(desc.geometry.args || [])) + + const matType = desc.material.type + const matProps = { ...desc.material } + for (const key in matProps) { + if (key === 'type') continue + if (typeof matProps[key] === 'string' && THREE[matProps[key]] !== undefined) { + matProps[key] = THREE[matProps[key]] + } + } + // convert color strings like "0xffffaa" to numbers + if (typeof matProps.color === 'string' && matProps.color.startsWith('0x')) { + matProps.color = parseInt(matProps.color) + } + const mat = new THREE[matType](matProps) + + obj = new THREE.Mesh(geom, mat) + } else if(desc.type === 'Group') { + obj = new THREE.Group() + } else { + throw new Error("Unknown type: " + desc.type) + } + + // Apply transforms + if(desc.position) obj.position.set(...desc.position) + if(desc.rotation) obj.rotation.set(...desc.rotation) + if(desc.scale) obj.scale.set(...desc.scale) + + // Recursively add children + if(desc.children) { + desc.children.forEach(childDesc => { + const childId = (childDesc.childSuffix) ? `${id}_${childDesc.childSuffix}` : '' + obj.add(this.agentFromJSON(childId, childDesc)) + }) + } + obj.name = id + return obj + }, + + cameraAutoFrame(object, camera, offset = 1.25, controls) { + const box = new THREE.Box3().setFromObject(object) + const size = new THREE.Vector3() + const center = new THREE.Vector3() + box.getSize(size) + box.getCenter(center) + + const maxDim = Math.max(size.x, size.y, size.z) + const fov = camera.fov * Math.PI / 180 + let cameraZ = maxDim / (2 * Math.tan(fov / 2)) * offset + + camera.position.copy(center) + camera.position.z += cameraZ + camera.lookAt(center) + + if (controls) { + controls.target.copy(center) + controls.update() + } + } + +} \ No newline at end of file diff --git a/app/thirdparty/Threetobus/OrbitControls.module.js b/app/thirdparty/Three/OrbitControls.module.js similarity index 100% rename from app/thirdparty/Threetobus/OrbitControls.module.js rename to app/thirdparty/Three/OrbitControls.module.js diff --git a/app/thirdparty/Threetobus/three.core.js b/app/thirdparty/Three/three.core.js similarity index 100% rename from app/thirdparty/Threetobus/three.core.js rename to app/thirdparty/Three/three.core.js diff --git a/app/thirdparty/Threetobus/three.module.js b/app/thirdparty/Three/three.module.js similarity index 100% rename from app/thirdparty/Threetobus/three.module.js rename to app/thirdparty/Three/three.module.js diff --git a/app/thirdparty/Threetobus/tween.module.js b/app/thirdparty/Three/tween.module.js similarity index 100% rename from app/thirdparty/Threetobus/tween.module.js rename to app/thirdparty/Three/tween.module.js diff --git a/app/thirdparty/Threetobus/threetobus.module.js b/app/thirdparty/Threetobus/threetobus.module.js index efad0bd..bc31751 100644 --- a/app/thirdparty/Threetobus/threetobus.module.js +++ b/app/thirdparty/Threetobus/threetobus.module.js @@ -1,10 +1,11 @@ -import * as THREE from './three.module.js' -import { OrbitControls } from './OrbitControls.module.js' -import * as TWEEN from './tween.module.js' +import * as THREE from '../Three/three.module.js' +import { OrbitControls } from '../Three/OrbitControls.module.js' +import * as TWEEN from '../Three/tween.module.js' export class Threetobus{ constructor(options){ + Object.assign(this, app.helpers.helpers3D) this._curEventsMapping = [] this._stagedEventsMapping = options.eventsMapping this.sceneSize = options.sceneSize @@ -214,47 +215,9 @@ export class Threetobus{ return(renderEngine) } - agentFromJSON(id, desc){ - let obj - if(desc.type === 'Mesh') { - const geom = new THREE[desc.geometry.type](...(desc.geometry.args || [])) - - const matType = desc.material.type - const matProps = { ...desc.material } - for (const key in matProps) { - if (key === 'type') continue - if (typeof matProps[key] === 'string' && THREE[matProps[key]] !== undefined) { - matProps[key] = THREE[matProps[key]] - } - } - // convert color strings like "0xffffaa" to numbers - if (typeof matProps.color === 'string' && matProps.color.startsWith('0x')) { - matProps.color = parseInt(matProps.color) - } - const mat = new THREE[matType](matProps) - - obj = new THREE.Mesh(geom, mat) - } else if(desc.type === 'Group') { - obj = new THREE.Group() - } else { - throw new Error("Unknown type: " + desc.type) - } - - // Apply transforms - if(desc.position) obj.position.set(...desc.position) - if(desc.rotation) obj.rotation.set(...desc.rotation) - if(desc.scale) obj.scale.set(...desc.scale) - - // Recursively add children - if(desc.children) { - desc.children.forEach(childDesc => { - const childId = (childDesc.childSuffix) ? `${id}_${childDesc.childSuffix}` : '' - obj.add(this.agentFromJSON(childId, childDesc)) - }) - } - obj.name = id + createAgent(id, desc){ this.tweensRegistry[id] = { 'move': null, 'rotate': null } - return obj + return(this.agentFromJSON(id, desc)) } smoothMove(options){ diff --git a/app/views/editors/KeyframeView.html b/app/views/editors/KeyframeView.html index 8a54727..7ffa99f 100644 --- a/app/views/editors/KeyframeView.html +++ b/app/views/editors/KeyframeView.html @@ -1,16 +1,25 @@
-
-
Agent show
-
agents selector
+
+
+
+ agents selector + +
@@ -25,5 +34,3 @@
- - diff --git a/app/views/editors/KeyframeView.js b/app/views/editors/KeyframeView.js index e111f28..2311b23 100644 --- a/app/views/editors/KeyframeView.js +++ b/app/views/editors/KeyframeView.js @@ -3,7 +3,6 @@ class KeyframeView extends WindozDomContent { constructor() { super() Object.assign(this, app.helpers.activeAttributes) - //this.tileMarkup = app.Assets.Store.html['/app/assets/html/mailing/tile.html'] } DOMContentFocused(options) { @@ -15,36 +14,31 @@ class KeyframeView extends WindozDomContent { DOMContentBlured(options) { this.wasBlured = true } - DOMContentLoaded(options) { + async DOMContentLoaded(options) { this.windowPrefsId = `editors.keyframeview` - for(let model in options.models) this[model] = options.models[model] - // this.kfe = options.kfe + this.models = options.models const components = ui.eicfy(this.el) this.setupTriggers(components) this.setupRefs(components) - // this.renderingEngine = this.kfe.startRendering(this.outputs.kfeCanvas, options.mode) - this.output('settingsMenu',app.Assets.Store.html.spaceViewSetting) - this.outputs.settingsMenu.querySelectorAll('input[type="toggler"]').forEach(el => { - const tog = new InputToggler(el) - // if(this.kfe[tog._el.name].layers){ - // tog.value = this.renderingEngine.camera.layers.test(this.kfe[tog._el.name].layers) ? 'yes' : 'no' - // } - tog.onToggle = this.settingsToggle.bind(this) - }) + + + this.agentDefs = await this.models.agents.getSprites('Basic 3D') + for(const agentType in this.agentDefs){ + const opt = new Option(agentType, agentType) + this.outputs.agentsSelector.add(opt) + } + this.outputs.agentsSelector.addEventListener('change',this.onChangeAgent.bind(this)) + + this.agentPreview = new app.LoadedModules.AgentPreview(this.outputs.agentSampleCanvas, this.agentDefs) + this.onChangeAgent() + this.agentPreview.startRendering() + this.agentPreview.animation = true } - settingsToggle(value, object){ - if(['grid','axes'].includes(object._el.name)){ - const layerId = {'grid':1,'axes':2}[object._el.name] - if(value=='yes'){ - this.renderingEngine.camera.layers.enable(layerId) - } else { - this.renderingEngine.camera.layers.disable(layerId) - } - } - //TODO save & restore in prefs + onChangeAgent(event){ + this.agentPreview.setAgent(this.outputs.agentsSelector.value) } - + } app.registerClass('KeyframeView', KeyframeView) diff --git a/app/views/editors/modules/agentPreview.module.js b/app/views/editors/modules/agentPreview.module.js new file mode 100644 index 0000000..15b5b6a --- /dev/null +++ b/app/views/editors/modules/agentPreview.module.js @@ -0,0 +1,76 @@ +import * as THREE from '/app/thirdparty/Three/three.module.js' +import { OrbitControls } from '/app/thirdparty/Three/OrbitControls.module.js' +import * as TWEEN from '/app/thirdparty/Three/tween.module.js' + +export class AgentPreview{ + + constructor(canvasEl, agentDefs){ + Object.assign(this, app.helpers.helpers3D) + this.agentDefs = app.Assets.Store.json.agentDefs + this.canvasEl = canvasEl + this.agentDefs = agentDefs + this.currentAgentObj = null + this.renderer = null + this._animation = false + this.initScene() + } + + initScene(){ + // Scene + this.scene = new THREE.Scene() + + // Camera + this.camera = new THREE.PerspectiveCamera(75, this.canvasEl.clientWidth / this.canvasEl.clientHeight, 0.1, 1000) + this.camera.position.set(3, 3, 5) + this.camera.lookAt(0, 0, 0) + this.camera.layers.enable(1) + this.camera.layers.enable(2) + + // Lights + const light = new THREE.DirectionalLight(0xffffff, 1) + light.position.set(5, 5, 5) + light.intensity = 2 + this.scene.add(light) + this.scene.add(new THREE.AmbientLight(0xffffff, 0.4)) + + this.renderer = new THREE.WebGLRenderer({ antialias: true, canvas: this.canvasEl }) + } + + startRendering(){ + //this.renderer.addControls() + this.renderer.render(this.scene, this.camera) + } + + setAgent(atype){ + if(this.currentAgentObj && (this.scene.children.includes(this.currentAgentObj))){ + this.scene.remove(this.currentAgentObj) + } + this.currentAgentObj = this.agentFromJSON('previewedAgent', this.agentDefs[atype]) + this.currentAgentObj.position.set(0, 0, 0) + this.scene.add(this.currentAgentObj) + + this.cameraAutoFrame(this.currentAgentObj, this.camera) + } + + set animation(value){ + this._animation = value + if(value) this._animate() + } + + get animation(){ + return(this._animation) + } + + _animate = () => { // to avoid havind to bind(this) in requestAnimationFrame, because one bound fn per frame = continuous GC load + if(!this.animation) return + requestAnimationFrame(this._animate) + this.currentAgentObj.rotation.x += 0.005 + this.currentAgentObj.rotation.y += 0.01 + this.renderer.render(this.scene, this.camera) + } +} + +// Make this module available to common JS +if(!app.LoadedModules) app.LoadedModules = {} +app.LoadedModules.AgentPreview = AgentPreview +