init and that it

This commit is contained in:
Morgan 'ARR\!' Allen 2023-05-26 10:33:36 -07:00
commit 66916e55fc
9 changed files with 392 additions and 0 deletions

8
README.md Normal file
View file

@ -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

77
ThreeBaseGeometry.js Normal file
View file

@ -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;
}
}

20
ThreeCube.js Normal file
View file

@ -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);

29
ThreeGroup.js Normal file
View file

@ -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);

37
ThreeLight.js Normal file
View file

@ -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);

113
ThreeRendererElement.js Normal file
View file

@ -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' });

34
ThreeScene.js Normal file
View file

@ -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);

18
ThreeSphere.js Normal file
View file

@ -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);

56
index.html Normal file
View file

@ -0,0 +1,56 @@
<!DOCTYPE html>
<html>
<head>
<script type="module">
import * as THREE from 'https://esm.sh/three';
import './ThreeRendererElement.js'
import './ThreeCube.js';
import './ThreeGroup.js';
import './ThreeLight.js';
import './ThreeScene.js';
import './ThreeSphere.js';
document.addEventListener('DOMContentLoaded', function() {
var group = document.querySelector('three-group');
var sphere = document.querySelector('three-sphere');
});
</script>
<style>
html, body {
height: 100%;
}
@keyframe position {
from {
x: 140;
}
to {
x: 0;
}
}
three-cube.left {
animation-duration: 3s;
animation-name: position;
x: 0;
}
</style>
</head>
<body>
<canvas is=three-renderer>
<three-scene name="main">
<three-light color="#ee00aa"></three-light>
<three-cube color="#ffffff" height=40 width=90 depth=10 x=0 y=40 z=-200 focus></three-cube>
<three-sphere color="#a0a00f" radius=30 x=0 y=40 z=-200 focus></three-sphere>
<three-group x=35 y=20 rz=30>
<three-cube color="#ffffff" height=40 width=90 depth=10 x=0 y=40 z=-200 focus></three-cube>
<three-sphere color="#a0a00f" radius=30 x=0 y=40 z=-200 focus></three-sphere>
</three-group>
</three-scene>
</canvas>
</body>
</html>