зеркало из https://github.com/mozilla/Spoke.git
Merge branch 'master' into feature/link-thumbnails
This commit is contained in:
Коммит
ae7e808a28
|
@ -2,6 +2,6 @@ HUBS_SERVER="dev.reticulum.io"
|
|||
RETICULUM_SERVER="dev.reticulum.io"
|
||||
FARSPARK_SERVER="farspark-dev.reticulum.io"
|
||||
NON_CORS_PROXY_DOMAINS="hubs.local,localhost"
|
||||
CORS_PROXY_SERVER="hubs-proxy.com"
|
||||
CORS_PROXY_SERVER="cors-proxy-dev.reticulum.io"
|
||||
HOST_PORT="9090"
|
||||
IS_MOZ="false"
|
||||
|
|
|
@ -891,7 +891,7 @@ export default class Project extends EventEmitter {
|
|||
allow_promotion: publishParams.allowPromotion,
|
||||
name: publishParams.name,
|
||||
attributions: {
|
||||
creator: publishParams.creatorAttribution,
|
||||
creator: publishParams.creatorAttribution && publishParams.creatorAttribution.trim(),
|
||||
content: publishParams.contentAttributions
|
||||
}
|
||||
};
|
||||
|
|
|
@ -8,12 +8,18 @@ import mergeMeshGeometries from "../utils/mergeMeshGeometries";
|
|||
import RecastClient from "../recast/RecastClient";
|
||||
import HeightfieldClient from "../heightfield/HeightfieldClient";
|
||||
import SpawnPointNode from "../nodes/SpawnPointNode";
|
||||
import { RethrownError } from "../utils/errors";
|
||||
import * as recastWasmUrl from "recast-wasm/dist/recast.wasm";
|
||||
import traverseFilteredSubtrees from "../utils/traverseFilteredSubtrees";
|
||||
|
||||
const recastClient = new RecastClient();
|
||||
const heightfieldClient = new HeightfieldClient();
|
||||
|
||||
export const NavMeshMode = {
|
||||
Automatic: "automatic",
|
||||
Custom: "custom"
|
||||
};
|
||||
|
||||
export default class FloorPlanNode extends EditorNodeMixin(FloorPlan) {
|
||||
static nodeName = "Floor Plan";
|
||||
|
||||
|
@ -25,7 +31,7 @@ export default class FloorPlanNode extends EditorNodeMixin(FloorPlan) {
|
|||
return editor.scene.findNodeByType(FloorPlanNode) === null;
|
||||
}
|
||||
|
||||
static async deserialize(editor, json) {
|
||||
static async deserialize(editor, json, loadAsync, onError) {
|
||||
const node = await super.deserialize(editor, json);
|
||||
|
||||
const {
|
||||
|
@ -38,7 +44,9 @@ export default class FloorPlanNode extends EditorNodeMixin(FloorPlan) {
|
|||
agentMaxSlope,
|
||||
regionMinSize,
|
||||
maxTriangles,
|
||||
forceTrimesh
|
||||
forceTrimesh,
|
||||
navMeshMode,
|
||||
navMeshSrc
|
||||
} = json.components.find(c => c.name === "floor-plan").props;
|
||||
|
||||
node.autoCellSize = autoCellSize;
|
||||
|
@ -52,6 +60,14 @@ export default class FloorPlanNode extends EditorNodeMixin(FloorPlan) {
|
|||
node.maxTriangles = maxTriangles || 1000;
|
||||
node.forceTrimesh = forceTrimesh || false;
|
||||
|
||||
node._navMeshMode = navMeshMode || NavMeshMode.Automatic;
|
||||
|
||||
if (navMeshMode === NavMeshMode.Custom) {
|
||||
loadAsync(node.load(navMeshSrc, onError));
|
||||
} else {
|
||||
node._navMeshSrc = navMeshSrc || "";
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
|
@ -68,6 +84,88 @@ export default class FloorPlanNode extends EditorNodeMixin(FloorPlan) {
|
|||
this.maxTriangles = 1000;
|
||||
this.forceTrimesh = false;
|
||||
this.heightfieldMesh = null;
|
||||
this._navMeshMode = NavMeshMode.Automatic;
|
||||
this._navMeshSrc = "";
|
||||
this.navMesh = null;
|
||||
this.trimesh = null;
|
||||
this.heightfieldMesh = null;
|
||||
}
|
||||
|
||||
get navMeshMode() {
|
||||
return this._navMeshMode;
|
||||
}
|
||||
|
||||
set navMeshMode(value) {
|
||||
if (value === NavMeshMode.Custom) {
|
||||
// Force reloading nav mesh since it was removed and this._navMeshSrc didn't change
|
||||
this.load(this._navMeshSrc, undefined, true).catch(console.error);
|
||||
} else if (this.navMesh) {
|
||||
this.remove(this.navMesh);
|
||||
this.navMesh = null;
|
||||
}
|
||||
|
||||
this._navMeshMode = value;
|
||||
}
|
||||
|
||||
get navMeshSrc() {
|
||||
return this._navMeshSrc;
|
||||
}
|
||||
|
||||
set navMeshSrc(value) {
|
||||
this.load(value).catch(console.error);
|
||||
}
|
||||
|
||||
async load(src, onError, force = false) {
|
||||
const nextSrc = src || "";
|
||||
|
||||
if (nextSrc === this._navMeshSrc && nextSrc !== "" && !force) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._navMeshSrc = nextSrc;
|
||||
this.issues = [];
|
||||
|
||||
if (this.navMesh) {
|
||||
this.remove(this.navMesh);
|
||||
this.navMesh = null;
|
||||
}
|
||||
|
||||
try {
|
||||
const { accessibleUrl } = await this.editor.api.resolveMedia(nextSrc);
|
||||
|
||||
const loader = this.editor.gltfCache.getLoader(accessibleUrl);
|
||||
|
||||
const { scene } = await loader.getDependency("gltf");
|
||||
|
||||
const mesh = scene.getObjectByProperty("type", "Mesh");
|
||||
|
||||
if (!mesh) {
|
||||
throw new Error("No mesh available.");
|
||||
}
|
||||
|
||||
const geometry = mesh.geometry.clone(); // Clone in case the user reuses a mesh for the navmesh.
|
||||
mesh.updateMatrixWorld();
|
||||
geometry.applyMatrix(mesh.matrixWorld);
|
||||
|
||||
this.setNavMesh(new Mesh(geometry, new MeshBasicMaterial({ color: 0x0000ff, transparent: true, opacity: 0.2 })));
|
||||
|
||||
if (this.navMesh) {
|
||||
this.navMesh.visible = this.editor.selected.indexOf(this) !== -1;
|
||||
}
|
||||
} catch (error) {
|
||||
const modelError = new RethrownError(`Error loading custom navmesh "${this._navMeshSrc}"`, error);
|
||||
|
||||
if (onError) {
|
||||
onError(this, modelError);
|
||||
}
|
||||
|
||||
console.error(modelError);
|
||||
|
||||
this.issues.push({ severity: "error", message: "Error loading custom navmesh." });
|
||||
}
|
||||
|
||||
this.editor.emit("objectsChanged", [this]);
|
||||
this.editor.emit("selectionChanged");
|
||||
}
|
||||
|
||||
onSelect() {
|
||||
|
@ -99,7 +197,6 @@ export default class FloorPlanNode extends EditorNodeMixin(FloorPlan) {
|
|||
}
|
||||
|
||||
async generate(signal) {
|
||||
window.scene = this;
|
||||
const collidableMeshes = [];
|
||||
const walkableMeshes = [];
|
||||
|
||||
|
@ -129,56 +226,63 @@ export default class FloorPlanNode extends EditorNodeMixin(FloorPlan) {
|
|||
}
|
||||
});
|
||||
|
||||
const boxColliderNodes = this.editor.scene.getNodesByType(BoxColliderNode, false);
|
||||
if (this.navMeshMode === NavMeshMode.Automatic) {
|
||||
const boxColliderNodes = this.editor.scene.getNodesByType(BoxColliderNode, false);
|
||||
|
||||
for (const node of boxColliderNodes) {
|
||||
if (node.walkable) {
|
||||
const helperMesh = node.helper.object;
|
||||
const boxColliderMesh = new Mesh(helperMesh.geometry, new MeshBasicMaterial());
|
||||
boxColliderMesh.applyMatrix(node.matrixWorld);
|
||||
boxColliderMesh.updateMatrixWorld();
|
||||
walkableMeshes.push(boxColliderMesh);
|
||||
for (const node of boxColliderNodes) {
|
||||
if (node.walkable) {
|
||||
const helperMesh = node.helper.object;
|
||||
const boxColliderMesh = new Mesh(helperMesh.geometry, new MeshBasicMaterial());
|
||||
boxColliderMesh.applyMatrix(node.matrixWorld);
|
||||
boxColliderMesh.updateMatrixWorld();
|
||||
walkableMeshes.push(boxColliderMesh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const walkableGeometry = mergeMeshGeometries(walkableMeshes);
|
||||
const walkableGeometry = mergeMeshGeometries(walkableMeshes);
|
||||
|
||||
const box = new Box3().setFromBufferAttribute(walkableGeometry.attributes.position);
|
||||
const size = new Vector3();
|
||||
box.getSize(size);
|
||||
if (Math.max(size.x, size.y, size.z) > 2000) {
|
||||
throw new Error(
|
||||
`Scene is too large (${size.x.toFixed(3)} x ${size.y.toFixed(3)} x ${size.z.toFixed(3)}) ` +
|
||||
`to generate a floor plan.\n` +
|
||||
`You can un-check the "walkable" checkbox on models to exclude them from the floor plan.`
|
||||
const box = new Box3().setFromBufferAttribute(walkableGeometry.attributes.position);
|
||||
const size = new Vector3();
|
||||
box.getSize(size);
|
||||
if (Math.max(size.x, size.y, size.z) > 2000) {
|
||||
throw new Error(
|
||||
`Scene is too large (${size.x.toFixed(3)} x ${size.y.toFixed(3)} x ${size.z.toFixed(3)}) ` +
|
||||
`to generate a floor plan.\n` +
|
||||
`You can un-check the "walkable" checkbox on models to exclude them from the floor plan.`
|
||||
);
|
||||
}
|
||||
|
||||
const area = size.x * size.z;
|
||||
|
||||
// Tuned to produce cell sizes from ~0.5 to ~1.5 for areas from ~200 to ~350,000.
|
||||
const cellSize = this.autoCellSize ? Math.pow(area, 1 / 3) / 50 : this.cellSize;
|
||||
|
||||
const navGeometry = await recastClient.buildNavMesh(
|
||||
walkableGeometry,
|
||||
{
|
||||
cellSize,
|
||||
cellHeight: this.cellHeight,
|
||||
agentHeight: this.agentHeight,
|
||||
agentRadius: this.agentRadius,
|
||||
agentMaxClimb: this.agentMaxClimb,
|
||||
agentMaxSlope: this.agentMaxSlope,
|
||||
regionMinSize: this.regionMinSize,
|
||||
wasmUrl: new URL(recastWasmUrl, configs.BASE_ASSETS_PATH || window.location).href
|
||||
},
|
||||
signal
|
||||
);
|
||||
|
||||
const navMesh = new Mesh(
|
||||
navGeometry,
|
||||
new MeshBasicMaterial({ color: 0x0000ff, transparent: true, opacity: 0.2 })
|
||||
);
|
||||
|
||||
this.setNavMesh(navMesh);
|
||||
}
|
||||
|
||||
const area = size.x * size.z;
|
||||
|
||||
// Tuned to produce cell sizes from ~0.5 to ~1.5 for areas from ~200 to ~350,000.
|
||||
const cellSize = this.autoCellSize ? Math.pow(area, 1 / 3) / 50 : this.cellSize;
|
||||
|
||||
const navGeometry = await recastClient.buildNavMesh(
|
||||
walkableGeometry,
|
||||
{
|
||||
cellSize,
|
||||
cellHeight: this.cellHeight,
|
||||
agentHeight: this.agentHeight,
|
||||
agentRadius: this.agentRadius,
|
||||
agentMaxClimb: this.agentMaxClimb,
|
||||
agentMaxSlope: this.agentMaxSlope,
|
||||
regionMinSize: this.regionMinSize,
|
||||
wasmUrl: new URL(recastWasmUrl, configs.BASE_ASSETS_PATH || window.location).href
|
||||
},
|
||||
signal
|
||||
);
|
||||
|
||||
const navMesh = new Mesh(navGeometry, new MeshBasicMaterial({ color: 0x0000ff, transparent: true, opacity: 0.2 }));
|
||||
|
||||
this.setNavMesh(navMesh);
|
||||
|
||||
navMesh.visible = this.editor.selected.indexOf(this) !== -1;
|
||||
if (this.navMesh) {
|
||||
this.navMesh.visible = this.editor.selected.indexOf(this) !== -1;
|
||||
}
|
||||
|
||||
const collidableGeometry = mergeMeshGeometries(collidableMeshes);
|
||||
|
||||
|
@ -252,6 +356,7 @@ export default class FloorPlanNode extends EditorNodeMixin(FloorPlan) {
|
|||
copy(source, recursive = true) {
|
||||
if (recursive) {
|
||||
this.remove(this.heightfieldMesh);
|
||||
this.remove(this.navMesh);
|
||||
}
|
||||
|
||||
super.copy(source, recursive);
|
||||
|
@ -262,6 +367,12 @@ export default class FloorPlanNode extends EditorNodeMixin(FloorPlan) {
|
|||
if (heightfieldMeshIndex !== -1) {
|
||||
this.heightfieldMesh = this.children[heightfieldMeshIndex];
|
||||
}
|
||||
|
||||
const navMeshIndex = source.children.findIndex(child => child === source.navMesh);
|
||||
|
||||
if (navMeshIndex !== -1) {
|
||||
this.navMesh = this.children[navMeshIndex];
|
||||
}
|
||||
}
|
||||
|
||||
this.autoCellSize = source.autoCellSize;
|
||||
|
@ -274,6 +385,9 @@ export default class FloorPlanNode extends EditorNodeMixin(FloorPlan) {
|
|||
this.regionMinSize = source.regionMinSize;
|
||||
this.maxTriangles = source.maxTriangles;
|
||||
this.forceTrimesh = source.forceTrimesh;
|
||||
this._navMeshMode = source._navMeshMode;
|
||||
this._navMeshSrc = source._navMeshSrc;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -289,7 +403,9 @@ export default class FloorPlanNode extends EditorNodeMixin(FloorPlan) {
|
|||
agentMaxSlope: this.agentMaxSlope,
|
||||
regionMinSize: this.regionMinSize,
|
||||
maxTriangles: this.maxTriangles,
|
||||
forceTrimesh: this.forceTrimesh
|
||||
forceTrimesh: this.forceTrimesh,
|
||||
navMeshMode: this.navMeshMode,
|
||||
navMeshSrc: this.navMeshSrc
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -301,9 +417,14 @@ export default class FloorPlanNode extends EditorNodeMixin(FloorPlan) {
|
|||
this.remove(this.heightfieldMesh);
|
||||
}
|
||||
|
||||
const navMeshMaterial = this.navMesh.material;
|
||||
if (!this.navMesh && this.navMeshMode === NavMeshMode.Custom) {
|
||||
throw new Error("The FloorPlan Node is set to use a custom navigation mesh but none was provided.");
|
||||
}
|
||||
|
||||
const navMeshMaterial = this.navMesh.material.clone();
|
||||
navMeshMaterial.transparent = true;
|
||||
navMeshMaterial.opacity = 0;
|
||||
this.navMesh.material = navMeshMaterial;
|
||||
|
||||
this.navMesh.name = "navMesh";
|
||||
this.navMesh.userData.gltfExtensions = {
|
||||
|
@ -315,10 +436,11 @@ export default class FloorPlanNode extends EditorNodeMixin(FloorPlan) {
|
|||
|
||||
if (this.trimesh) {
|
||||
this.trimesh.name = "trimesh";
|
||||
const trimeshMaterial = this.trimesh.material;
|
||||
const trimeshMaterial = this.trimesh.material.clone();
|
||||
trimeshMaterial.transparent = true;
|
||||
trimeshMaterial.opacity = 0;
|
||||
trimeshMaterial.wireframe = false;
|
||||
this.trimesh.material = trimeshMaterial;
|
||||
|
||||
this.trimesh.userData.gltfExtensions = {
|
||||
MOZ_hubs_components: {
|
||||
|
|
|
@ -656,7 +656,13 @@ export default class SceneNode extends EditorNodeMixin(Scene) {
|
|||
|
||||
if (!attribution) return;
|
||||
|
||||
const attributionKey = attribution.url || `${attribution.title}_${attribution.author}`;
|
||||
const url = attribution.url && attribution.url.trim();
|
||||
const title = attribution.title && attribution.title.trim();
|
||||
const author = attribution.author && attribution.author.trim();
|
||||
|
||||
if (!url && !title && !author) return;
|
||||
|
||||
const attributionKey = url || `${title}_${author}`;
|
||||
if (seenAttributions.has(attributionKey)) return;
|
||||
seenAttributions.add(attributionKey);
|
||||
contentAttributions.push(attribution);
|
||||
|
|
|
@ -10,6 +10,20 @@ import ErrorDialog from "../dialogs/ErrorDialog";
|
|||
import { withDialog } from "../contexts/DialogContext";
|
||||
import { withSettings } from "../contexts/SettingsContext";
|
||||
import { ShoePrints } from "styled-icons/fa-solid/ShoePrints";
|
||||
import { NavMeshMode } from "../../editor/nodes/FloorPlanNode";
|
||||
import SelectInput from "../inputs/SelectInput";
|
||||
import ModelInput from "../inputs/ModelInput";
|
||||
|
||||
const NavMeshModeOptions = [
|
||||
{
|
||||
label: "Automatic",
|
||||
value: NavMeshMode.Automatic
|
||||
},
|
||||
{
|
||||
label: "Custom",
|
||||
value: NavMeshMode.Custom
|
||||
}
|
||||
];
|
||||
|
||||
class FloorPlanNodeEditor extends Component {
|
||||
static propTypes = {
|
||||
|
@ -37,6 +51,8 @@ class FloorPlanNodeEditor extends Component {
|
|||
this.onChangeRegionMinSize = createPropSetter("regionMinSize");
|
||||
this.onChangeMaxTriangles = createPropSetter("maxTriangles");
|
||||
this.onChangeForceTrimesh = createPropSetter("forceTrimesh");
|
||||
this.onChangeNavMeshMode = createPropSetter("navMeshMode");
|
||||
this.onChangeNavMeshSrc = createPropSetter("navMeshSrc");
|
||||
}
|
||||
|
||||
onRegenerate = async () => {
|
||||
|
@ -72,82 +88,93 @@ class FloorPlanNodeEditor extends Component {
|
|||
|
||||
return (
|
||||
<NodeEditor {...this.props} description={FloorPlanNodeEditor.description}>
|
||||
<InputGroup name="Auto Cell Size">
|
||||
<BooleanInput value={node.autoCellSize} onChange={this.onChangeAutoCellSize} />
|
||||
<InputGroup name="Nav Mesh Mode">
|
||||
<SelectInput options={NavMeshModeOptions} value={node.navMeshMode} onChange={this.onChangeNavMeshMode} />
|
||||
</InputGroup>
|
||||
{!node.autoCellSize && (
|
||||
<NumericInputGroup
|
||||
name="Cell Size"
|
||||
value={node.cellSize}
|
||||
smallStep={0.001}
|
||||
mediumStep={0.01}
|
||||
largeStep={0.1}
|
||||
min={0.1}
|
||||
displayPrecision={0.0001}
|
||||
onChange={this.onChangeCellSize}
|
||||
/>
|
||||
{node.navMeshMode === NavMeshMode.Automatic ? (
|
||||
<>
|
||||
<InputGroup name="Auto Cell Size">
|
||||
<BooleanInput value={node.autoCellSize} onChange={this.onChangeAutoCellSize} />
|
||||
</InputGroup>
|
||||
{!node.autoCellSize && (
|
||||
<NumericInputGroup
|
||||
name="Cell Size"
|
||||
value={node.cellSize}
|
||||
smallStep={0.001}
|
||||
mediumStep={0.01}
|
||||
largeStep={0.1}
|
||||
min={0.1}
|
||||
displayPrecision={0.0001}
|
||||
onChange={this.onChangeCellSize}
|
||||
/>
|
||||
)}
|
||||
<NumericInputGroup
|
||||
name="Cell Height"
|
||||
value={node.cellHeight}
|
||||
smallStep={0.001}
|
||||
mediumStep={0.01}
|
||||
largeStep={0.1}
|
||||
min={0.1}
|
||||
onChange={this.onChangeCellHeight}
|
||||
unit="m"
|
||||
/>
|
||||
<NumericInputGroup
|
||||
name="Agent Height"
|
||||
value={node.agentHeight}
|
||||
smallStep={0.001}
|
||||
mediumStep={0.01}
|
||||
largeStep={0.1}
|
||||
min={0.1}
|
||||
onChange={this.onChangeAgentHeight}
|
||||
unit="m"
|
||||
/>
|
||||
<NumericInputGroup
|
||||
name="Agent Radius"
|
||||
value={node.agentRadius}
|
||||
min={0}
|
||||
smallStep={0.001}
|
||||
mediumStep={0.01}
|
||||
largeStep={0.1}
|
||||
onChange={this.onChangeAgentRadius}
|
||||
unit="m"
|
||||
/>
|
||||
<NumericInputGroup
|
||||
name="Maximum Step Height"
|
||||
value={node.agentMaxClimb}
|
||||
min={0}
|
||||
smallStep={0.001}
|
||||
mediumStep={0.01}
|
||||
largeStep={0.1}
|
||||
onChange={this.onChangeAgentMaxClimb}
|
||||
unit="m"
|
||||
/>
|
||||
<NumericInputGroup
|
||||
name="Maximum Slope"
|
||||
value={node.agentMaxSlope}
|
||||
min={0.00001}
|
||||
max={90}
|
||||
smallStep={1}
|
||||
mediumStep={5}
|
||||
largeStep={15}
|
||||
onChange={this.onChangeAgentMaxSlope}
|
||||
unit="°"
|
||||
/>
|
||||
<NumericInputGroup
|
||||
name="Minimum Region Area"
|
||||
value={node.regionMinSize}
|
||||
min={0.1}
|
||||
smallStep={0.1}
|
||||
mediumStep={1}
|
||||
largeStep={10}
|
||||
onChange={this.onChangeRegionMinSize}
|
||||
unit="m²"
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<InputGroup name="Custom Navmesh Url">
|
||||
<ModelInput value={node.navMeshSrc} onChange={this.onChangeNavMeshSrc} />
|
||||
</InputGroup>
|
||||
)}
|
||||
<NumericInputGroup
|
||||
name="Cell Height"
|
||||
value={node.cellHeight}
|
||||
smallStep={0.001}
|
||||
mediumStep={0.01}
|
||||
largeStep={0.1}
|
||||
min={0.1}
|
||||
onChange={this.onChangeCellHeight}
|
||||
unit="m"
|
||||
/>
|
||||
<NumericInputGroup
|
||||
name="Agent Height"
|
||||
value={node.agentHeight}
|
||||
smallStep={0.001}
|
||||
mediumStep={0.01}
|
||||
largeStep={0.1}
|
||||
min={0.1}
|
||||
onChange={this.onChangeAgentHeight}
|
||||
unit="m"
|
||||
/>
|
||||
<NumericInputGroup
|
||||
name="Agent Radius"
|
||||
value={node.agentRadius}
|
||||
min={0}
|
||||
smallStep={0.001}
|
||||
mediumStep={0.01}
|
||||
largeStep={0.1}
|
||||
onChange={this.onChangeAgentRadius}
|
||||
unit="m"
|
||||
/>
|
||||
<NumericInputGroup
|
||||
name="Maximum Step Height"
|
||||
value={node.agentMaxClimb}
|
||||
min={0}
|
||||
smallStep={0.001}
|
||||
mediumStep={0.01}
|
||||
largeStep={0.1}
|
||||
onChange={this.onChangeAgentMaxClimb}
|
||||
unit="m"
|
||||
/>
|
||||
<NumericInputGroup
|
||||
name="Maximum Slope"
|
||||
value={node.agentMaxSlope}
|
||||
min={0.00001}
|
||||
max={90}
|
||||
smallStep={1}
|
||||
mediumStep={5}
|
||||
largeStep={15}
|
||||
onChange={this.onChangeAgentMaxSlope}
|
||||
unit="°"
|
||||
/>
|
||||
<NumericInputGroup
|
||||
name="Minimum Region Area"
|
||||
value={node.regionMinSize}
|
||||
min={0.1}
|
||||
smallStep={0.1}
|
||||
mediumStep={1}
|
||||
largeStep={10}
|
||||
onChange={this.onChangeRegionMinSize}
|
||||
unit="m²"
|
||||
/>
|
||||
<InputGroup name="Force Trimesh">
|
||||
<BooleanInput value={node.forceTrimesh} onChange={this.onChangeForceTrimesh} />
|
||||
</InputGroup>
|
||||
|
|
|
@ -58,7 +58,7 @@ export default class SpotLightNodeEditor extends Component {
|
|||
largeStep={0.1}
|
||||
value={node.intensity}
|
||||
onChange={this.onChangeIntensity}
|
||||
unit="°"
|
||||
unit="cd"
|
||||
/>
|
||||
<RadianNumericInputGroup
|
||||
name="Inner Cone Angle"
|
||||
|
|
|
@ -732,6 +732,8 @@ Generated by [AVA](https://avajs.dev).
|
|||
cellSize: 1,
|
||||
forceTrimesh: false,
|
||||
maxTriangles: 1000,
|
||||
navMeshMode: 'automatic',
|
||||
navMeshSrc: '',
|
||||
regionMinSize: 4,
|
||||
},
|
||||
},
|
||||
|
@ -1420,6 +1422,8 @@ Generated by [AVA](https://avajs.dev).
|
|||
cellSize: 0.1200000000000001,
|
||||
forceTrimesh: false,
|
||||
maxTriangles: 1000,
|
||||
navMeshMode: 'automatic',
|
||||
navMeshSrc: '',
|
||||
regionMinSize: 4,
|
||||
},
|
||||
},
|
||||
|
@ -2730,6 +2734,8 @@ Generated by [AVA](https://avajs.dev).
|
|||
cellSize: 0.1200000000000001,
|
||||
forceTrimesh: false,
|
||||
maxTriangles: 1000,
|
||||
navMeshMode: 'automatic',
|
||||
navMeshSrc: '',
|
||||
regionMinSize: 4,
|
||||
},
|
||||
},
|
||||
|
@ -4115,6 +4121,8 @@ Generated by [AVA](https://avajs.dev).
|
|||
cellSize: 0.1200000000000001,
|
||||
forceTrimesh: false,
|
||||
maxTriangles: 1000,
|
||||
navMeshMode: 'automatic',
|
||||
navMeshSrc: '',
|
||||
regionMinSize: 4,
|
||||
},
|
||||
},
|
||||
|
|
Двоичные данные
test/integration/snapshots/Editor.test.js.snap
Двоичные данные
test/integration/snapshots/Editor.test.js.snap
Двоичный файл не отображается.
Загрузка…
Ссылка в новой задаче