@needle-tools/engine
Version:
Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.
104 lines (90 loc) • 3.56 kB
text/typescript
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);
}
}