import { Color, Material, Mesh } from "three"; import { WaitForSeconds } from "../engine/engine_coroutine.js"; import { RoomEvents } from "../engine/engine_networking.js"; import { PlayerState } from "../engine-components-experimental/networking/PlayerSync.js"; import { Behaviour, GameObject } from "./Component.js"; import { AvatarMarker } from "./webxr/WebXRAvatar.js"; /** * PlayerColor assigns a unique color for each user in the room to the object it is attached to. * The color is generated based on the user's ID. * @category Networking * @group Components */ export class PlayerColor extends Behaviour { private _didAssignPlayerColor: boolean = false; onEnable(): void { this.context.connection.beginListen(RoomEvents.JoinedRoom, this.tryAssignColor); if (!this._didAssignPlayerColor) this.startCoroutine(this.waitForConnection()); } onDisable(): void { this.context.connection.stopListen(RoomEvents.JoinedRoom, this.tryAssignColor); } private *waitForConnection() { while (!this.destroyed && this.activeAndEnabled) { yield WaitForSeconds(.2); if (this.tryAssignColor()) break; } } private tryAssignColor = () => { const marker = GameObject.getComponentInParent(this.gameObject, PlayerState); if (marker && marker.owner) { this._didAssignPlayerColor = true; this.assignUserColor(marker.owner); return true; } const avatar = GameObject.getComponentInParent(this.gameObject, AvatarMarker); if (avatar?.connectionId) { this._didAssignPlayerColor = true; this.assignUserColor(avatar.connectionId); return true; } return false; } assignUserColor(id: string) { // console.log(this.name, id, this); const hash = PlayerColor.hashCode(id); const color = PlayerColor.colorFromHashCode(hash); if (this.gameObject.type === "Mesh") { const mesh: Mesh = this.gameObject as any; this.assignColor(color, id, mesh); } else if (this.gameObject.children) { for (const ch of this.gameObject.children) { const obj = ch as any; if (obj.material && obj.material.color) { this.assignColor(color, id, obj); } } } } private assignColor(col: Color, id: string, mesh: Mesh) { let mat = mesh.material as Material; if (!mat) return; if (mat["_playerMaterial"] !== id) { // console.log("ORIG", mat); mat = mat.clone(); mat["_playerMaterial"] = id; mesh.material = mat; // console.log("CLONE", mat); } // else console.log("DONT CLONE", mat); mat["color"] = col; } public static hashCode(str: string) { var hash = 0, i, chr; if (str.length === 0) return hash; for (i = 0; i < str.length; i++) { chr = str.charCodeAt(i); hash = ((hash << 5) - hash) + chr; hash |= 0; // Convert to 32bit integer } return hash; }; public static colorFromHashCode(hash: number) { const r = (hash & 0xFF0000) >> 16; const g = (hash & 0x00FF00) >> 8; const b = hash & 0x0000FF; return new Color(r / 255, g / 255, b / 255); } }