From 686f1c9123e9870edbf2e741c695448bb73803c7 Mon Sep 17 00:00:00 2001 From: Evert Prants Date: Mon, 11 Apr 2022 22:43:45 +0300 Subject: [PATCH] colorable eyes --- assets/eyes/eye-base.png | Bin 0 -> 23099 bytes assets/eyes/eye-mask.png | Bin 0 -> 18219 bytes src/client/game.ts | 33 +++++- src/client/object/other/eyes.ts | 173 +++++++++++++++++++++++++++++ src/client/object/other/texture.ts | 18 +++ src/client/object/pony.ts | 24 +++- src/common/types/packet.ts | 1 + 7 files changed, 240 insertions(+), 9 deletions(-) create mode 100644 assets/eyes/eye-base.png create mode 100644 assets/eyes/eye-mask.png create mode 100644 src/client/object/other/eyes.ts create mode 100644 src/client/object/other/texture.ts diff --git a/assets/eyes/eye-base.png b/assets/eyes/eye-base.png new file mode 100644 index 0000000000000000000000000000000000000000..35c4e05a6ec5f5ce475e2383d2d6c4423cad1944 GIT binary patch literal 23099 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 literal 0 HcmV?d00001 diff --git a/assets/eyes/eye-mask.png b/assets/eyes/eye-mask.png new file mode 100644 index 0000000000000000000000000000000000000000..b6c566a946e37c2dc28150b7153b4238689f94ff GIT binary patch literal 18219 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 A&Hw-a literal 0 HcmV?d00001 diff --git a/src/client/game.ts b/src/client/game.ts index a7405af..8124db5 100644 --- a/src/client/game.ts +++ b/src/client/game.ts @@ -7,6 +7,7 @@ import { ThirdPersonCamera } from './object/camera'; import { Chat } from './object/chat'; import { Joystick } from './object/joystick'; import { CubeMap } from './object/other/cubemap'; +import { PonyEyes } from './object/other/eyes'; import { VideoPlayer } from './object/other/video-player'; import { Player } from './object/player'; import { PlayerEntity } from './object/player-entity'; @@ -40,6 +41,7 @@ export class Game { const cube = await CubeMap.load('/assets/skybox/default'); await modelLoaderInstance.loadPonyModel(); + await PonyEyes.getInstance().initialize(); await this.world.initialize(); this.renderer.initialize(); @@ -57,9 +59,11 @@ export class Game { (item) => item, ); - const colorGet = localStorage.getItem('color'); - if (colorGet) { - this.character.color = colorGet; + const character = localStorage.getItem('character') + ? JSON.parse(localStorage.getItem('character')) + : {}; + if (character) { + this.character = character; } // end of @@ -254,9 +258,28 @@ export class Game { return; } + this.character = { ...this.character, color }; this.player.setColor(color); - this.socket.emit('character', { color }); - localStorage.setItem('color', color); + this.socket.emit('character', this.character); + localStorage.setItem('character', JSON.stringify(this.character)); + } + + if (message.startsWith('!eyecolor')) { + const [cmd, eyeColor] = message.split(' '); + try { + const colorr = new Color(eyeColor); + if (!colorr) { + throw 'invalid'; + } + } catch (e: any) { + this.chat.addMessage('Invalid color.'); + return; + } + + this.character = { ...this.character, eyeColor }; + this.player.setEyeColor(eyeColor); + this.socket.emit('character', this.character); + localStorage.setItem('character', JSON.stringify(this.character)); } if (message.startsWith('!party')) { diff --git a/src/client/object/other/eyes.ts b/src/client/object/other/eyes.ts new file mode 100644 index 0000000..18780d4 --- /dev/null +++ b/src/client/object/other/eyes.ts @@ -0,0 +1,173 @@ +import { Color, ShaderMaterial, UniformsLib, UniformsUtils } from 'three'; +import { BaseTexture } from './texture'; + +export const vertex = /* glsl */ ` +varying vec3 vViewPosition; +varying vec2 vUv; +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +void main() { + vUv = uv; + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + vViewPosition = - mvPosition.xyz; + #include + #include + #include + #include +} +`; + +export const fragment = /* glsl */ ` +uniform vec3 emissive; +uniform vec3 specular; +uniform float shininess; + +uniform sampler2D baseT; +uniform sampler2D maskT; +uniform vec3 colorMask; + +varying vec2 vUv; +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +void main() { + #include + + vec4 baseTex = texture2D(baseT, vUv); + vec4 maskTex = texture2D(maskT, vUv); + vec3 combine = mix(baseTex.rgb, colorMask, maskTex.r); + + vec4 diffuseColor = vec4( combine, 1.0 ); + ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); + vec3 totalEmissiveRadiance = emissive; + #include + #include + #include + #include + #include + #include + #include + #include + #include + // accumulation + #include + #include + #include + #include + // modulation + #include + vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance; + #include + #include + #include + #include + #include + #include + #include +} +`; + +let instance: PonyEyes; + +export class PonyEyes { + private base!: BaseTexture; + private mask!: BaseTexture; + public shader!: ShaderMaterial; + + async initialize() { + this.base = await BaseTexture.load('/assets/eyes/eye-base.png'); + this.mask = await BaseTexture.load('/assets/eyes/eye-mask.png'); + + this.base.texture.flipY = false; + this.mask.texture.flipY = false; + this.shader = new ShaderMaterial({ + vertexShader: vertex, + fragmentShader: fragment, + lights: true, + uniforms: UniformsUtils.merge([ + UniformsLib.common, + UniformsLib.specularmap, + UniformsLib.envmap, + UniformsLib.aomap, + UniformsLib.lightmap, + UniformsLib.emissivemap, + UniformsLib.fog, + UniformsLib.lights, + { + baseT: { value: this.base.texture }, + maskT: { value: this.mask.texture }, + colorMask: { value: new Color(0xff0000) }, + shininess: { value: 100 }, + }, + ]), + }); + } + + public getShader(): ShaderMaterial { + return this.shader.clone(); + } + + public setColor( + shader: ShaderMaterial, + color: number | string, + ): ShaderMaterial { + shader.uniforms.colorMask.value = new Color(color); + return shader; + } + + public static getInstance(): PonyEyes { + if (!instance) { + instance = new PonyEyes(); + } + return instance; + } +} diff --git a/src/client/object/other/texture.ts b/src/client/object/other/texture.ts new file mode 100644 index 0000000..ba965d0 --- /dev/null +++ b/src/client/object/other/texture.ts @@ -0,0 +1,18 @@ +import { TextureLoader, Texture } from 'three'; + +const loader = new TextureLoader(); + +export class BaseTexture { + constructor(public source: string, public texture: Texture) {} + + public static async load(src: string): Promise { + return new Promise((resolve, reject) => + loader.load( + src, + (texture) => resolve(new BaseTexture(src, texture)), + undefined, + reject, + ), + ); + } +} diff --git a/src/client/object/pony.ts b/src/client/object/pony.ts index 7a8b0e9..74d3f5d 100644 --- a/src/client/object/pony.ts +++ b/src/client/object/pony.ts @@ -11,8 +11,10 @@ import { AnimationMixer, Object3D, Vector3, + ShaderMaterial, } from 'three'; import { ClientWorld } from './world/ClientWorld'; +import { PonyEyes } from './other/eyes'; const nameTagBuilder = new CanvasUtils({ fill: false, @@ -43,6 +45,8 @@ export class PonyEntity { .material as MeshStandardMaterial ).clone(); (this.model.children[0].children[1] as Mesh).material = this.material; + (this.model.children[0].children[2] as Mesh).material = + PonyEyes.getInstance().getShader(); this.mixer = new AnimationMixer(this.model); this.idleAction = this.mixer.clipAction(modelLoaderInstance.animations[0]); this.walkAction = this.mixer.clipAction(modelLoaderInstance.animations[2]); @@ -62,10 +66,11 @@ export class PonyEntity { ); // Put pony on the terrain - const terrainFloorHeight = this.heightSource?.getInterpolatedHeight( - this.container.position.x, - this.container.position.z, - ) || 0; + const terrainFloorHeight = + this.heightSource?.getInterpolatedHeight( + this.container.position.x, + this.container.position.z, + ) || 0; this.container.position.y = terrainFloorHeight; this.mixer.update(dt); @@ -103,10 +108,21 @@ export class PonyEntity { this.material.color = new Color(color); } + public setEyeColor(color: number | string) { + PonyEyes.getInstance().setColor( + (this.model.children[0].children[2] as Mesh).material as ShaderMaterial, + color, + ); + } + public setCharacter(packet: CharacterPacket) { if (packet.color) { this.setColor(packet.color); } + + if (packet.eyeColor) { + this.setEyeColor(packet.eyeColor); + } } public setHeightSource(source: ClientWorld) { diff --git a/src/common/types/packet.ts b/src/common/types/packet.ts index 1f5e52b..4fcf8ef 100644 --- a/src/common/types/packet.ts +++ b/src/common/types/packet.ts @@ -19,6 +19,7 @@ export interface FullStatePacket { export interface CharacterPacket { color?: number | string; + eyeColor?: number | string; } export interface CompositePacket extends IcyNetUser, FullStatePacket {}