diff --git a/packages/frontend/assets/world/lobby/default.blend b/packages/frontend/assets/world/lobby/default.blend index dc14eee2ac..32fc604077 100644 Binary files a/packages/frontend/assets/world/lobby/default.blend and b/packages/frontend/assets/world/lobby/default.blend differ diff --git a/packages/frontend/assets/world/lobby/default.glb b/packages/frontend/assets/world/lobby/default.glb index 076cf3d97d..51f276edf7 100644 Binary files a/packages/frontend/assets/world/lobby/default.glb and b/packages/frontend/assets/world/lobby/default.glb differ diff --git a/packages/frontend/src/world/engine.ts b/packages/frontend/src/world/engine.ts index c31be71f19..cbe5926a06 100644 --- a/packages/frontend/src/world/engine.ts +++ b/packages/frontend/src/world/engine.ts @@ -112,15 +112,18 @@ export class WorldEngine extends EventEmitter { //this.scene.activeCamera = this.camera; - const ambientLight = new BABYLON.HemisphericLight('ambientLight', new BABYLON.Vector3(0, 1, -0.5), this.scene); - ambientLight.diffuse = new BABYLON.Color3(1.0, 1.0, 1.0); - ambientLight.intensity = 0.5; + const ambientLight1 = new BABYLON.HemisphericLight('ambientLight1', new BABYLON.Vector3(0, 1, 0), this.scene); + ambientLight1.diffuse = new BABYLON.Color3(1.0, 0.9, 0.8); + ambientLight1.intensity = 1; + const ambientLight2 = new BABYLON.HemisphericLight('ambientLight2', new BABYLON.Vector3(0, -1, 0), this.scene); + ambientLight2.diffuse = new BABYLON.Color3(0.8, 0.9, 1.0); + ambientLight2.intensity = 1; //ambientLight.intensity = 0; - const sunLight = new BABYLON.DirectionalLight('sunLight', new BABYLON.Vector3(0.2, -1, -1), this.scene); - sunLight.position = new BABYLON.Vector3(cm(-20), cm(1000), cm(1000)); + const sunLight = new BABYLON.DirectionalLight('sunLight', new BABYLON.Vector3(0, -1, 0), this.scene); + sunLight.position = new BABYLON.Vector3(cm(0), cm(10000), cm(0)); sunLight.diffuse = this.time === 0 ? new BABYLON.Color3(1.0, 1.0, 1.0) : this.time === 1 ? new BABYLON.Color3(1.0, 0.8, 0.6) : new BABYLON.Color3(0.6, 0.8, 1.0); - sunLight.intensity = this.time === 0 ? 3 : this.time === 1 ? 1 : 0.25; + sunLight.intensity = this.time === 0 ? 2 : this.time === 1 ? 0.5 : 0.25; sunLight.shadowMinZ = cm(1000); sunLight.shadowMaxZ = cm(2000); @@ -231,7 +234,7 @@ export class WorldEngine extends EventEmitter { { const messageRingRoot = new BABYLON.TransformNode('', this.scene); - const messageRing = envObj.meshes.find(m => m.name.includes('__MESSAGE_RING__')); + const messageRing = envObj.meshes.find(m => m.name.includes('__MESSAGE_RING_OUTER_1__')); messageRing.parent = messageRingRoot; messageRing.rotation = messageRing.rotationQuaternion.toEulerAngles(); messageRing.rotationQuaternion = null; @@ -271,6 +274,33 @@ export class WorldEngine extends EventEmitter { }, 10000); } + { + const messageRingRoot = new BABYLON.TransformNode('', this.scene); + const messageRing = envObj.meshes.find(m => m.name.includes('__MESSAGE_RING_OUTER_2__')); + messageRing.parent = messageRingRoot; + messageRing.rotation = messageRing.rotationQuaternion.toEulerAngles(); + messageRing.rotationQuaternion = null; + const text = new RecyvlingTextGrid(messageRing, 256, { + meshFlipped: true, + material: this.textMaterial, + repeatSeparator: ' ', + }); + + messageRingRoot.rotation.x = Math.PI / 2; + + const anim = new BABYLON.Animation('', 'rotation.y', 60, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE); + anim.setKeys([ + { frame: 0, value: 0 }, + { frame: 10000, value: -(Math.PI * 2) }, + ]); + messageRing.animations = [anim]; + this.scene.beginAnimation(messageRing, 0, 10000, true); + + setInterval(() => { + text.write(Date.now().toString()); + }, 100); + } + { const messageRingRoot = new BABYLON.TransformNode('', this.scene); const messageRing = envObj.meshes.find(m => m.name.includes('__MESSAGE_RING_INNER_1__')); @@ -517,3 +547,35 @@ export class WorldEngine extends EventEmitter { this.disposed = true; } } + +class MessageRing { + constructor(mesh: BABYLON.Mesh, scene: BABYLON.Scene, options: { material: BABYLON.StandardMaterial; repeatSeparator: string; }) { + const messageRingRoot = new BABYLON.TransformNode('', this.scene); + const messageRing = envObj.meshes.find(m => m.name.includes('__MESSAGE_RING_INNER_1__')); + messageRing.parent = messageRingRoot; + messageRing.rotation = messageRing.rotationQuaternion.toEulerAngles(); + messageRing.rotationQuaternion = null; + const text = new RecyvlingTextGrid(messageRing, 64, { + material: this.textMaterial, + repeatSeparator: ' ', + }); + + //messageRingRoot.rotation.x = Math.PI / 4; + + const anim = new BABYLON.Animation('', 'rotation.y', 60, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE); + anim.setKeys([ + { frame: 0, value: 0 }, + { frame: 10000, value: (Math.PI * 2) }, + ]); + messageRing.animations = [anim]; + this.scene.beginAnimation(messageRing, 0, 10000, true); + + setInterval(() => { + const now = new Date(); + const hours = now.getHours().toString().padStart(2, '0'); + const minutes = now.getMinutes().toString().padStart(2, '0'); + const seconds = now.getSeconds().toString().padStart(2, '0'); + text.write(`${hours}:${minutes}:${seconds}`); + }, 1000); + } +} diff --git a/packages/frontend/src/world/utility.ts b/packages/frontend/src/world/utility.ts index a9630fcd78..bd56189837 100644 --- a/packages/frontend/src/world/utility.ts +++ b/packages/frontend/src/world/utility.ts @@ -500,11 +500,30 @@ export class RecyvlingText { export class RecyvlingTextGrid { public facesCount: number; public mesh: BABYLON.Mesh; - private originalUvs: BABYLON.FloatArray; + private uvs: BABYLON.FloatArray; private currentText = ''; private meshFlipped: boolean; private repeatSeparator: string; + /* (non-flipped) + a d--e + | \ \ | + b--c f + */ + /* (flipped) + a--b d + | / / | + c e--f + */ + private aIndex = 0; + private bIndex = 0; + private cIndex = 0; + private dIndex = 0; + private eIndex = 0; + private fIndex = 0; + + private verticesCountPerFace = 6; // ひとつの四角はふたつの三角に分割されるので 3*2=6 + constructor(mesh: BABYLON.Mesh, facesCount: number, options: { meshFlipped: boolean; material: BABYLON.StandardMaterial; @@ -516,10 +535,56 @@ export class RecyvlingTextGrid { this.mesh.markVerticesDataAsUpdatable(BABYLON.VertexBuffer.UVKind, true); this.facesCount = facesCount; - this.originalUvs = mesh.getVerticesData(BABYLON.VertexBuffer.UVKind)!.slice(); + this.uvs = mesh.getVerticesData(BABYLON.VertexBuffer.UVKind)!; this.meshFlipped = options.meshFlipped; this.repeatSeparator = options.repeatSeparator ?? ' ■ '; + for (let j = 0; j < (this.verticesCountPerFace * 2); j += 2) { + const x = this.uvs[j]; + const y = this.uvs[j + 1]; + + // 多少ずれがあってもいいように(例えばblenderではUV展開時にデフォルトでわずかなマージンを追加する)、中心より大きいか/小さいかで判定する + // また、すべての面が同じ頂点構造である前提としている + if (this.meshFlipped) { + // ひとつの四角はふたつの三角に分割される。右下に来る三角(d-e-f)の方が先にくるっぽい + if (j >= 6) { + if (x < 0.5 && y < 0.5) { + this.aIndex = j; + } else if (x > 0.5 && y < 0.5) { + this.bIndex = j; + } else if (x < 0.5 && y > 0.5) { + this.cIndex = j; + } + } else { + if (x > 0.5 && y < 0.5) { + this.dIndex = j; + } else if (x < 0.5 && y > 0.5) { + this.eIndex = j; + } else if (x > 0.5 && y > 0.5) { + this.fIndex = j; + } + } + } else { + if (j >= 6) { + if (x < 0.5 && y < 0.5) { + this.aIndex = j; + } else if (x < 0.5 && y > 0.5) { + this.bIndex = j; + } else if (x > 0.5 && y > 0.5) { + this.cIndex = j; + } + } else { + if (x < 0.5 && y < 0.5) { + this.dIndex = j; + } else if (x > 0.5 && y < 0.5) { + this.eIndex = j; + } else if (x > 0.5 && y > 0.5) { + this.fIndex = j; + } + } + } + } + //this.write(''); } @@ -554,93 +619,27 @@ export class RecyvlingTextGrid { charIndexes.push(index); } - const uvs = this.originalUvs.slice(); - - const verticesCountPerFace = 6; // ひとつの四角はふたつの三角に分割されるので 3*2=6 - for (let i = 0; i < this.facesCount; i++) { const charIndex = charIndexes[this.meshFlipped ? i : (this.facesCount - i - 1)]; const charX = charIndex % TEXT_TEXTURE_CHAR_COLS; const charY = Math.floor(charIndex / TEXT_TEXTURE_CHAR_COLS); - const uvIndex = i * (verticesCountPerFace * 2); // uvは(x,y)の2要素なので*2 - - /* (non-flipped) - a d--e - | \ \ | - b--c f - */ - /* (flipped) - a--b d - | / / | - c e--f - */ - let aIndex = 0; - let bIndex = 0; - let cIndex = 0; - let dIndex = 0; - let eIndex = 0; - let fIndex = 0; - - for (let j = 0; j < (verticesCountPerFace * 2); j += 2) { - const x = uvs[uvIndex + j]; - const y = uvs[uvIndex + j + 1]; - - // 多少ずれがあってもいいように(例えばblenderではUV展開時にデフォルトでわずかなマージンを追加する)、中心より大きいか/小さいかで判定する - if (this.meshFlipped) { - // ひとつの四角はふたつの三角に分割される。右下に来る三角(d-e-f)の方が先にくるっぽい - if (j >= 6) { - if (x < 0.5 && y < 0.5) { - aIndex = j; - } else if (x > 0.5 && y < 0.5) { - bIndex = j; - } else if (x < 0.5 && y > 0.5) { - cIndex = j; - } - } else { - if (x > 0.5 && y < 0.5) { - dIndex = j; - } else if (x < 0.5 && y > 0.5) { - eIndex = j; - } else if (x > 0.5 && y > 0.5) { - fIndex = j; - } - } - } else { - if (j >= 6) { - if (x < 0.5 && y < 0.5) { - aIndex = j; - } else if (x < 0.5 && y > 0.5) { - bIndex = j; - } else if (x > 0.5 && y > 0.5) { - cIndex = j; - } - } else { - if (x < 0.5 && y < 0.5) { - dIndex = j; - } else if (x > 0.5 && y < 0.5) { - eIndex = j; - } else if (x > 0.5 && y > 0.5) { - fIndex = j; - } - } - } - } + const uvIndex = i * (this.verticesCountPerFace * 2); // uvは(x,y)の2要素なので*2 if (this.meshFlipped) { - uvs[uvIndex + aIndex + 0] = uvs[uvIndex + cIndex + 0] = uvs[uvIndex + eIndex + 0] = charX / TEXT_TEXTURE_CHAR_COLS; - uvs[uvIndex + aIndex + 1] = uvs[uvIndex + bIndex + 1] = uvs[uvIndex + dIndex + 1] = charY / TEXT_TEXTURE_CHAR_ROWS; - uvs[uvIndex + bIndex + 0] = uvs[uvIndex + dIndex + 0] = uvs[uvIndex + fIndex + 0] = (charX + 1) / TEXT_TEXTURE_CHAR_COLS; - uvs[uvIndex + cIndex + 1] = uvs[uvIndex + eIndex + 1] = uvs[uvIndex + fIndex + 1] = (charY + 1) / TEXT_TEXTURE_CHAR_ROWS; + this.uvs[uvIndex + this.aIndex + 0] = this.uvs[uvIndex + this.cIndex + 0] = this.uvs[uvIndex + this.eIndex + 0] = (charX + 0) / TEXT_TEXTURE_CHAR_COLS; + this.uvs[uvIndex + this.aIndex + 1] = this.uvs[uvIndex + this.bIndex + 1] = this.uvs[uvIndex + this.dIndex + 1] = (charY + 0) / TEXT_TEXTURE_CHAR_ROWS; + this.uvs[uvIndex + this.bIndex + 0] = this.uvs[uvIndex + this.dIndex + 0] = this.uvs[uvIndex + this.fIndex + 0] = (charX + 1) / TEXT_TEXTURE_CHAR_COLS; + this.uvs[uvIndex + this.cIndex + 1] = this.uvs[uvIndex + this.eIndex + 1] = this.uvs[uvIndex + this.fIndex + 1] = (charY + 1) / TEXT_TEXTURE_CHAR_ROWS; } else { - uvs[uvIndex + aIndex + 0] = uvs[uvIndex + dIndex + 0] = uvs[uvIndex + bIndex + 0] = charX / TEXT_TEXTURE_CHAR_COLS; - uvs[uvIndex + aIndex + 1] = uvs[uvIndex + dIndex + 1] = uvs[uvIndex + eIndex + 1] = charY / TEXT_TEXTURE_CHAR_ROWS; - uvs[uvIndex + eIndex + 0] = uvs[uvIndex + fIndex + 0] = uvs[uvIndex + cIndex + 0] = (charX + 1) / TEXT_TEXTURE_CHAR_COLS; - uvs[uvIndex + bIndex + 1] = uvs[uvIndex + cIndex + 1] = uvs[uvIndex + fIndex + 1] = (charY + 1) / TEXT_TEXTURE_CHAR_ROWS; + this.uvs[uvIndex + this.aIndex + 0] = this.uvs[uvIndex + this.dIndex + 0] = this.uvs[uvIndex + this.bIndex + 0] = (charX + 0) / TEXT_TEXTURE_CHAR_COLS; + this.uvs[uvIndex + this.aIndex + 1] = this.uvs[uvIndex + this.dIndex + 1] = this.uvs[uvIndex + this.eIndex + 1] = (charY + 0) / TEXT_TEXTURE_CHAR_ROWS; + this.uvs[uvIndex + this.eIndex + 0] = this.uvs[uvIndex + this.fIndex + 0] = this.uvs[uvIndex + this.cIndex + 0] = (charX + 1) / TEXT_TEXTURE_CHAR_COLS; + this.uvs[uvIndex + this.bIndex + 1] = this.uvs[uvIndex + this.cIndex + 1] = this.uvs[uvIndex + this.fIndex + 1] = (charY + 1) / TEXT_TEXTURE_CHAR_ROWS; } } - this.mesh.updateVerticesData(BABYLON.VertexBuffer.UVKind, uvs); + this.mesh.updateVerticesData(BABYLON.VertexBuffer.UVKind, this.uvs); } public async writeWithAnimation(text: string) {