import { Component } from "@uino/thing";
import { Utils, getTransformOffset } from "../common/Utils";
const __ = {
private: Symbol('private'),
}
let _defaultQuat = [0, 0, 0, 1];
const emptyParam = {};
/**
* 房间平面组件
* @class PlaneComponent
* @extends THING.Component
* @public
* @example
* // 1. 单独使用(不作为房间的子)
* // 模拟数据
* const points = [[0, 0], [2, 0], [2, 2], [0, 2]]
* const holes = [[[0.25, 0.25], [0.75, 0.25], [0.75, 0.75], [0.25, 0.75],]]
* const materialResource = { sideType: 'Double', map: './images/camera.png' }
* // 创建对象作为平面
* const plane = new THING.Object3D();
* // 实例化平面组件
* const planeComponent = new THING.PlaneComponent();
* plane.addComponent(planeComponent, 'planeComponent');
* planeComponent.load({ points, holes, materialResource }).then(() => {
* console.log('平面加载完成');
* })
* // 2. 配合房间一起使用
* const roomFloorMaterialResource = { sideType: 'Double', map: './images/camera.png' }
* const roomCeilingMaterialResource = { sideType: 'Back', map: './images/camera.png' }
* const roomRoofMaterialResource = { sideType: 'Front', map: './images/camera.png' }
* // 创建房间
* const room = new THING.Room({
* position: [10, 0, 10],
* points,
* holes
* });
* // 创建对象作为房间地面
* const roomFloor = new THING.Object3D({
* parent: room,
* tags: ['BuildingElement', 'RoomFloor']
* });
* // 实例化平面组件
* const roomFloorComponent = new THING.PlaneComponent();
* roomFloor.addComponent(roomFloorComponent, 'planeComponent');
* roomFloorComponent.load({ materialResource: roomFloorMaterialResource }).then(() => {
* console.log('房间地面加载完成');
* })
* // 创建对象作为房间天花板
* const roomCeiling = new THING.Object3D({
* parent: room,
* localPosition: [0, 2.9, 0],
* tags: ['BuildingElement', 'RoomCeiling']
* });
* // 实例化平面组件
* const roomCeilingComponent = new THING.PlaneComponent();
* roomCeiling.addComponent(roomCeilingComponent, 'planeComponent');
* roomCeilingComponent.load({ materialResource: roomCeilingMaterialResource }).then(() => {
* console.log('房间天花板加载完成');
* })
* // 创建对象作为房间天花板
* const roomRoof = new THING.Object3D({
* parent: room,
* localPosition: [0, 3, 0],
* tags: ['BuildingElement', 'RoomRoof']
* });
* // 实例化平面组件
* const roomRoofComponent = new THING.PlaneComponent();
* roomRoof.addComponent(roomRoofComponent, 'planeComponent');
* roomRoofComponent.load({ materialResource: roomRoofMaterialResource }).then(() => {
* console.log('房间房顶加载完成');
* })
*/
class PlaneComponent extends Component {
/**
* Represents a PlaneComponent.
* @param {Object} param - The parameter object for the PlaneComponent.
*/
constructor(param = emptyParam) {
super(param);
let _private = this[__.private] = {
dynamic: param.dynamic,
points: null,
holes: null,
alwaysHide: false
};
_defaultQuat[1] = getTransformOffset();
//#region private
/**
* Sets the data of the private object.
* @param {Object} data - The data object to set.
*/
_private.setData = (data) => {
_private.materialResourceData = data['materialResource'];
_private.alwaysHide = Utils.parseBoolean(data.alwaysHide, false);
_private.uv = data.uv;
_private.selfPoints = data.points;
_private.selfHoles = data.holes;
if (!_private.selfPoints) {
_private.selfPoints = this.object.parent._selfPoints;
}
if (!_private.selfHoles) {
_private.selfHoles = this.object.parent._selfHoles;
}
_private.instanceGroupName = data.instanceGroupName;
}
/**
* Gets the data of the private object.
* @returns {Object} - The data object.
*/
_private.getData = () => {
const data = {}
if (_private.materialResourceData) {
data.materialResource = _private.materialResourceData;
}
if (_private.alwaysHide === true) {
data.alwaysHide = _private.alwaysHide;
}
// Use the parent data
let parentExternal = this.object.parent.external;
if (parentExternal && parentExternal.points) {
return data;
}
// Use self data
if (_private.selfPoints) {
data.points = _private.selfPoints;
}
if (_private.selfHoles) {
data.holes = _private.selfHoles;
}
return data;
}
/**
* Creates the plane component.
* @param {Object} object - The object to create the component on.
* @param {MaterialResource} materialResource - The material resource for the component.
*/
_private.create = function (object, materialResource) {
const node = Utils.createObject('ExtrudeShape', { materialResource });
node.setQuaternion(_defaultQuat);
object.body.setNode(node);
node.begin();
{
node.setPoints(Utils.cloneObject(_private.selfPoints, false));
if (_private.selfHoles) {
node.setHoles(Utils.cloneObject(_private.selfHoles, false));
}
}
node.end();
if (_private.uv) {
_private.updateVertexUV(object);
}
if (_private.instanceGroupName) {
object.instanceGroupName = _private.instanceGroupName;
object.makeInstancedDrawing(true);
}
}
_private.updateVertexUV = function (object) {
const geo = object.node._node.geometry;
const uvBuffer = geo.attributes.a_Uv.buffer;
const uvArray = uvBuffer.array;
const uvMatrix = getUVMatrix(_private.uv);
for (let i = 0; i < uvArray.length; i += 2) {
const x = uvArray[i];
const y = uvArray[i + 1];
const newUV = vec2ApplyMatrix3(x, y, uvMatrix);
// update uv
uvArray[i] = newUV[0];
uvArray[i + 1] = newUV[1];
}
uvBuffer.version++;
function getUVMatrix(uv) {
let offset = uv.offset || [0, 0];
let repeat = uv.repeat || [1, 1];
let center = uv.center || [0, 0];
let rotation = (uv.rotation || 0) * Math.PI / 180;
const c = Math.cos(rotation);
const s = Math.sin(rotation);
return [
repeat[0] * c, -repeat[1] * s, 0,
repeat[0] * s, repeat[1] * c, 0,
-repeat[0] * (c * center[0] + s * center[1]) + center[0] + offset[0],
-repeat[1] * (-s * center[0] + c * center[1]) + center[1] + offset[1], 1
];
}
function vec2ApplyMatrix3(x, y, m) {
const z = 1;
return [
m[0] * x + m[3] * y + m[6] * z,
m[1] * x + m[4] * y + m[7] * z
];
}
}
//#endregion
}
/**
* 加载平面
* @param {Object} data - 平面数据 (数据格式参考:https://wiki.uino.com/book/6538d1e88ea0ba892398fdf2/6526121eae83a4dfb2c2d08d.html)
* @returns
* @public
*/
load(data) {
const _private = this[__.private];
_private.setData(data);
return this.onLoadResource();
}
/**
* Handles the import of external data.
* @param {Object} external - The external data to import.
* @param {Object} options - The import options.
*/
onImport(external, options) {
super.onImport(external, options);
let _private = this[__.private];
_private.setData(external);
}
/**
* Handles the export of data.
* @param {Object} options - The export options.
* @returns {Object} - The exported data.
*/
onExport(options) {
super.onExport(options);
const data = this[__.private].getData();
return data;
}
/**
* Handles the loading of the resource.
*/
onLoadResource() {
let _private = this[__.private];
return new Promise((resolve, reject) => {
if (_private.alwaysHide) {
resolve();
return;
}
this.app._materialResourceLoader.createMaterialResource(_private.materialResourceData, (materialResource) => {
_private.create(this.object, materialResource);
resolve();
}, reject);
});
}
get alwaysHide() {
let _private = this[__.private];
return _private.alwaysHide;
}
}
Utils.registerClass("PlaneComponent", PlaneComponent);
export { PlaneComponent }