commit 66916e55fc97090a8aeb4f4aa0be1ccf4cd0917a Author: Morgan 'ARR\!' Allen Date: Fri May 26 10:33:36 2023 -0700 init and that it diff --git a/README.md b/README.md new file mode 100644 index 0000000..4ebe8d4 --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +A short experiment on Parent/Child interaction with Web Components. Creates Web Components for various ThreeJS base classes + +Renderer +Scene +Group +Light +BoxGeometry +SphereGeometry diff --git a/ThreeBaseGeometry.js b/ThreeBaseGeometry.js new file mode 100644 index 0000000..32ad818 --- /dev/null +++ b/ThreeBaseGeometry.js @@ -0,0 +1,77 @@ +import * as THREE from 'https://esm.sh/three'; + +const attrs_dimensions = [ 'height', 'width', 'depth' ]; +const attrs_position = [ 'x', 'y', 'z' ]; +const attrs_rotation = [ 'rx', 'ry', 'rz' ]; + +export class BaseGeometryElement extends HTMLElement { + constructor() { + super(); + + var attrs = BaseGeometryElement.observedAttributes; + + for(let i in attrs) { + Object.defineProperty(this, attrs[i], { + get: function() { + return this.getAttribute(attrs[i]); + }, + set: function(v) { + return this.setAttribute(attrs[i], v); + } + }); + } + } + + static get observedAttributes() { + return [ + 'color', + 'height', 'width', 'depth', + 'x', 'y', 'z', + 'rx', 'ry', 'rz', + ] + } + + parentO3DCallback() {} + + connectedCallback() { + var parentEl = this.parentElement.closest('three-group, three-scene'); + + if(!parentEl.ready) { + var self = this; + + parentEl.addEventListener('ready', function() { + var o3d = parentEl.nodeName == "THREE-GROUP" ? parentEl.group : parentEl.scene; + self.parentO3DCallback(o3d); + + self.ready = true; + self.dispatchEvent(new CustomEvent("ready")); + }); + } else { + var o3d = parentEl.nodeName == "THREE-GROUP" ? parentEl.group : parentEl.scene; + this.parentO3DCallback(o3d); + this.ready = true; + this.dispatchEvent(new CustomEvent("ready")); + } + } + + attributeChangedCallback(name, old_value, value) { + if(!this.ready) return; + + var obj = {}; + + if(attrs_dimensions.includes(name)) { + obj = this.geometry[name]; + } + + if(attrs_position.includes(name)) { + obj = this.mesh.position[name]; + } + + if(attrs_rotation.includes(name)) { + console.log(name); + obj = this.mesh.rotation[name]; + } + + obj[name] = value; + } +} diff --git a/ThreeCube.js b/ThreeCube.js new file mode 100644 index 0000000..702cab8 --- /dev/null +++ b/ThreeCube.js @@ -0,0 +1,20 @@ +import * as THREE from 'https://esm.sh/three'; +import { BaseGeometryElement } from './ThreeBaseGeometry.js'; + +const attrs_position = [ 'x', 'y', 'z' ]; +const attrs_dimensions = [ 'height', 'width', 'depth' ]; + +export class CubeElement extends BaseGeometryElement { + parentO3DCallback(parent) { + var scene = this.closest('three-scene'); + + this.geometry = new THREE.BoxGeometry(this.width, this.height, this.depth); // width, height, depth + this.material = new THREE.MeshLambertMaterial({ color: this.color }); + this.mesh = new THREE.Mesh(this.geometry, this.material); + this.mesh.position.set(this.x, this.y, this.z); // Optional, 0,0,0 is the default + + parent.add(this.mesh); + } +} + +customElements.define('three-cube', CubeElement); diff --git a/ThreeGroup.js b/ThreeGroup.js new file mode 100644 index 0000000..1d869e4 --- /dev/null +++ b/ThreeGroup.js @@ -0,0 +1,29 @@ +import * as THREE from 'https://esm.sh/three'; +import { BaseGeometryElement } from './ThreeBaseGeometry.js'; + +export class GroupElement extends BaseGeometryElement { + constructor() { + super(); + } + + parentO3DCallback(parent) { + this.group = new THREE.Group(); + this.group.position.set(this.getAttribute('x'), this.getAttribute('y'), this.getAttribute('z')); + this.group.rotation.set(this.getAttribute('rx'), this.getAttribute('ry'), this.getAttribute('rz')); + + parent.add(this.group); + } + + attributeChangedCallback(name, old, value) { + if(this.group && [ 'x', 'y', 'z' ].includes(name)) { + this.group.position[name] = parseInt(value); + } + + console.log(this.group, name); + if(this.group && [ 'rx', 'ry', 'rz' ].includes(name)) { + this.group.rotation[name] = parseInt(value); + } + } +} + +customElements.define('three-group', GroupElement); diff --git a/ThreeLight.js b/ThreeLight.js new file mode 100644 index 0000000..e86dc32 --- /dev/null +++ b/ThreeLight.js @@ -0,0 +1,37 @@ +import * as THREE from 'https://esm.sh/three'; +import { ThreeRendererElement } from './ThreeRendererElement.js'; + +export class LightElement extends HTMLElement { + constructor() { + super(); + + this.light = new THREE.DirectionalLight(this.getAttribute('color') || 0xfff, 0.5); + } + + static get observedAttributes() { + return [ "color" ]; + } + + attributeChangedCallback(name, old_value, value) { + if(name == 'color') { + this.light.color = new THREE.Color(value); + } + } + + connectedCallback() { + var scene = this.closest('three-scene'); + + if(!scene.ready) { + var self = this; + + scene.addEventListener('ready', function() { + scene.scene.add(self.light); + }) + } else { + scene.scene.add(this.light); + } + this.ready = true; + } +} + +customElements.define('three-light', LightElement); diff --git a/ThreeRendererElement.js b/ThreeRendererElement.js new file mode 100644 index 0000000..2cc02ae --- /dev/null +++ b/ThreeRendererElement.js @@ -0,0 +1,113 @@ +import * as THREE from 'https://esm.sh/three'; + +THREE.Object3D.DEFAULT_UP.set(0,0,1); + +export class ThreeRendererElement extends HTMLCanvasElement { + constructor() { + super(); + + var parent_boundingbox = this.parentElement.getBoundingClientRect(); + this.height = parent_boundingbox.height; + this.width = parent_boundingbox.width; + + this.camera_persp = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 ); + //this.camera_ortho = new THREE.OrthographicCamera( this.width / - 2, this.width / 2, this.height / 2, this.height / - 2, 1, 1000 ); + + this.camera = this.camera_persp; + this.camera.position.x = 0; + this.camera.position.y = 0; + this.camera.position.z = 0; + + /* + var light = this.light = new THREE.AmbientLight(0xab1f2c, 1); + this.scene.add(light); + + var point_light = this.point_light = new THREE.DirectionalLight(0xffffff, 0.5); + this.point_light.position.set(0, 0, 10); + this.scene.add(point_light); + */ + + var renderer = this.renderer = new THREE.WebGLRenderer({ + canvas: this + }); + var bb = this.parentElement.getBoundingClientRect(); + this.renderer.setSize(bb.width, bb.height); + + + /* + // ground plane + const planeSize = 40; + const loader = new THREE.TextureLoader(); + const texture = loader.load('checker.png'); + texture.wrapS = THREE.RepeatWrapping; + texture.wrapT = THREE.RepeatWrapping; + texture.magFilter = THREE.NearestFilter; + const repeats = planeSize / 2; + texture.repeat.set(repeats, repeats); + + const planeGeo = new THREE.PlaneGeometry(planeSize, planeSize); + const planeMat = new THREE.MeshPhongMaterial({ + map: texture, + side: THREE.DoubleSide, + }); + const plane_mesh = new THREE.Mesh(planeGeo, planeMat); + window.plane_mesh = plane_mesh; + //plane_mesh.rotation.x = Math.PI * -.5; + this.scene.add(plane_mesh); + */ + + // XXX applWHY? + this.do_animate = this.do_animate.bind(this); + this.do_animate(); + + // TODO improve value checking, current src="" will be true + + /* + this.childObserver = new MutationObserver(function(changes) { + }) + + this.childObserver.observe(this, { + attributes: true, + childList: true, + subtree: true + }); + */ + } + + static get observedAttributes() { + return [ ]; + } + + do_animate() { + // TODO handle pausing + requestAnimationFrame(this.do_animate); + + if(this.active_scene) { + this.renderer.render(this.active_scene, this.camera); + } + } + + attributeChangedCallback(name, old_value, value) { + } + + parsedCallack() { + console.log('parsedCallack'); + } + + setupRenderer() { + this.scenes = this.querySelectorAll('three-scene'); + this.active_scene = this.scenes[this.scenes.length - 1].scene; + } + + connectedCallback() { + if(document.readyState != "complete") { + document.addEventListener("DOMContentLoaded", this.setupRenderer.bind(this)); + } + + this.dispatchEvent(new CustomEvent("renderer-ready", { + cancelable: false + })); + } +} + +customElements.define('three-renderer', ThreeRendererElement, { extends: 'canvas' }); diff --git a/ThreeScene.js b/ThreeScene.js new file mode 100644 index 0000000..335b50d --- /dev/null +++ b/ThreeScene.js @@ -0,0 +1,34 @@ +import * as THREE from 'https://esm.sh/three'; +import { ThreeRendererElement } from './ThreeRendererElement.js'; + +export class SceneElement extends HTMLElement { + constructor() { + super(); + + } + + rendererConnected() { + console.log('fjlkjsdflksjdf'); + } + + findRenderer() { + var p = this; + while((p = p.parentElement) !== undefined) + if(p instanceof ThreeRendererElement) return p; + } + + connectedCallback() { + this.scene = new THREE.Scene(); + this.scene.background = new THREE.Color(this.getAttribute('background-color') || 0xababab); + if(!(this.parentElement instanceof ThreeRendererElement)) { + throw Error("Parent not ThreeRendererElement"); + } + + this.parentElement.addEventListener("connected", this.rendererConnected.bind(this)); + + this.dispatchEvent(new CustomEvent('ready')); + this.ready = true; + } +} + +customElements.define('three-scene', SceneElement); diff --git a/ThreeSphere.js b/ThreeSphere.js new file mode 100644 index 0000000..b13f5bf --- /dev/null +++ b/ThreeSphere.js @@ -0,0 +1,18 @@ +import * as THREE from 'https://esm.sh/three'; +import { BaseGeometryElement } from './ThreeBaseGeometry.js'; + +const attrs_position = [ 'x', 'y', 'z' ]; +const attrs_dimensions = [ 'height', 'width', 'depth' ]; + +export class SphereElement extends BaseGeometryElement { + parentO3DCallback(parent) { + this.geometry = new THREE.SphereGeometry(this.getAttribute('radius')); // width, height, depth + this.material = new THREE.MeshLambertMaterial({ color: this.color }); + this.mesh = new THREE.Mesh(this.geometry, this.material); + this.mesh.position.set(this.x, this.y, this.z); // Optional, 0,0,0 is the default + + parent.add(this.mesh); + } +} + +customElements.define('three-sphere', SphereElement); diff --git a/index.html b/index.html new file mode 100644 index 0000000..825df17 --- /dev/null +++ b/index.html @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + +