logs in console, caching in getAgentProps, console createAgent independent of GUI

This commit is contained in:
STEINNI
2025-12-06 18:53:23 +00:00
parent 1143c52c11
commit c1d0a16cf3
26 changed files with 18025 additions and 100 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

+11
View File
@@ -0,0 +1,11 @@
Model Information:
* title: Mech Drone
* source: https://sketchfab.com/3d-models/mech-drone-8d06874aac5246c59edb4adbe3606e0e
* author: Willy Decarpentrie (https://sketchfab.com/skudgee)
Model License:
* license type: CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)
* requirements: Author must be credited. Commercial use is allowed.
If you use this 3D model in your project be sure to copy paste this credit wherever you share it:
This work is based on "Mech Drone" (https://sketchfab.com/3d-models/mech-drone-8d06874aac5246c59edb4adbe3606e0e) by Willy Decarpentrie (https://sketchfab.com/skudgee) licensed under CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)
File diff suppressed because it is too large Load Diff
Binary file not shown.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 MiB

+40
View File
@@ -0,0 +1,40 @@
//////////////TESTING /////////////////
this.threeClock = new THREE.Clock()
const loader = new GLTFLoader().setPath( '/app/assets/3dModels/' );
loader.load( 'mechdrone/scene.gltf', async function ( gltf ) {
const model = gltf.scene;
// wait until the model can be added to the scene without blocking due to shader compilation
await this.renderer.compileAsync( model, this.camera, this.scene );
model.scale.set(5,5,5)
this.scene.add( model )
model.traverse(obj => {
console.log(
obj.type,
obj.name || '(no name)',
obj.isBone ? '[BONE]' : ''
)
if (obj.isMesh && obj.material && obj.material.color) {
const c = obj.material.color.clone()
const boost = 0.1 // réglage global
obj.material.emissive = c
obj.material.emissiveIntensity = boost
}
})
this.leftArm = model.getObjectByName('ArmL1_01')
if (gltf.animations && gltf.animations.length > 0) {
this.mixer = new THREE.AnimationMixer(gltf.scene)
// On joue la première animation
const action = this.mixer.clipAction(gltf.animations[0])
action.play()
}
}.bind(this) )
// In Render:
const delta = this.threeClock.getDelta()
if(this.mixer) this.mixer.update(delta)
+29 -4
View File
@@ -1,5 +1,5 @@
<style>
.kf-editor .inner-console .results .api-summary{ border: 1px solid white;margin: .5em;width: 90%; }
.kf-editor .inner-console .results .api-summary{ border: 1px solid white;margin: .5em;width: 90%; border-radius: 7px;}
.kf-editor .inner-console .results .api-summary h1{
font-size: 1.5em;
margin: 0;
@@ -12,6 +12,7 @@
background: #DDD;
color: black;
}
.kf-editor .inner-console .results button[data-trigger="onExample"]{ margin-left: 2em; }
</style>
<div class="title">Javascript Keyframe console</div>
Use any combination of Javascript and API calls to update your keyframe scene.<br>
@@ -21,8 +22,32 @@ Special commands:<br>
<div class="api-summary">
<h1>API:</h1>
<ul>
<li>Create an agent: <b>newAgent(type, properties)</b> - returns the AID</li>
<li>Remove an agent: <b>removeAgent(aid)</b></li>
<li>Update an agent: <b>updateAgent(aid, properties)</b></li>
<li>Log here: <b>log()</b> - any number of arguments of any type</li>
<li>Create an agent: <b>createAgent(type, properties)</b> - Promise returning the AID <button eicbutton info xxsmall data-trigger="onSnippet" data-snippet="createAgent">Snippet</button></li>
<li>Remove an agent: <b>removeAgent(aid)</b> <button eicbutton info xxsmall data-trigger="onSnippet" data-snippet="removeAgent">Snippet</button></li>
<li>Update an agent: <b>updateAgent(aid, properties)</b> <button eicbutton info xxsmall data-trigger="onSnippet" data-snippet="updateAgent">Snippet</button></li>
<li>List agent types: <b>listAgentTypes()</b> <button eicbutton info xxsmall data-trigger="onSnippet" data-snippet="listAgentTypes">Snippet</button></li>
<li>List agents on scene: <b>listAgentsOnScene()</b> <button eicbutton info xxsmall data-trigger="onSnippet" data-snippet="listAgentsOnScene">Snippet</button></li>
</ul>
</div>
<div class="snippet" data-snippet="createAgent" style="display:none">
const agtIds = []
for(let i=0; i<360; i+=22.5){
agtIds.push(
await createAgent(1, { position: { x: 10*Math.sin(i*Math.PI/180), y: 10*Math.cos(i*Math.PI/180), z: 0 } } )
)
}
log(agtIds)
</div>
<div class="snippet" data-snippet="removeAgent" style="display:none">
</div>
<div class="snippet" data-snippet="updateAgent" style="display:none">
</div>
<div class="snippet" data-snippet="listAgentTypes" style="display:none">
for(const agt of listAgentTypes() console.log(agt)
</div>
<div class="snippet" data-snippet="listAgentsOnScene" style="display:none">
for(const agt of listAgentsOnScene() console.log(agt)
</div>
Binary file not shown.
+240
View File
@@ -0,0 +1,240 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - FBX loader</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link type="text/css" rel="stylesheet" href="https://threejs.org/examples/main.css">
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@0.160.0/build/three.module.js",
"three/examples/jsm/loaders/FontLoader.js": "https://unpkg.com/three@0.160.0/examples/jsm/loaders/FontLoader.js",
"three/examples/jsm/geometries/TextGeometry.js": "https://unpkg.com/three@0.160.0/examples/jsm/geometries/TextGeometry.js",
"three/examples/jsm/loaders/RGBELoader.js": "https://unpkg.com/three@0.160.0/examples/jsm/loaders/RGBELoader.js",
"three/examples/jsm/controls/OrbitControls.js": "https://unpkg.com/three@0.160.0/examples/jsm/controls/OrbitControls.js",
"three/examples/jsm/libs/tween.module.js": "https://unpkg.com/three@0.160.0/examples/jsm/libs/tween.module.js",
"three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/"
}
}
</script>
</head>
<body>
<div id="info">
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - FBXLoader<br />
Character and animation from <a href="https://www.mixamo.com/" target="_blank" rel="noopener">Mixamo</a>
</div>
<script type="module">
import * as THREE from 'three';
import Stats from 'three/addons/libs/stats.module.js';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { FBXLoader } from 'three/addons/loaders/FBXLoader.js';
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
const manager = new THREE.LoadingManager();
let camera, scene, renderer, stats, object, loader, guiMorphsFolder;
let mixer;
const clock = new THREE.Clock();
const params = {
asset: 'Samba Dancing'
};
const assets = [
'Samba Dancing',
'morph_test',
'monkey',
'monkey_embedded_texture',
'vCube',
];
init();
function init() {
const container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 2000 );
camera.position.set( 100, 200, 300 );
scene = new THREE.Scene();
scene.background = new THREE.Color( 0xa0a0a0 );
scene.fog = new THREE.Fog( 0xa0a0a0, 200, 1000 );
const hemiLight = new THREE.HemisphereLight( 0xffffff, 0x444444, 5 );
hemiLight.position.set( 0, 200, 0 );
scene.add( hemiLight );
const dirLight = new THREE.DirectionalLight( 0xffffff, 5 );
dirLight.position.set( 0, 200, 100 );
dirLight.castShadow = true;
dirLight.shadow.camera.top = 180;
dirLight.shadow.camera.bottom = - 100;
dirLight.shadow.camera.left = - 120;
dirLight.shadow.camera.right = 120;
scene.add( dirLight );
// scene.add( new THREE.CameraHelper( dirLight.shadow.camera ) );
// ground
const mesh = new THREE.Mesh( new THREE.PlaneGeometry( 2000, 2000 ), new THREE.MeshPhongMaterial( { color: 0x999999, depthWrite: false } ) );
mesh.rotation.x = - Math.PI / 2;
mesh.receiveShadow = true;
scene.add( mesh );
const grid = new THREE.GridHelper( 2000, 20, 0x000000, 0x000000 );
grid.material.opacity = 0.2;
grid.material.transparent = true;
scene.add( grid );
loader = new FBXLoader( manager );
loadAsset( params.asset );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setAnimationLoop( animate );
renderer.shadowMap.enabled = true;
container.appendChild( renderer.domElement );
const controls = new OrbitControls( camera, renderer.domElement );
controls.target.set( 0, 100, 0 );
controls.update();
window.addEventListener( 'resize', onWindowResize );
// stats
stats = new Stats();
container.appendChild( stats.dom );
const gui = new GUI();
gui.add( params, 'asset', assets ).onChange( function ( value ) {
loadAsset( value );
} );
guiMorphsFolder = gui.addFolder( 'Morphs' ).hide();
}
function loadAsset( asset ) {
loader.load( 'models/ffbxbx/' + asset + '.fbx', function ( group ) {
if ( object ) {
object.traverse( function ( child ) {
if ( child.isSkinnedMesh ) {
child.skeleton.dispose();
}
if ( child.material ) {
const materials = Array.isArray( child.material ) ? child.material : [ child.material ];
materials.forEach( material => {
if ( material.map ) material.map.dispose();
material.dispose();
} );
}
if ( child.geometry ) child.geometry.dispose();
} );
scene.remove( object );
}
//
object = group;
if ( object.animations && object.animations.length ) {
mixer = new THREE.AnimationMixer( object );
const action = mixer.clipAction( object.animations[ 0 ] );
action.play();
} else {
mixer = null;
}
guiMorphsFolder.children.forEach( ( child ) => child.destroy() );
guiMorphsFolder.hide();
object.traverse( function ( child ) {
if ( child.isMesh ) {
child.castShadow = true;
child.receiveShadow = true;
if ( child.morphTargetDictionary ) {
guiMorphsFolder.show();
const meshFolder = guiMorphsFolder.addFolder( child.name || child.uuid );
Object.keys( child.morphTargetDictionary ).forEach( ( key ) => {
meshFolder.add( child.morphTargetInfluences, child.morphTargetDictionary[ key ], 0, 1, 0.01 );
} );
}
}
} );
scene.add( object );
} );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
//
function animate() {
const delta = clock.getDelta();
if ( mixer ) mixer.update( delta );
renderer.render( scene, camera );
stats.update();
}
</script>
</body>
</html>
@@ -31,7 +31,8 @@
"/helpers/validators",
"/helpers/activeAttributes",
"/helpers/helpers3D.module",
"/helpers/formBuilder"
"/helpers/formBuilder",
"/helpers/kfConsole"
],
"assets": {
"styles": [
+56 -12
View File
@@ -1,32 +1,77 @@
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.
* All of this should not be a helper, but inherited this from EICDomContent, but not my framework anymore.
* @category MyEic
*/
app.helpers.activeAttributes = {
/**
* setupTriggers adds all click (data-trigger) and change (data-change) handlers.
* handlers should have the signatue : onXyz(component, event), will spit a warning if the handler doesn't exist.
* setupTriggers is re-entrant: it can be called again after refreshing part of the view
* @param {eicui-components []} components : the view's components (usually result of ui.eicfy(this.el) )
*/
setupTriggers(components){ // Should inherit this from WindozDomContent, but not my framework anymore.
for(let component of components.filter(component => component.el.hasAttribute('data-trigger'))) {
_triggersRegister: { 'click': new WeakMap(), 'change': new WeakMap()},
setupTriggers(components = []){ // Should inherit this from EICDomContent, but not my framework anymore.
for(let component of components.filter(component => component.el.hasAttribute('data-trigger'))) { //components with or without click property
if(typeof this[component.el.dataset.trigger] !== 'function') {
console.warn(`data-trigger without corresponding method : ${component.el.dataset.trigger}`)
continue
}
component.click = this[component.el.dataset.trigger].bind(this, component)
if(component.click) component.click = this[component.el.dataset.trigger].bind(this, component)
else {
const oldTrigger = this._triggersRegister.click.get(component.el)
if(oldTrigger) component.el.removeEventListener('click', oldTrigger)
const newTrigger = this[component.el.dataset.trigger].bind(this, component)
this._triggersRegister.click.set(component.el, newTrigger)
component.el.addEventListener('click', newTrigger)
}
for(let component of components.filter(component => component.el.hasAttribute('data-change'))) {
if(typeof this[component.el.dataset.change] !== 'function') {
console.warn(`data-change without corresponding method : ${component.el.dataset.trigger}`)
}
if(this.el){ // for views and other content-based classes, add triggers on simple non-component elements
for(const el of this.el.querySelectorAll('[data-trigger]:not([data-eicui-id])')){
if(typeof this[el.dataset.trigger] !== 'function') {
console.warn(`data-trigger without corresponding method : ${el.dataset.trigger}`)
continue
}
component.el.addEventListener("change",this[component.el.dataset.change].bind(this, component))
if(component.el.type=='text') component.el.addEventListener("keyup",this[component.el.dataset.change].bind(this, component))
const oldTrigger = this._triggersRegister.click.get(el)
if(oldTrigger) el.removeEventListener('click', oldTrigger)
const newTrigger = this[el.dataset.trigger].bind(this)
this._triggersRegister.click.set(el, newTrigger)
el.addEventListener('click', newTrigger)
}
}
for(let component of components.filter(component => component.el.hasAttribute('data-change'))) { //components with or without click property
if(typeof this[component.el.dataset.change] !== 'function') {
console.warn(`data-change without corresponding method : ${component.el.dataset.change}`)
continue
}
const oldTrigger = this._triggersRegister.change.get(component.el)
if(oldTrigger) {
component.el.removeEventListener('change', oldTrigger)
component.el.removeEventListener('keyup', oldTrigger)
}
const newTrigger = this[component.el.dataset.change].bind(this, component)
this._triggersRegister.change.set(component.el, newTrigger)
component.el.addEventListener("change", newTrigger)
if(component.el.type=='text') component.el.addEventListener("keyup", newTrigger)
}
if(this.el){ // for views and other content-based classes, add triggers on simple non-component elements
for(const el of this.el.querySelectorAll('[data-change]:not([data-eicui-id])')){
if(typeof this[el.dataset.change] !== 'function') {
console.warn(`data-change without corresponding method : ${el.dataset.change}`)
continue
}
const oldTrigger = this._triggersRegister.change.get(el)
if(oldTrigger) {
el.removeEventListener('change', oldTrigger)
el.removeEventListener('keyup', oldTrigger)
}
const newTrigger = this[el.dataset.change].bind(this)
this._triggersRegister.change.set(el, newTrigger)
el.addEventListener('change', newTrigger)
if(el.type=='text') el.addEventListener('keyup', newTrigger)
}
}
},
@@ -39,7 +84,7 @@ app.helpers.activeAttributes = {
* setupRefs is re-entrant: it can be called again after refreshing part of the view
* @param {eicui-components []} components : the view's components (usually result of ui.eicfy(this.el) )
*/
setupRefs(components = []){
setupRefs(components=[]){
if(!this.components) this.components = {}
for(let component of components.filter(component => component.el.hasAttribute('data-ref'))) {
this.components[component.el.dataset.ref] = component
@@ -61,7 +106,6 @@ app.helpers.activeAttributes = {
}
},
/**
* output (singular) : this.output('mydiv', '<p>Some markup</p>') places markup in a data-output node
* @param {*} name
+70
View File
@@ -0,0 +1,70 @@
import { PDBLoader } from 'three/examples/jsm/loaders/PDBLoader.js'
async function loadMolecule(url) {
const loader = new PDBLoader()
return new Promise((resolve, reject) => {
loader.load(
url,
pdb => {
// pdb contient:
// pdb.geometryAtoms : BufferGeometry des atomes
// pdb.geometryBonds : BufferGeometry des liaisons
// pdb.json : infos (numéro atomique, couleurs…)
const group = new THREE.Group()
// --- Atomes ---
const atomsGeo = pdb.geometryAtoms
const atomsInfo = pdb.json.atoms
for (let i = 0; i < atomsInfo.length; i++) {
const atom = atomsInfo[i]
const position = new THREE.Vector3(
atomsGeo.attributes.position.getX(i),
atomsGeo.attributes.position.getY(i),
atomsGeo.attributes.position.getZ(i)
)
const color = new THREE.Color(atom.color)
const radius = atom.radius
const sphere = new THREE.Mesh(
new THREE.SphereGeometry(radius, 32, 16),
new THREE.MeshPhongMaterial({ color })
)
sphere.position.copy(position)
group.add(sphere)
}
// --- Liaisons ---
const bondsGeo = pdb.geometryBonds
for (let i = 0; i < bondsGeo.attributes.position.count; i += 2) {
const start = new THREE.Vector3().fromBufferAttribute(bondsGeo.attributes.position, i)
const end = new THREE.Vector3().fromBufferAttribute(bondsGeo.attributes.position, i + 1)
const bondLength = start.distanceTo(end)
const cyl = new THREE.Mesh(
new THREE.CylinderGeometry(0.1, 0.1, bondLength, 16),
new THREE.MeshPhongMaterial({ color: 0xffffff })
)
// Orienter et positionner le cylindre
cyl.position.copy(start).lerp(end, 0.5)
cyl.lookAt(end)
cyl.rotateX(Math.PI / 2)
group.add(cyl)
}
resolve(group)
},
undefined,
reject
)
})
}
+87
View File
@@ -0,0 +1,87 @@
if(!app.helpers) app.helpers = {}
app.helpers.kfConsole = {
async execCommand(event){
if(this.outputs.commands.value.trim()=='\\help'){
this.outputs.commands.value = ''
this.outputs.results.innerHTML += await app.Assets.loadHtml({ name: 'help/KFconsoleHelp.html' })
this.setupTriggers()
} else if(this.outputs.commands.value.trim()=='\\clear'){
this.outputs.commands.value = ''
this.outputs.results.innerHTML = ''
} else {
this.outputs.results.innerHTML += await this.evalCmd(this.outputs.commands.value)
}
const lines = this.outputs.results.querySelectorAll('div.line')
if(lines.length > 100) {
for(let i=0; i<(lines.length-100); i++) lines[i].remove()
}
this.outputs.results.scrollTo({ top: this.outputs.results.scrollHeight, behavior: 'smooth' })
},
onSnippet(evt){
const snippetName = evt.target.dataset.snippet
const codeDiv = this.outputs.results.querySelector(`div.snippet[data-snippet="${snippetName}"]`)
if(codeDiv){ this.outputs.commands.value = codeDiv.textContent }
},
async evalCmd(code){
const api = {
log: (...args) => {
const res=[]
for(const arg of args){
if(typeof(arg)=='string') res.push(arg)
else res.push(JSON.stringify(arg))
}
return(res)
},
createAgent: async (type, properties) => {
if(Array.from(this.outputs.agentsSelector.options).find(item => item.value==type)){
const defaultValues = await this.models.agents.getDefaultProps(type)
console.log('==deflt===>', defaultValues,)
return(await this.newAgent(type, { ...defaultValues, ...properties })) //TODO: deepMerge
} else {
throw(`Invalid agent type: ${type}`)
}
},
removeAgent: (aid) => {
},
updateAgent: (aid, properties) => {
},
listagentTypes: () => {
},
}
try {
const fn = new Function(...Object.keys(api), `
return(
(async () => {
const logs = []
const log = (...args) => {
for(const arg of args){
if(typeof(arg)=='string') logs.push(arg)
else logs.push(JSON.stringify(arg))
}
}
${code}
return(logs)
})()
)
`)
const res = await fn(...Object.values(api))
return(
'<div class="line">'+
res.map(item => {
if(typeof(item) == 'object') return(JSON.stringify(item))
return(item)
}).join('</div><div class="line">')
+'</div>'
)
} catch (err) {
return(`<div class="error">${err.name}: ${err.message}</div>`)
}
},
}
+17 -2
View File
@@ -3,6 +3,7 @@ class AgentsModel extends WindozModel {
constructor() {
super()
this.ressource = '/agents'
this.agentProps = {}
}
async getTypes(family) {
@@ -23,13 +24,27 @@ class AgentsModel extends WindozModel {
)
}
async getProperties(id) {
async getProperties(id, force=false) {
if((!(id in this.agentProps)) || force){
let endpoint = {...app.config.api[this.ressource].getProperties}
endpoint.uri = endpoint.uri.replace('{id}', id)
return (
this.request(endpoint.uri, endpoint.method)
.then( async serverData => serverData.payload.agentProperties)
.then( async serverData => {
this.agentProps[id] = serverData.payload.agentProperties.atp_props
return(serverData.payload.agentProperties.atp_props)
})
)
} else {
return(this.agentProps[id])
}
}
async getDefaultProps(id){
const aprops = await this.getProperties(id)
const defaults={ position: { x:0, y:0, z:0 }, speed: { x:0, y:0, z:0 }}
for(const p in aprops) defaults[p] = aprops[p].default
return(defaults)
}
}
+6
View File
@@ -634,6 +634,12 @@ body[eicapp] {
border-radius: 10px;
box-shadow: 0 0 25px rgb(255, 255, 255);
border: 1px solid white;
transform: translateX(150%);
animation: growlerSlideIn 0.3s ease forwards;
}
@keyframes growlerSlideIn {
from { transform: translateX(150%); }
to { transform: translateX(0); }
}
[eicapp] .eic-growler [eicalert][danger],
[eicapp] .eic-growler [eicalert][danger]:after,
+21 -66
View File
@@ -2,8 +2,7 @@ class KeyframeView extends WindozDomContent {
constructor() {
super()
Object.assign(this, app.helpers.activeAttributes)
Object.assign(this, app.helpers.formBuilder)
Object.assign(this, app.helpers.activeAttributes, app.helpers.formBuilder, app.helpers.kfConsole)
}
DOMContentFocused(options) {
@@ -65,67 +64,6 @@ class KeyframeView extends WindozDomContent {
this.currentlySelectedAid = null
}
async execCommand(event){
console.log('cmd:', this.outputs.commands)
if(this.outputs.commands.value.trim()=='\\help'){
this.outputs.results.innerHTML += await app.Assets.loadHtml({ name: 'help/KFconsoleHelp.html' })
} else if(this.outputs.commands.value.trim()=='\\clear'){
this.outputs.results.innerHTML = ''
} else {
this.outputs.results.innerHTML += await this.evalCmd(this.outputs.commands.value)
}
const lines = this.outputs.results.querySelectorAll('div.line')
if(lines.length > 100) {
for(let i=0; i<(lines.length-100); i++) lines[i].remove()
}
}
async evalCmd(code){
const api = {
newAgent: async (type, properties) => {
if(Array.from(this.outputs.agentsSelector.options).find(item => item.value==type)){
this.outputs.agentsSelector.value = type
await this.onChangeAgent()
const defaultValues = this.getFieldsValues('div[data-output="agentProperties"]')
return(await this.newAgent(type, { ...defaultValues, ...properties })) //TODO: deepMerge
} else {
throw(`Invalid agent type: ${type}`)
}
},
removeAgent: (aid) => {
},
updateAgent: (aid, properties) => {
},
}
try {
const fn = new Function(...Object.keys(api), `
return(
(async () => {
const logs = []
const log = (item) => { logs.push(item) }
${code}
return(logs)
})()
)
`)
const res = await fn(...Object.values(api))
return(
'<div class="line">'+
res.map(item => {
if(typeof(item) == 'object') return(JSON.stringify(item))
return(item)
}).join('</div><div class="line">')
+'</div>'
)
} catch (err) {
return(`<div class="error">${err.name}: ${err.message}</div>`)
}
}
async onChangeAgent(event){
if(this.outputs.agentsSelector.value) this.agentPreview.setAgent(this.outputs.agentsSelector.value)
if(!this.outputs.agentsSelector.value) return
@@ -134,7 +72,7 @@ class KeyframeView extends WindozDomContent {
} else {
this.currentAgentType = await this.models.agents.getProperties(this.outputs.agentsSelector.value)
this.fillAgentProperties('', this.currentAgentType.atp_props)
this.fillAgentProperties('', this.currentAgentType)
// Deselect any on-scene selection
if(this.currentlySelectedAid){
this.kfArena.clearHighlight3DObj(this.kfArena.scene.getObjectByName(this.currentlySelectedAid), this.kfArena.scene)
@@ -191,9 +129,10 @@ class KeyframeView extends WindozDomContent {
this.kfArena.removeAgent(this.currentlySelectedAid)
}
newAgent(aType, AgentValues){
async newAgent(aType, AgentValues){
const aid = crypto.randomUUIDv7()
this.kfArena.addAgent(aType, aid, this.currentAgentType.atp_props , AgentValues)
const agentProps = await this.models.agents.getProperties(aType)
this.kfArena.addAgent(aType, aid, agentProps, AgentValues)
return(aid)
}
@@ -291,3 +230,19 @@ class KeyframeView extends WindozDomContent {
app.registerClass('KeyframeView', KeyframeView)
//TODO :
/*
API update
API remove
API listAgentTypes
if unsavec changes in scene => confirm before reloading
Bugs
=> reselect same scene resets it
=> Added with API = non-selectable
=> loaded from KF => loosing internal props
*/
+11 -8
View File
@@ -1,6 +1,7 @@
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import * as TWEEN from 'three/examples/jsm/libs/tween.module.js'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
export class kfArena{
@@ -30,10 +31,15 @@ export class kfArena{
this.camera.layers.enable(2)
// Lights
const light = new THREE.DirectionalLight(0xffffff, 1)
light.position.set(5, 5, 5)
this.scene.add(light)
this.scene.add(new THREE.AmbientLight(0xffffff, .4))
const dLight1 = new THREE.DirectionalLight(0xffffff, 1.5)
dLight1.position.set(5, 5, 5)
this.scene.add(dLight1)
const dLight2 = new THREE.DirectionalLight(0xffffff, 1.5)
dLight2.position.set(-5, -5, 5)
this.scene.add(dLight2)
this.scene.add(new THREE.AmbientLight(0xffffff, 1))
this.grid = new THREE.GridHelper(this.sceneSize.x, this.sceneSize.x, 0x8888AA, 0x8888AA)
this.grid.layers.set(1)
@@ -57,10 +63,6 @@ export class kfArena{
this.scene.add(this.axes)
this.renderer = new THREE.WebGLRenderer({ antialias: true, canvas: this.canvasEl, stencil: true })
// this.renderer.physicallyCorrectLights = true
// this.renderer.outputColorSpace = THREE.SRGBColorSpace
// this.renderer.toneMapping = THREE.ACESFilmicToneMapping
// this.renderer.toneMappingExposure = 1
this.canvasEl.addEventListener('click', this.onSceneClick.bind(this))
}
@@ -74,6 +76,7 @@ export class kfArena{
TWEEN.update()
this.animateHighlight3DObj()
const resized = this.resizeRendererToDisplaySize()
this.renderer.render(this.scene, this.camera)
requestAnimationFrame(this.render.bind(this))
}
+2 -1
View File
@@ -10,8 +10,9 @@
"imports": {
"three": "https://unpkg.com/three@0.160.0/build/three.module.js",
"three/examples/jsm/loaders/FontLoader.js": "https://unpkg.com/three@0.160.0/examples/jsm/loaders/FontLoader.js",
"three/examples/jsm/geometries/TextGeometry.js": "https://unpkg.com/three@0.160.0/examples/jsm/geometries/TextGeometry.js",
"three/examples/jsm/loaders/GLTFLoader.js": "https://unpkg.com/three@0.160.0/examples/jsm/loaders/GLTFLoader.js",
"three/examples/jsm/loaders/RGBELoader.js": "https://unpkg.com/three@0.160.0/examples/jsm/loaders/RGBELoader.js",
"three/examples/jsm/geometries/TextGeometry.js": "https://unpkg.com/three@0.160.0/examples/jsm/geometries/TextGeometry.js",
"three/examples/jsm/controls/OrbitControls.js": "https://unpkg.com/three@0.160.0/examples/jsm/controls/OrbitControls.js",
"three/examples/jsm/libs/tween.module.js": "https://unpkg.com/three@0.160.0/examples/jsm/libs/tween.module.js"
}