This commit is contained in:
syuilo
2026-03-03 18:29:53 +09:00
parent 8eebeab692
commit a90c179998
3 changed files with 135 additions and 82 deletions

View File

@@ -5,6 +5,7 @@
import * as BABYLON from '@babylonjs/core';
import { defineObject } from '../engine.js';
import { getPlaneUvIndexes } from '../utility.js';
export const pictureFrame = defineObject({
id: 'pictureFrame',
@@ -87,21 +88,17 @@ export const pictureFrame = defineObject({
const pictureMaterial = findMaterial('__X_PICTURE__');
const uvs = pictureMesh.getVerticesData(BABYLON.VertexBuffer.UVKind);
const uvs = pictureMesh.getVerticesData(BABYLON.VertexBuffer.UVKind)!;
const uvIndexes = getPlaneUvIndexes(pictureMesh);
/**
* a(x,y)---b(x,y)
* | |
* c(x,y)---d(x,y)
*/
const ax = uvs[6];
const ay = uvs[7];
const bx = uvs[2];
const by = uvs[3];
const cx = uvs[4];
const cy = uvs[5];
const dx = uvs[0];
const dy = uvs[1];
const ax = uvs[uvIndexes[0]];
const ay = uvs[uvIndexes[0] + 1];
const bx = uvs[uvIndexes[1]];
const by = uvs[uvIndexes[1] + 1];
const cx = uvs[uvIndexes[2]];
const cy = uvs[uvIndexes[2] + 1];
const dx = uvs[uvIndexes[3]];
const dy = uvs[uvIndexes[3] + 1];
const applyFit = () => {
const tex = pictureMaterial.albedoTexture;
@@ -124,49 +121,71 @@ export const pictureFrame = defineObject({
let newDy = dy;
if (options.fit === 'cover') {
if (targetAspect > srcAspect) {
const fitHeight = targetWidth / srcAspect;
const crop = (fitHeight - targetHeight) / fitHeight / 2;
newAy = ay + crop * (by - ay);
newBy = by - crop * (by - ay);
newCy = cy + crop * (dy - cy);
newDy = dy - crop * (dy - cy);
const ratio = targetAspect / srcAspect;
let uRange: number;
let vRange: number;
if (ratio < 1) {
uRange = ratio; // < 1
vRange = 1;
} else {
const fitWidth = targetHeight * srcAspect;
const crop = (fitWidth - targetWidth) / fitWidth / 2;
newAx = ax + crop * (bx - ax);
newBx = bx - crop * (bx - ax);
newCx = cx + crop * (dx - cx);
newDx = dx - crop * (dx - cx);
uRange = 1;
vRange = 1 / ratio; // < 1
}
const uMin = (1 - uRange) / 2;
const uMax = uMin + uRange;
const vMin = (1 - vRange) / 2;
const vMax = vMin + vRange;
newAx = uMin;
newBx = uMax;
newCx = uMin;
newDx = uMax;
newAy = 1 - vMax;
newBy = 1 - vMax;
newCy = 1 - vMin;
newDy = 1 - vMin;
} else if (options.fit === 'contain') {
if (targetAspect > srcAspect) {
const fitWidth = targetHeight * srcAspect;
const crop = (fitWidth - targetWidth) / fitWidth / 2;
newAx = ax + crop * (bx - ax);
newBx = bx - crop * (bx - ax);
newCx = cx + crop * (dx - cx);
newDx = dx - crop * (dx - cx);
const ratio = targetAspect / srcAspect;
let uRange: number;
let vRange: number;
if (ratio > 1) {
uRange = ratio; // > 1
vRange = 1;
} else {
const fitHeight = targetWidth / srcAspect;
const crop = (fitHeight - targetHeight) / fitHeight / 2;
newAy = ay + crop * (by - ay);
newBy = by - crop * (by - ay);
newCy = cy + crop * (dy - cy);
newDy = dy - crop * (dy - cy);
uRange = 1;
vRange = 1 / ratio; // > 1
}
} else if (options.fit === 'stretch') {
// do nothing
const uMin = (1 - uRange) / 2;
const uMax = uMin + uRange;
const vMin = (1 - vRange) / 2;
const vMax = vMin + vRange;
newAx = uMin;
newBx = uMax;
newCx = uMin;
newDx = uMax;
newAy = 1 - vMax;
newBy = 1 - vMax;
newCy = 1 - vMin;
newDy = 1 - vMin;
}
uvs[6] = newAx;
uvs[7] = newAy;
uvs[2] = newBx;
uvs[3] = newBy;
uvs[4] = newCx;
uvs[5] = newCy;
uvs[0] = newDx;
uvs[1] = newDy;
uvs[uvIndexes[0]] = newAx;
uvs[uvIndexes[0] + 1] = newAy;
uvs[uvIndexes[1]] = newBx;
uvs[uvIndexes[1] + 1] = newBy;
uvs[uvIndexes[2]] = newCx;
uvs[uvIndexes[2] + 1] = newCy;
uvs[uvIndexes[3]] = newDx;
uvs[uvIndexes[3] + 1] = newDy;
pictureMesh.updateVerticesData(BABYLON.VertexBuffer.UVKind, uvs);
};

View File

@@ -5,6 +5,7 @@
import * as BABYLON from '@babylonjs/core';
import { defineObject } from '../engine.js';
import { getPlaneUvIndexes } from '../utility.js';
export const poster = defineObject({
id: 'poster',
@@ -52,22 +53,17 @@ export const poster = defineObject({
const pinMeshes = findMeshes('__X_PIN__');
const uvs = pictureMesh.getVerticesData(BABYLON.VertexBuffer.UVKind);
const uvs = pictureMesh.getVerticesData(BABYLON.VertexBuffer.UVKind)!;
const uvIndexes = getPlaneUvIndexes(pictureMesh);
/**
* 0 1
* 0 a(x,y) --- b(x,y)
* | |
* 1 c(x,y) --- d(x,y)
*/
const ax = uvs[4];
const ay = uvs[5];
const bx = uvs[6];
const by = uvs[7];
const cx = uvs[0];
const cy = uvs[1];
const dx = uvs[2];
const dy = uvs[3];
const ax = uvs[uvIndexes[0]];
const ay = uvs[uvIndexes[0] + 1];
const bx = uvs[uvIndexes[1]];
const by = uvs[uvIndexes[1] + 1];
const cx = uvs[uvIndexes[2]];
const cy = uvs[uvIndexes[2] + 1];
const dx = uvs[uvIndexes[3]];
const dy = uvs[uvIndexes[3] + 1];
const applyFit = () => {
const tex = pictureMaterial.albedoTexture;
@@ -147,14 +143,14 @@ export const poster = defineObject({
newDy = 1 - vMin;
}
uvs[4] = newAx;
uvs[5] = newAy;
uvs[6] = newBx;
uvs[7] = newBy;
uvs[0] = newCx;
uvs[1] = newCy;
uvs[2] = newDx;
uvs[3] = newDy;
uvs[uvIndexes[0]] = newAx;
uvs[uvIndexes[0] + 1] = newAy;
uvs[uvIndexes[1]] = newBx;
uvs[uvIndexes[1] + 1] = newBy;
uvs[uvIndexes[2]] = newCx;
uvs[uvIndexes[2] + 1] = newCy;
uvs[uvIndexes[3]] = newDx;
uvs[uvIndexes[3] + 1] = newDy;
pictureMesh.updateVerticesData(BABYLON.VertexBuffer.UVKind, uvs);
};

View File

@@ -292,6 +292,9 @@ export function initTv(room: RoomEngine, screenMesh: BABYLON.Mesh) {
tvScreenMaterial.freeze();
}
const uvs = screenMesh.getVerticesData(BABYLON.VertexBuffer.UVKind)!;
const uvIndexes = getPlaneUvIndexes(screenMesh);
const applyTvTexture = (tlIndex: number) => {
const [index, duration] = tvProgram.timeline[tlIndex];
@@ -311,15 +314,14 @@ export function initTv(room: RoomEngine, screenMesh: BABYLON.Mesh) {
const dx = bx;
const dy = cy;
const uvs = screenMesh.getVerticesData(BABYLON.VertexBuffer.UVKind);
uvs[0] = dx;
uvs[1] = dy;
uvs[2] = bx;
uvs[3] = by;
uvs[4] = cx;
uvs[5] = cy;
uvs[6] = ax;
uvs[7] = ay;
uvs[uvIndexes[0]] = ax;
uvs[uvIndexes[0] + 1] = ay;
uvs[uvIndexes[1]] = bx;
uvs[uvIndexes[1] + 1] = by;
uvs[uvIndexes[2]] = cx;
uvs[uvIndexes[2] + 1] = cy;
uvs[uvIndexes[3]] = dx;
uvs[uvIndexes[3] + 1] = dy;
screenMesh.updateVerticesData(BABYLON.VertexBuffer.UVKind, uvs);
const timeoutId = window.setTimeout(() => {
@@ -331,3 +333,39 @@ export function initTv(room: RoomEngine, screenMesh: BABYLON.Mesh) {
applyTvTexture(0);
}
/**
* 0 1
* 0 a(x,y) --- b(x,y)
* | |
* 1 c(x,y) --- d(x,y)
*/
export function getPlaneUvIndexes(mesh: BABYLON.Mesh) {
const uvs = mesh.getVerticesData(BABYLON.VertexBuffer.UVKind);
if (uvs == null) {
throw new Error('Mesh does not have UV data');
}
let aIndex = 0;
let bIndex = 0;
let cIndex = 0;
let dIndex = 0;
for (let i = 0; i < 8; i += 2) {
const x = uvs[i];
const y = uvs[i + 1];
// 多少ずれがあってもいいように(例えばblenderではUV展開時にデフォルトでわずかなマージンを追加する)、中心より大きいか/小さいかで判定する
if (x < 0.5 && y < 0.5) {
aIndex = i;
} else if (x > 0.5 && y < 0.5) {
bIndex = i;
} else if (x < 0.5 && y > 0.5) {
cIndex = i;
} else if (x > 0.5 && y > 0.5) {
dIndex = i;
}
}
return [aIndex, bIndex, cIndex, dIndex];
}