From 66916e55fc97090a8aeb4f4aa0be1ccf4cd0917a Mon Sep 17 00:00:00 2001 From: "Morgan 'ARR\\!' Allen" Date: Fri, 26 May 2023 10:33:36 -0700 Subject: [PATCH] init and that it --- README.md | 8 +++ ThreeBaseGeometry.js | 77 +++++++++++++++++++++++++++ ThreeCube.js | 20 +++++++ ThreeGroup.js | 29 +++++++++++ ThreeLight.js | 37 +++++++++++++ ThreeRendererElement.js | 113 ++++++++++++++++++++++++++++++++++++++++ ThreeScene.js | 34 ++++++++++++ ThreeSphere.js | 18 +++++++ index.html | 56 ++++++++++++++++++++ 9 files changed, 392 insertions(+) create mode 100644 README.md create mode 100644 ThreeBaseGeometry.js create mode 100644 ThreeCube.js create mode 100644 ThreeGroup.js create mode 100644 ThreeLight.js create mode 100644 ThreeRendererElement.js create mode 100644 ThreeScene.js create mode 100644 ThreeSphere.js create mode 100644 index.html 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 @@ + + + + + + + + + + + + + + + + + + + + + + +