mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-05-21 13:25:45 +02:00
wip
This commit is contained in:
BIN
packages/frontend/assets/room/objects/poster/poster.blend
Normal file
BIN
packages/frontend/assets/room/objects/poster/poster.blend
Normal file
Binary file not shown.
BIN
packages/frontend/assets/room/objects/poster/poster.glb
Normal file
BIN
packages/frontend/assets/room/objects/poster/poster.glb
Normal file
Binary file not shown.
@@ -134,6 +134,7 @@ type ObjectDef<OpSc extends OptionsSchema = OptionsSchema> = {
|
|||||||
loaderResult: BABYLON.ISceneLoaderAsyncResult;
|
loaderResult: BABYLON.ISceneLoaderAsyncResult;
|
||||||
meshUpdated: () => void;
|
meshUpdated: () => void;
|
||||||
findMesh: (keyword: string) => BABYLON.Mesh;
|
findMesh: (keyword: string) => BABYLON.Mesh;
|
||||||
|
findMeshes: (keyword: string) => BABYLON.Mesh[];
|
||||||
findMaterial: (keyword: string) => BABYLON.PBRMaterial;
|
findMaterial: (keyword: string) => BABYLON.PBRMaterial;
|
||||||
}) => RoomObjectInstance<GetOptionsSchemaValues<OpSc>>;
|
}) => RoomObjectInstance<GetOptionsSchemaValues<OpSc>>;
|
||||||
};
|
};
|
||||||
@@ -911,6 +912,10 @@ export class RoomEngine {
|
|||||||
}
|
}
|
||||||
return mesh as BABYLON.Mesh;
|
return mesh as BABYLON.Mesh;
|
||||||
},
|
},
|
||||||
|
findMeshes: (keyword) => {
|
||||||
|
const meshes = root.getChildMeshes().filter(m => m.name.includes(keyword));
|
||||||
|
return meshes as BABYLON.Mesh[];
|
||||||
|
},
|
||||||
findMaterial: (keyword) => {
|
findMaterial: (keyword) => {
|
||||||
for (const m of root.getChildMeshes()) {
|
for (const m of root.getChildMeshes()) {
|
||||||
if (m.material == null) continue;
|
if (m.material == null) continue;
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import { petBottle } from './objects/petBottle.js';
|
|||||||
import { pictureFrame } from './objects/pictureFrame.js';
|
import { pictureFrame } from './objects/pictureFrame.js';
|
||||||
import { plant } from './objects/plant.js';
|
import { plant } from './objects/plant.js';
|
||||||
import { plant2 } from './objects/plant2.js';
|
import { plant2 } from './objects/plant2.js';
|
||||||
|
import { poster } from './objects/poster.js';
|
||||||
import { powerStrip } from './objects/powerStrip.js';
|
import { powerStrip } from './objects/powerStrip.js';
|
||||||
import { rolledUpPoster } from './objects/rolledUpPoster.js';
|
import { rolledUpPoster } from './objects/rolledUpPoster.js';
|
||||||
import { roundRug } from './objects/roundRug.js';
|
import { roundRug } from './objects/roundRug.js';
|
||||||
@@ -98,6 +99,7 @@ export const OBJECT_DEFS = [
|
|||||||
pictureFrame,
|
pictureFrame,
|
||||||
plant,
|
plant,
|
||||||
plant2,
|
plant2,
|
||||||
|
poster,
|
||||||
powerStrip,
|
powerStrip,
|
||||||
rolledUpPoster,
|
rolledUpPoster,
|
||||||
roundRug,
|
roundRug,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { defineObject } from '../engine.js';
|
|||||||
|
|
||||||
export const pictureFrame = defineObject({
|
export const pictureFrame = defineObject({
|
||||||
id: 'pictureFrame',
|
id: 'pictureFrame',
|
||||||
name: 'Rectangular picture frame',
|
name: 'Simple picture frame',
|
||||||
options: {
|
options: {
|
||||||
schema: {
|
schema: {
|
||||||
frameColor: {
|
frameColor: {
|
||||||
@@ -103,11 +103,10 @@ export const pictureFrame = defineObject({
|
|||||||
|
|
||||||
const srcWidth = tex.getSize().width;
|
const srcWidth = tex.getSize().width;
|
||||||
const srcHeight = tex.getSize().height;
|
const srcHeight = tex.getSize().height;
|
||||||
|
const srcAspect = srcWidth / srcHeight;
|
||||||
const targetWidth = options.width * (1 - (options.matHThickness * MAT_THICKNESS_FACTOR));
|
const targetWidth = options.width * (1 - (options.matHThickness * MAT_THICKNESS_FACTOR));
|
||||||
const targetHeight = options.height * (1 - (options.matVThickness * MAT_THICKNESS_FACTOR));
|
const targetHeight = options.height * (1 - (options.matVThickness * MAT_THICKNESS_FACTOR));
|
||||||
|
|
||||||
const targetAspect = targetWidth / targetHeight;
|
const targetAspect = targetWidth / targetHeight;
|
||||||
const srcAspect = srcWidth / srcHeight;
|
|
||||||
|
|
||||||
let newAx = ax;
|
let newAx = ax;
|
||||||
let newAy = ay;
|
let newAy = ay;
|
||||||
|
|||||||
189
packages/frontend/src/utility/room/objects/poster.ts
Normal file
189
packages/frontend/src/utility/room/objects/poster.ts
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as BABYLON from '@babylonjs/core';
|
||||||
|
import { defineObject } from '../engine.js';
|
||||||
|
|
||||||
|
export const poster = defineObject({
|
||||||
|
id: 'poster',
|
||||||
|
name: 'Poster',
|
||||||
|
options: {
|
||||||
|
schema: {
|
||||||
|
width: {
|
||||||
|
type: 'range',
|
||||||
|
label: 'Width',
|
||||||
|
min: 0,
|
||||||
|
max: 1,
|
||||||
|
step: 0.01,
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: 'range',
|
||||||
|
label: 'Height',
|
||||||
|
min: 0,
|
||||||
|
max: 1,
|
||||||
|
step: 0.01,
|
||||||
|
},
|
||||||
|
customPicture: {
|
||||||
|
type: 'image',
|
||||||
|
label: 'Custom picture',
|
||||||
|
},
|
||||||
|
fit: {
|
||||||
|
type: 'enum',
|
||||||
|
label: 'Custom picture fit',
|
||||||
|
enum: ['cover', 'contain', 'stretch'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: {
|
||||||
|
width: 0.15,
|
||||||
|
height: 0.15,
|
||||||
|
customPicture: null,
|
||||||
|
fit: 'cover',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
placement: 'side',
|
||||||
|
createInstance: ({ room, root, options, findMaterial, findMesh, findMeshes, meshUpdated }) => {
|
||||||
|
const pictureMesh = findMesh('__X_PICTURE__');
|
||||||
|
pictureMesh.rotationQuaternion = null;
|
||||||
|
pictureMesh.markVerticesDataAsUpdatable(BABYLON.VertexBuffer.UVKind, true);
|
||||||
|
|
||||||
|
const pictureMaterial = findMaterial('__X_PICTURE__');
|
||||||
|
|
||||||
|
const pinMeshes = findMeshes('__X_PIN__');
|
||||||
|
|
||||||
|
const uvs = pictureMesh.getVerticesData(BABYLON.VertexBuffer.UVKind);
|
||||||
|
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 applyFit = () => {
|
||||||
|
const tex = pictureMaterial.albedoTexture;
|
||||||
|
if (tex == null) return;
|
||||||
|
|
||||||
|
const srcWidth = tex.getSize().width;
|
||||||
|
const srcHeight = tex.getSize().height;
|
||||||
|
const srcAspect = srcWidth / srcHeight;
|
||||||
|
const targetWidth = options.width;
|
||||||
|
const targetHeight = options.height;
|
||||||
|
const targetAspect = targetWidth / targetHeight;
|
||||||
|
|
||||||
|
let newAx = ax;
|
||||||
|
let newAy = ay;
|
||||||
|
let newBx = bx;
|
||||||
|
let newBy = by;
|
||||||
|
let newCx = cx;
|
||||||
|
let newCy = cy;
|
||||||
|
let newDx = dx;
|
||||||
|
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);
|
||||||
|
} 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);
|
||||||
|
}
|
||||||
|
} 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);
|
||||||
|
} 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);
|
||||||
|
}
|
||||||
|
} else if (options.fit === 'stretch') {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
uvs[6] = newAx;
|
||||||
|
uvs[7] = newAy;
|
||||||
|
uvs[2] = newBx;
|
||||||
|
uvs[3] = newBy;
|
||||||
|
uvs[4] = newCx;
|
||||||
|
uvs[5] = newCy;
|
||||||
|
uvs[0] = newDx;
|
||||||
|
uvs[1] = newDy;
|
||||||
|
|
||||||
|
pictureMesh.updateVerticesData(BABYLON.VertexBuffer.UVKind, uvs);
|
||||||
|
};
|
||||||
|
|
||||||
|
applyFit();
|
||||||
|
|
||||||
|
const applySize = () => {
|
||||||
|
pictureMesh.morphTargetManager!.getTargetByName('Width')!.influence = options.width;
|
||||||
|
pictureMesh.morphTargetManager!.getTargetByName('Height')!.influence = options.height;
|
||||||
|
for (const pinMesh of pinMeshes) {
|
||||||
|
pinMesh.morphTargetManager!.getTargetByName('Width')!.influence = options.width;
|
||||||
|
pinMesh.morphTargetManager!.getTargetByName('Height')!.influence = options.height;
|
||||||
|
}
|
||||||
|
meshUpdated();
|
||||||
|
|
||||||
|
applyFit();
|
||||||
|
};
|
||||||
|
|
||||||
|
applySize();
|
||||||
|
|
||||||
|
const applyCustomPicture = () => {
|
||||||
|
if (options.customPicture != null) {
|
||||||
|
const tex = new BABYLON.Texture(options.customPicture, room.scene, false, false);
|
||||||
|
tex.wrapU = BABYLON.Texture.MIRROR_ADDRESSMODE;
|
||||||
|
tex.wrapV = BABYLON.Texture.MIRROR_ADDRESSMODE;
|
||||||
|
|
||||||
|
pictureMaterial.albedoColor = new BABYLON.Color3(1, 1, 1);
|
||||||
|
pictureMaterial.albedoTexture = tex;
|
||||||
|
|
||||||
|
applyFit();
|
||||||
|
|
||||||
|
tex.onLoadObservable.addOnce(() => {
|
||||||
|
applyFit();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
pictureMaterial.albedoColor = new BABYLON.Color3(0.5, 0.5, 0.5);
|
||||||
|
pictureMaterial.albedoTexture = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
applyCustomPicture();
|
||||||
|
|
||||||
|
return {
|
||||||
|
onInited: () => {
|
||||||
|
|
||||||
|
},
|
||||||
|
onOptionsUpdated: ([k, v]) => {
|
||||||
|
if (k === 'width' || k === 'height') {
|
||||||
|
applySize();
|
||||||
|
}
|
||||||
|
if (k === 'customPicture') {
|
||||||
|
applyCustomPicture();
|
||||||
|
}
|
||||||
|
if (k === 'fit') {
|
||||||
|
applyFit();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
interactions: {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user