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
zcmeHNeVi6m6@HfGLq$PQ5zUt6HHBcYefQ&K7h`8%5zSOcBsCFfRYVa)WlN0kl9Za3
zsjo?)5n7p=
~R
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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);