colorable eyes
This commit is contained in:
parent
3907950340
commit
686f1c9123
BIN
assets/eyes/eye-base.png
Normal file
BIN
assets/eyes/eye-base.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
BIN
assets/eyes/eye-mask.png
Normal file
BIN
assets/eyes/eye-mask.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
@ -7,6 +7,7 @@ import { ThirdPersonCamera } from './object/camera';
|
|||||||
import { Chat } from './object/chat';
|
import { Chat } from './object/chat';
|
||||||
import { Joystick } from './object/joystick';
|
import { Joystick } from './object/joystick';
|
||||||
import { CubeMap } from './object/other/cubemap';
|
import { CubeMap } from './object/other/cubemap';
|
||||||
|
import { PonyEyes } from './object/other/eyes';
|
||||||
import { VideoPlayer } from './object/other/video-player';
|
import { VideoPlayer } from './object/other/video-player';
|
||||||
import { Player } from './object/player';
|
import { Player } from './object/player';
|
||||||
import { PlayerEntity } from './object/player-entity';
|
import { PlayerEntity } from './object/player-entity';
|
||||||
@ -40,6 +41,7 @@ export class Game {
|
|||||||
const cube = await CubeMap.load('/assets/skybox/default');
|
const cube = await CubeMap.load('/assets/skybox/default');
|
||||||
|
|
||||||
await modelLoaderInstance.loadPonyModel();
|
await modelLoaderInstance.loadPonyModel();
|
||||||
|
await PonyEyes.getInstance().initialize();
|
||||||
await this.world.initialize();
|
await this.world.initialize();
|
||||||
|
|
||||||
this.renderer.initialize();
|
this.renderer.initialize();
|
||||||
@ -57,9 +59,11 @@ export class Game {
|
|||||||
(item) => item,
|
(item) => item,
|
||||||
);
|
);
|
||||||
|
|
||||||
const colorGet = localStorage.getItem('color');
|
const character = localStorage.getItem('character')
|
||||||
if (colorGet) {
|
? JSON.parse(localStorage.getItem('character'))
|
||||||
this.character.color = colorGet;
|
: {};
|
||||||
|
if (character) {
|
||||||
|
this.character = character;
|
||||||
}
|
}
|
||||||
// end of
|
// end of
|
||||||
|
|
||||||
@ -254,9 +258,28 @@ export class Game {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.character = { ...this.character, color };
|
||||||
this.player.setColor(color);
|
this.player.setColor(color);
|
||||||
this.socket.emit('character', { color });
|
this.socket.emit('character', this.character);
|
||||||
localStorage.setItem('color', color);
|
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')) {
|
if (message.startsWith('!party')) {
|
||||||
|
173
src/client/object/other/eyes.ts
Normal file
173
src/client/object/other/eyes.ts
Normal file
@ -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 <common>
|
||||||
|
#include <uv_pars_vertex>
|
||||||
|
#include <uv2_pars_vertex>
|
||||||
|
#include <displacementmap_pars_vertex>
|
||||||
|
#include <envmap_pars_vertex>
|
||||||
|
#include <color_pars_vertex>
|
||||||
|
#include <fog_pars_vertex>
|
||||||
|
#include <normal_pars_vertex>
|
||||||
|
#include <morphtarget_pars_vertex>
|
||||||
|
#include <skinning_pars_vertex>
|
||||||
|
#include <shadowmap_pars_vertex>
|
||||||
|
#include <logdepthbuf_pars_vertex>
|
||||||
|
#include <clipping_planes_pars_vertex>
|
||||||
|
void main() {
|
||||||
|
vUv = uv;
|
||||||
|
#include <uv_vertex>
|
||||||
|
#include <uv2_vertex>
|
||||||
|
#include <color_vertex>
|
||||||
|
#include <beginnormal_vertex>
|
||||||
|
#include <morphnormal_vertex>
|
||||||
|
#include <skinbase_vertex>
|
||||||
|
#include <skinnormal_vertex>
|
||||||
|
#include <defaultnormal_vertex>
|
||||||
|
#include <normal_vertex>
|
||||||
|
#include <begin_vertex>
|
||||||
|
#include <morphtarget_vertex>
|
||||||
|
#include <skinning_vertex>
|
||||||
|
#include <displacementmap_vertex>
|
||||||
|
#include <project_vertex>
|
||||||
|
#include <logdepthbuf_vertex>
|
||||||
|
#include <clipping_planes_vertex>
|
||||||
|
vViewPosition = - mvPosition.xyz;
|
||||||
|
#include <worldpos_vertex>
|
||||||
|
#include <envmap_vertex>
|
||||||
|
#include <shadowmap_vertex>
|
||||||
|
#include <fog_vertex>
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
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 <common>
|
||||||
|
#include <packing>
|
||||||
|
#include <dithering_pars_fragment>
|
||||||
|
#include <color_pars_fragment>
|
||||||
|
#include <uv_pars_fragment>
|
||||||
|
#include <uv2_pars_fragment>
|
||||||
|
#include <map_pars_fragment>
|
||||||
|
#include <alphamap_pars_fragment>
|
||||||
|
#include <alphatest_pars_fragment>
|
||||||
|
#include <aomap_pars_fragment>
|
||||||
|
#include <lightmap_pars_fragment>
|
||||||
|
#include <emissivemap_pars_fragment>
|
||||||
|
#include <envmap_common_pars_fragment>
|
||||||
|
#include <envmap_pars_fragment>
|
||||||
|
#include <cube_uv_reflection_fragment>
|
||||||
|
#include <fog_pars_fragment>
|
||||||
|
#include <bsdfs>
|
||||||
|
#include <lights_pars_begin>
|
||||||
|
#include <normal_pars_fragment>
|
||||||
|
#include <lights_phong_pars_fragment>
|
||||||
|
#include <shadowmap_pars_fragment>
|
||||||
|
#include <bumpmap_pars_fragment>
|
||||||
|
#include <normalmap_pars_fragment>
|
||||||
|
#include <specularmap_pars_fragment>
|
||||||
|
#include <logdepthbuf_pars_fragment>
|
||||||
|
#include <clipping_planes_pars_fragment>
|
||||||
|
void main() {
|
||||||
|
#include <clipping_planes_fragment>
|
||||||
|
|
||||||
|
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 <logdepthbuf_fragment>
|
||||||
|
#include <map_fragment>
|
||||||
|
#include <color_fragment>
|
||||||
|
#include <alphamap_fragment>
|
||||||
|
#include <alphatest_fragment>
|
||||||
|
#include <specularmap_fragment>
|
||||||
|
#include <normal_fragment_begin>
|
||||||
|
#include <normal_fragment_maps>
|
||||||
|
#include <emissivemap_fragment>
|
||||||
|
// accumulation
|
||||||
|
#include <lights_phong_fragment>
|
||||||
|
#include <lights_fragment_begin>
|
||||||
|
#include <lights_fragment_maps>
|
||||||
|
#include <lights_fragment_end>
|
||||||
|
// modulation
|
||||||
|
#include <aomap_fragment>
|
||||||
|
vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;
|
||||||
|
#include <envmap_fragment>
|
||||||
|
#include <output_fragment>
|
||||||
|
#include <tonemapping_fragment>
|
||||||
|
#include <encodings_fragment>
|
||||||
|
#include <fog_fragment>
|
||||||
|
#include <premultiplied_alpha_fragment>
|
||||||
|
#include <dithering_fragment>
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
18
src/client/object/other/texture.ts
Normal file
18
src/client/object/other/texture.ts
Normal file
@ -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<BaseTexture> {
|
||||||
|
return new Promise((resolve, reject) =>
|
||||||
|
loader.load(
|
||||||
|
src,
|
||||||
|
(texture) => resolve(new BaseTexture(src, texture)),
|
||||||
|
undefined,
|
||||||
|
reject,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -11,8 +11,10 @@ import {
|
|||||||
AnimationMixer,
|
AnimationMixer,
|
||||||
Object3D,
|
Object3D,
|
||||||
Vector3,
|
Vector3,
|
||||||
|
ShaderMaterial,
|
||||||
} from 'three';
|
} from 'three';
|
||||||
import { ClientWorld } from './world/ClientWorld';
|
import { ClientWorld } from './world/ClientWorld';
|
||||||
|
import { PonyEyes } from './other/eyes';
|
||||||
|
|
||||||
const nameTagBuilder = new CanvasUtils({
|
const nameTagBuilder = new CanvasUtils({
|
||||||
fill: false,
|
fill: false,
|
||||||
@ -43,6 +45,8 @@ export class PonyEntity {
|
|||||||
.material as MeshStandardMaterial
|
.material as MeshStandardMaterial
|
||||||
).clone();
|
).clone();
|
||||||
(this.model.children[0].children[1] as Mesh).material = this.material;
|
(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.mixer = new AnimationMixer(this.model);
|
||||||
this.idleAction = this.mixer.clipAction(modelLoaderInstance.animations[0]);
|
this.idleAction = this.mixer.clipAction(modelLoaderInstance.animations[0]);
|
||||||
this.walkAction = this.mixer.clipAction(modelLoaderInstance.animations[2]);
|
this.walkAction = this.mixer.clipAction(modelLoaderInstance.animations[2]);
|
||||||
@ -62,10 +66,11 @@ export class PonyEntity {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Put pony on the terrain
|
// Put pony on the terrain
|
||||||
const terrainFloorHeight = this.heightSource?.getInterpolatedHeight(
|
const terrainFloorHeight =
|
||||||
this.container.position.x,
|
this.heightSource?.getInterpolatedHeight(
|
||||||
this.container.position.z,
|
this.container.position.x,
|
||||||
) || 0;
|
this.container.position.z,
|
||||||
|
) || 0;
|
||||||
this.container.position.y = terrainFloorHeight;
|
this.container.position.y = terrainFloorHeight;
|
||||||
|
|
||||||
this.mixer.update(dt);
|
this.mixer.update(dt);
|
||||||
@ -103,10 +108,21 @@ export class PonyEntity {
|
|||||||
this.material.color = new Color(color);
|
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) {
|
public setCharacter(packet: CharacterPacket) {
|
||||||
if (packet.color) {
|
if (packet.color) {
|
||||||
this.setColor(packet.color);
|
this.setColor(packet.color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (packet.eyeColor) {
|
||||||
|
this.setEyeColor(packet.eyeColor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public setHeightSource(source: ClientWorld) {
|
public setHeightSource(source: ClientWorld) {
|
||||||
|
@ -19,6 +19,7 @@ export interface FullStatePacket {
|
|||||||
|
|
||||||
export interface CharacterPacket {
|
export interface CharacterPacket {
|
||||||
color?: number | string;
|
color?: number | string;
|
||||||
|
eyeColor?: number | string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CompositePacket extends IcyNetUser, FullStatePacket {}
|
export interface CompositePacket extends IcyNetUser, FullStatePacket {}
|
||||||
|
Loading…
Reference in New Issue
Block a user