[READ-ONLY] Mirror of https://github.com/flo-bit/tiny-creature. three.js journey #16 - tamagotchi flo-bit.dev/tiny-creature/
0

Configure Feed

Select the types of activity you want to include in your feed.

add room

Florian (Feb 3, 2025, 4:12 PM +0100) fded7c87 a634776d

+181 -19
public/room.glb

This is a binary file and will not be displayed.

+79 -11
src/main.ts
··· 10 10 11 11 const mouse = new THREE.Vector2(); 12 12 13 - // Create the camera 14 - const camera = new THREE.PerspectiveCamera( 15 - 75, // Field of view 16 - window.innerWidth / window.innerHeight, // Aspect ratio 17 - ); 13 + // // Create the camera 14 + // const camera = new THREE.PerspectiveCamera( 15 + // 75, // Field of view 16 + // window.innerWidth / window.innerHeight, // Aspect ratio 17 + // ); 18 18 19 - // Position the camera 20 - camera.position.z = 3; 19 + // // Position the camera 20 + // camera.position.z = 7; 21 + // camera.position.y = 7; 22 + // camera.position.x = -7; 23 + 24 + const s = 7; 25 + const width = window.innerWidth * 0.015; 26 + const height = window.innerHeight * 0.015; 27 + const camera = new THREE.OrthographicCamera( 28 + width / -2, 29 + width / 2, 30 + height / 2, 31 + height / -2, 32 + 0.1, 33 + 100, 34 + ); 35 + camera.position.z = 7; 36 + camera.position.y = 7; 37 + camera.position.x = -7; 38 + 39 + // look at the origin 40 + camera.lookAt(0, 0, 0); 21 41 22 42 // Set up the renderer 23 43 const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); ··· 46 66 let rootBone: THREE.Bone; 47 67 const wiggleBones: WiggleBone[] = []; 48 68 69 + let creature: THREE.Group; 70 + 49 71 loader.load("/tiny-creature/model.glb", ({ scene: obj }) => { 50 72 const mesh = obj.getObjectByName("Icosphere"); 51 73 if (!mesh) return; 74 + 75 + creature = obj; 52 76 53 77 if (mesh instanceof THREE.Mesh) { 54 78 mesh.material = new THREE.MeshStandardMaterial({ ··· 56 80 }); 57 81 } 58 82 83 + // obj.rotation.y = -Math.PI / 2; 84 + obj.lookAt(camera.position); 85 + 59 86 if (mesh instanceof THREE.SkinnedMesh) { 60 87 mesh.skeleton.bones.forEach((bone) => { 61 88 if (!(bone.parent instanceof THREE.Bone) || !bone.parent?.isBone) { 62 89 rootBone = bone; 63 90 64 - rootBone.rotation.y = -Math.PI / 2; 91 + // rootBone.rotation.x = Math.PI / 2; 65 92 } else { 66 93 const wiggleBone = new WiggleBone(bone, { 67 94 velocity: 0.4, ··· 74 101 scene.add(obj); 75 102 }); 76 103 77 - const light = new THREE.DirectionalLight(0xffffff, 1); 104 + loader.load("/tiny-creature/room.glb", ({ scene: obj }) => { 105 + scene.add(obj); 106 + console.log(obj); 107 + // find mesh named Cube 108 + const mesh = obj.getObjectByName("Cube"); 109 + if (mesh instanceof THREE.Mesh) { 110 + mesh.material = new THREE.MeshStandardMaterial({ 111 + color: 0xb1b1b1, 112 + side: THREE.DoubleSide, 113 + }); 114 + } 115 + 116 + // find Light object 117 + const light = obj.getObjectByName("Light"); 118 + // add light there 119 + if (light) { 120 + const pointLight = new THREE.PointLight(0xffffff, 0.7); 121 + pointLight.position.copy(light.position); 122 + scene.add(pointLight); 123 + } 124 + 125 + // find Light object 126 + const light2 = obj.getObjectByName("Light2"); 127 + if (light2) { 128 + const pointLight = new THREE.PointLight(0xffffff, 0.4); 129 + pointLight.position.copy(light2.position); 130 + scene.add(pointLight); 131 + } 132 + }); 133 + 134 + const light = new THREE.DirectionalLight(0xffffff, 0.2); 78 135 light.position.set(10, 10, 10); 79 136 scene.add(light); 137 + 138 + const light2 = new THREE.DirectionalLight(0xffffff, 0.1); 139 + light2.position.set(-10, 10, -10); 140 + scene.add(light2); 80 141 81 142 const ambientLight = new THREE.AmbientLight(0xffffff, 0.2); 82 143 scene.add(ambientLight); ··· 92 153 updateFace(deltaTime, mouse.x, mouse.y); 93 154 94 155 if (rootBone) { 95 - rootBone.position.x = mouse.x * 2; 96 - rootBone.position.y = mouse.y * 2; 156 + creature.position.z = -mouse.x * 2; 157 + creature.position.x = mouse.y * 2; 158 + creature.position.y = 0.5; 159 + 160 + // look at camera 161 + creature.lookAt( 162 + new THREE.Vector3(camera.position.x, 0, camera.position.z), 163 + ); 164 + rootBone.rotation.y = -Math.PI / 2; 97 165 } 98 166 99 167 wiggleBones.forEach((wiggleBone) => {
+1 -1
src/face/eye.ts
··· 102 102 this.eyeContainer.mask = this.mask; 103 103 104 104 let eyebrowWidth = opts?.eyebrow?.width ?? 2; 105 - let eyebrowCurve = opts?.eyebrow?.curve ?? 0; 105 + let eyebrowCurve = opts?.eyebrow?.curve ?? -0.2; 106 106 107 107 const eyebrow = new PIXI.Graphics() 108 108 .moveTo((-eyebrowWidth / 2) * this.size, 0)
+11 -7
src/face/face.ts
··· 1 1 import { Application, Graphics, Renderer } from "pixi.js"; 2 2 import Eye from "./eye"; 3 + import Mouth from "./mouth"; 3 4 4 5 let leftEye: Eye; 5 6 let rightEye: Eye; 7 + 8 + let mouth: Mouth; 6 9 7 10 export async function createFaceTexture({ 8 11 width = 512, ··· 21 24 22 25 rightEye = new Eye({ 23 26 x: width * 0.5 + 75, 24 - y: height * 0.5, 27 + y: height * 0.5 + 100, 25 28 size: 50, 26 29 }); 27 30 28 31 leftEye = new Eye({ 29 32 x: width * 0.5 - 75, 30 - y: height * 0.5, 33 + y: height * 0.5 + 100, 31 34 size: 50, 32 35 }); 33 36 34 37 app.stage.addChild(rightEye.container); 35 38 app.stage.addChild(leftEye.container); 36 39 37 - // draw mouth 38 - const mouth = new Graphics(); 39 - mouth.rect(0, 0, 50, 10).fill(0x0); 40 - mouth.position.set(width * 0.5 - 25, height * 0.5 - 75); 41 - app.stage.addChild(mouth); 40 + mouth = new Mouth({ 41 + x: width * 0.5, 42 + y: height * 0.5 - 20, 43 + size: 50, 44 + }); 45 + app.stage.addChild(mouth.container); 42 46 43 47 return { 44 48 app,
+90
src/face/mouth.ts
··· 1 + import * as PIXI from "pixi.js"; 2 + import { AnimatedValue, degreesToRadians } from "./math-helper"; 3 + 4 + export type EyeOptions = { 5 + container: PIXI.Container; 6 + 7 + x: number; 8 + y: number; 9 + 10 + eyeColor: PIXI.ColorSource; 11 + pupilColor: PIXI.ColorSource; 12 + highlightColor: PIXI.ColorSource; 13 + 14 + size: number; 15 + 16 + eyebrow: Partial<{ 17 + length: number; 18 + 19 + width: number; 20 + 21 + y: number; 22 + x: number; 23 + 24 + curve: number; 25 + 26 + color: PIXI.ColorSource; 27 + }>; 28 + 29 + blinkStart: number; 30 + blinkTimer: number; 31 + }; 32 + 33 + export default class Mouth { 34 + eyeContainer: PIXI.Container; 35 + 36 + container: PIXI.Container; 37 + 38 + base?: PIXI.Graphics; 39 + 40 + _x: AnimatedValue; 41 + _y: AnimatedValue; 42 + 43 + dx: number = 0; 44 + dy: number = 0; 45 + 46 + size: number = 1; 47 + 48 + targetX: number = 0; 49 + targetY: number = 0; 50 + 51 + constructor(opts: Partial<EyeOptions>) { 52 + console.log("Eye", opts); 53 + this.container = new PIXI.Container(); 54 + 55 + this.eyeContainer = new PIXI.Container(); 56 + 57 + this.container.position.set(opts.x ?? 0, opts.y ?? 0); 58 + 59 + this.size = opts.size ?? 1; 60 + 61 + this._x = new AnimatedValue(); 62 + this._y = new AnimatedValue(); 63 + 64 + this.container.addChild(this.eyeContainer); 65 + 66 + if (opts.container) opts.container.addChild(this.container); 67 + 68 + let eyebrowWidth = opts?.eyebrow?.width ?? 2; 69 + let eyebrowCurve = opts?.eyebrow?.curve ?? -0.3; 70 + 71 + const eyebrow = new PIXI.Graphics() 72 + .moveTo((-eyebrowWidth / 2) * this.size, 0) 73 + .quadraticCurveTo( 74 + 0, 75 + eyebrowCurve * this.size, 76 + (eyebrowWidth / 2) * this.size, 77 + 0, 78 + ) 79 + .stroke({ 80 + color: opts.eyebrow?.color ?? 0, 81 + width: opts.eyebrow?.width ?? this.size * 0.2, 82 + }); 83 + eyebrow.position.set( 84 + opts.eyebrow?.x ?? 0, 85 + (opts.eyebrow?.y ?? 0.9) * this.size, 86 + ); 87 + 88 + this.container.addChild(eyebrow); 89 + } 90 + }