threepipe
Version:
A 3D viewer framework built on top of three.js in TypeScript with a focus on quality rendering, modularity and extensibility.
241 lines • 9.2 kB
JavaScript
import { Vector3 } from 'three';
import { ThreeViewer } from '../../viewer';
export function makeICameraCommonUiConfig(config) {
return [
{
type: 'button',
label: 'Set View',
value: () => {
this.setViewToMain({ ui: true });
config.uiRefresh?.(true, 'postFrame'); // config is parent config
},
},
{
type: 'button',
label: 'Activate main',
hidden: () => this?.isMainCamera,
value: () => {
this.activateMain({ ui: true });
config.uiRefresh?.(true, 'postFrame');
},
},
{
type: 'button',
label: 'Deactivate main',
hidden: () => !this?.isMainCamera,
value: () => {
this.deactivateMain({ ui: true });
config.uiRefresh?.(true, 'postFrame');
},
},
{
type: 'checkbox',
label: 'Auto LookAt Target',
getValue: () => this.userData.autoLookAtTarget ?? false,
setValue: (v) => {
this.userData.autoLookAtTarget = v;
config.uiRefresh?.(true, 'postFrame');
},
},
];
}
export function makeIObject3DUiConfig(isMesh) {
if (!this)
return {};
if (this.uiConfig)
return this.uiConfig;
const config = {
type: 'folder',
label: () => this.name || 'unnamed',
expanded: true,
onChange: (ev) => {
if (!ev.config || ev.config.onChange)
return;
this.setDirty({ uiChangeEvent: ev, refreshScene: false, refreshUi: true });
},
children: [
{
type: 'checkbox',
label: 'Visible',
property: [this, 'visible'],
},
{
type: 'button',
label: 'Pick/Focus', // todo: move to the plugin that does the picking
value: () => {
this.dispatchEvent({ type: 'select', ui: true, object: this, bubbleToParent: true, focusCamera: true });
},
},
{
type: 'button',
label: 'Pick Parent', // todo: move to the plugin that does the picking
hidden: () => !this.parent,
value: () => {
const parent = this.parent;
if (parent) {
parent.dispatchEvent({ type: 'select', ui: true, bubbleToParent: true, object: parent });
}
},
},
{
type: 'input',
label: 'Name',
property: [this, 'name'],
onChange: (e) => {
if (e.last)
this.setDirty?.({ refreshScene: true, frameFade: false, refreshUi: true });
},
},
{
type: 'checkbox',
label: 'Casts Shadow',
hidden: () => !this.isMesh,
property: [this, 'castShadow'],
},
{
type: 'checkbox',
label: 'Receive Shadow',
hidden: () => !this.isMesh,
property: [this, 'receiveShadow'],
},
{
type: 'checkbox',
label: 'Frustum culled',
property: [this, 'frustumCulled'],
},
{
type: 'vec3',
label: 'Position',
property: [this, 'position'],
},
{
type: 'vec3',
label: 'Rotation',
property: [this, 'rotation'],
},
{
type: 'vec3',
label: 'Scale',
property: [this, 'scale'],
},
{
type: 'input',
label: 'Render Order',
property: [this, 'renderOrder'],
},
{
type: 'button',
label: 'Auto Scale',
hidden: () => !this.autoScale,
// prompt: ['Auto Scale Radius: Object will be scaled to the given radius', this.userData.autoScaleRadius || '2', true],
value: async () => {
const def = (this.userData.autoScaleRadius || 2) + '';
const res = await ThreeViewer.Dialog.prompt('Auto Scale Radius: Object will be scaled to the given radius', def);
if (res === null)
return;
const rad = parseFloat(res || def);
if (Math.abs(rad) > 0) {
return {
action: () => this.autoScale?.(rad),
undo: () => this.autoScale?.(rad, undefined, undefined, true),
};
}
},
},
{
type: 'button',
label: 'Auto Center',
value: () => {
// const res = await ThreeViewer.Dialog.confirm('Auto Center: Object will be centered, are you sure you want to proceed?')
// if (!res) return
return {
action: () => this.autoCenter?.(true),
undo: () => this.autoCenter?.(true, true),
};
},
},
{
type: 'button',
label: 'Pivot to Node Center',
value: async () => {
const res = await ThreeViewer.Dialog.confirm('Pivot to Center: Adjust the pivot to bounding box center. The object will rotate around the new pivot, are you sure you want to proceed?');
if (!res)
return;
return this.pivotToBoundsCenter?.(true); // return value is the undo function
},
},
{
type: 'folder',
label: 'Rotate model',
children: [
'X +', 'X -', 'Y +', 'Y -', 'Z +', 'Z -',
].map((l) => {
return {
type: 'button',
label: 'Rotate ' + l + '90',
value: () => {
const axis = new Vector3(l.includes('X') ? 1 : 0, l.includes('Y') ? 1 : 0, l.includes('Z') ? 1 : 0);
const angle = Math.PI / 2 * (l.includes('-') ? -1 : 1);
return {
action: () => {
this.rotateOnAxis(axis, angle);
this.setDirty?.({ refreshScene: true, refreshUi: false });
},
undo: () => {
this.rotateOnAxis(axis, -angle);
this.setDirty?.({ refreshScene: true, refreshUi: false });
},
};
},
};
}),
},
this.userData.license !== undefined ? {
type: 'input',
label: 'License/Credits',
property: [this.userData, 'license'],
} : {},
],
};
if ((this.isLine || this.isMesh) && isMesh !== false) {
// todo: move to make mesh ui function?
const ui = [
// morph targets
() => {
const dict = Object.entries(this.morphTargetDictionary || {});
return dict.length ? {
label: 'Morph Targets',
type: 'folder',
children: dict.map(([name, i]) => ({
type: 'slider',
label: name,
bounds: [0, 1],
stepSize: 0.0001,
property: [this.morphTargetInfluences, i],
onChange: (e) => {
this.setDirty?.({ refreshScene: e.last, frameFade: false, refreshUi: false });
},
})),
} : undefined;
},
// geometry
() => this.geometry?.uiConfig,
// material(s)
() => Array.isArray(this.material) ? this.material.length < 1 ? undefined : {
label: 'Materials',
type: 'folder',
children: this.material.map((a) => a?.uiConfig).filter(a => a),
} : this.material?.uiConfig,
];
config.children.push(...ui);
}
// todo: if we are replacing all the cameras in the scene, is this even required?
if (this.isCamera) {
const ui = makeICameraCommonUiConfig.call(this, config);
config.children.push(...ui);
}
// todo: lights?
this.uiConfig = config;
return config;
}
//# sourceMappingURL=IObjectUi.js.map