From 47cd092380524e9ee0c454c0fa4ee02a0c80bc09 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Sat, 18 Apr 2026 15:08:36 +0900 Subject: [PATCH] wip --- .../frontend/assets/world/lobby/default.glb | Bin 1138704 -> 1149028 bytes packages/frontend/src/world/engine.ts | 64 +++++++++++++- packages/frontend/src/world/utility.ts | 83 +++++++++++++----- 3 files changed, 122 insertions(+), 25 deletions(-) diff --git a/packages/frontend/assets/world/lobby/default.glb b/packages/frontend/assets/world/lobby/default.glb index a90df9af0659c56fdc015da55d3cad6c41995303..076cf3d97da965d60f2b7b6b1a258ead52b42534 100644 GIT binary patch delta 13745 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 Qmf5x~v;U(lv!`nQ2Mm2czyJUM delta 7031 zcmai3Yj9Q775)Mw#DEuq2#BE9D6bGT_vS%D%)Pma1XB>2u?f9*fRau=~#y89~h_7na*@N(;54%efGIoCzX~x=UZ#9 zv+mk!eQWKLvHk1bse3a|hL)|IGqn$Z!Og`etnCPWdGhNq`R?TW0ezw+-8&1q2IN({ z9e0+x3x?F>m3J54wdU?|k=mNFl2uDKmP~#&QdJg>ltro*(y)9r4G+^|A9u;<``ydq z!+q)Rb_~1E&7TnN6RCBJhu`l`oe&;UHL1F+Wc{Z0r6rR~WdemAzj^BNdh9?%y9S9UqBQ zMgQw}60IzsRJ~>4mT>s1vBh}&si)yytsAlZYTb^j zb$NXH@afB^AD?_a1$_GRDdckppCUd3_zdK8C!Y|XL3{?Ey;@iN?j25h76|i^e_Br_ zdhGvOkDmQkTz>n{uSR7(J(=jO|M}+n2JyM~k9TD~J()1NV_tZhS-(4JoW3c{&w6?? z(OWN{dfoU?W;{Ka=&kcNOuk)WWIR2Y=&kc`&&-pjJ9ZX^0o^J2GX(e@s6sq1^J@*0ed(XrUMuFE79!vjUZ+)t+LHyS~ zw<4?cF(=L%`}hkBv!XlVB%8UhG zXs$Kl!{ST}0=h6`Sx<~{)JCYZEs%tAx==56sYL6n(%2<=t&&#$?|$baT4zwNdrg>2v#>OSLA%I>89(XYxlQKnZHgkrbEsUVcnb_Os${GkMhNgxKisO?O| z_%MMK6NyPeOzfhzvmoQcLQ*U!cD=u2D!iy)@Gp&VlFZ3WOjr?J+m(OJ3*<4S?dsr| zN~m7jZ#X@b#Z_hXs4hBoFpr`K59sE zb`?mb%?~8Q<{yw!2g}s@3CYwNA7M%rkY4TdR3_tZoP;XHgt>{M)_=>hcCY0=K*P%3lT|E{V!KsQC7RJAhQ8G74O3RYBk) ztBM1cS1}a0uZmH*dWJcZn7gWLQ0}s-CIv3&?Jni*F5>O(-fixi6IU}GxMb=+*o)=y zc)Po#`~&w##Sh#WFQK;Fp9wSHGXJUq`4eO8B2~pQX~r)1K`|tgX{K2wQ!%Q?zQ9pU zkjs=CWd@DQv`A1MXmmN4d!x&t3~Y2cNKF?H?fH|W>@npZvvDRs4pIli)xmL$Ig?NSE^zD1pT zoAZWszR?n@?7Y$v8qs-Epy)Y5sX&uJo4_uCBLY_hZVD7VPbd{=5@-|HC2&OGioi{Q zqNRjVfhK`Afn5Se1g;3&6ewyXlnOKnvkL z-j~gG@5_01@5@yZ7aIyZ2?-?tMAJ?tMAf?tNKk_l54B zFNZs~%$Nhdyk_^lylVHp%-FpzPuRUL58Ayi_u9QLlXh?A3N%}TFB|OMmtnj2CG6gp znR>5ZzD(M^FJbqn?>yKN8tI64yQSa-E1RvHXXPv_r&(!xlWR@6wfAm8Z_0hDVf}B# z;FxT$*&?o5nX&SOl?Sct&04dyH&_|A5?1ym!7DxweNl)!J_T?JCQ3is8iJxSMM@MiNJ21lLH6CXU9vT%!>thH)QP7!ML3!~7xRhp@qMoOSU_BWA8;+eV&Y;f}|wto!{Nj}~9Ic*tY23h=wwr*lKPT>VSj}M6- z;v@Wm@3X`#KEsds{uJ>PPNU7C!$)Z2(}ja{_>A}&F5xG1I88i_Gg!&@UBoVYj9>Eo zCE_Jq#%p|khIj^Nv6}BcCVq?~c%Sbt6EEX)?Be^g#IrbucD_GCJc3W~9N&LVeC2a| zfS=Og9Pu1J#acRiLi_|r@dj<55?sj+Aa_; e;5XPv+i~J?{E9@sNy}S&E|TYsw7ksc1pWh3Avi$* diff --git a/packages/frontend/src/world/engine.ts b/packages/frontend/src/world/engine.ts index 1329a1b58b..c31be71f19 100644 --- a/packages/frontend/src/world/engine.ts +++ b/packages/frontend/src/world/engine.ts @@ -236,7 +236,7 @@ export class WorldEngine extends EventEmitter { messageRing.rotation = messageRing.rotationQuaternion.toEulerAngles(); messageRing.rotationQuaternion = null; const text = new RecyvlingTextGrid(messageRing, 256, { - dir: 'left', + meshFlipped: true, material: this.textMaterial, }); @@ -247,7 +247,7 @@ export class WorldEngine extends EventEmitter { 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 }, + { frame: 10000, value: -(Math.PI * 2) }, ]); messageRing.animations = [anim]; this.scene.beginAnimation(messageRing, 0, 10000, true); @@ -271,6 +271,66 @@ export class WorldEngine extends EventEmitter { }, 10000); } + { + 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); + } + + { + const messageRingRoot = new BABYLON.TransformNode('', this.scene); + const messageRing = envObj.meshes.find(m => m.name.includes('__MESSAGE_RING_INNER_2__')); + 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 years = now.getFullYear().toString(); + const months = (now.getMonth() + 1).toString().padStart(2, '0'); + const days = now.getDate().toString().padStart(2, '0'); + text.write(`${years}/${months}/${days}`); + }, 1000); + } + for (let i = 0; i < 16; i++) { const sphereRoot = new BABYLON.TransformNode('', this.scene); sphereRoot.position = new BABYLON.Vector3(cm(0), cm(1000 + (100 * i)), cm(0)); diff --git a/packages/frontend/src/world/utility.ts b/packages/frontend/src/world/utility.ts index 606a612dfb..a9630fcd78 100644 --- a/packages/frontend/src/world/utility.ts +++ b/packages/frontend/src/world/utility.ts @@ -502,9 +502,13 @@ export class RecyvlingTextGrid { public mesh: BABYLON.Mesh; private originalUvs: BABYLON.FloatArray; private currentText = ''; + private meshFlipped: boolean; + private repeatSeparator: string; constructor(mesh: BABYLON.Mesh, facesCount: number, options: { + meshFlipped: boolean; material: BABYLON.StandardMaterial; + repeatSeparator?: string; }) { this.mesh = mesh; this.mesh.material = options.material; @@ -513,6 +517,8 @@ export class RecyvlingTextGrid { this.facesCount = facesCount; this.originalUvs = mesh.getVerticesData(BABYLON.VertexBuffer.UVKind)!.slice(); + this.meshFlipped = options.meshFlipped; + this.repeatSeparator = options.repeatSeparator ?? ' ■ '; //this.write(''); } @@ -522,17 +528,16 @@ export class RecyvlingTextGrid { const charIndexes: number[] = []; - const repeatSeparator = ' ■ '; let maxRepeat = Math.ceil(this.facesCount / text.length); if (maxRepeat > 1) { - text += repeatSeparator; + text += this.repeatSeparator; maxRepeat = Math.ceil(this.facesCount / text.length); } for (let i = 0; i < this.facesCount; i++) { if (i + text.length >= (maxRepeat * text.length)) { - if (i >= this.facesCount - repeatSeparator.length) { - charIndexes.push(TEXT_TEXTURE_CHAR_MAP[repeatSeparator[(i - (this.facesCount - repeatSeparator.length)) % repeatSeparator.length]]); + if (i >= this.facesCount - this.repeatSeparator.length) { + charIndexes.push(TEXT_TEXTURE_CHAR_MAP[this.repeatSeparator[(i - (this.facesCount - this.repeatSeparator.length)) % this.repeatSeparator.length]]); } else { charIndexes.push(TEXT_TEXTURE_CHAR_MAP[' ']); } @@ -554,13 +559,18 @@ export class RecyvlingTextGrid { const verticesCountPerFace = 6; // ひとつの四角はふたつの三角に分割されるので 3*2=6 for (let i = 0; i < this.facesCount; i++) { - const charIndex = charIndexes[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 @@ -577,30 +587,57 @@ export class RecyvlingTextGrid { 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; + 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 (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; + 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; + } } } } - 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; + 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; + } 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.mesh.updateVerticesData(BABYLON.VertexBuffer.UVKind, uvs);