auto-git:
[change] src/app/App.tsx [change] src/app/app.css [change] src/assets/image-assets.ts [change] src/commands/create-custom-material-command.ts [change] src/commands/set-custom-material-texture-command.ts [change] src/commands/update-custom-material-command.ts [change] src/document/migrate-scene-document.ts [change] src/rendering/terrain-layer-material.ts [change] src/runtime-three/runtime-host.ts
This commit is contained in:
140
src/app/App.tsx
140
src/app/App.tsx
@@ -2913,11 +2913,12 @@ export function App({
|
||||
terrainList
|
||||
);
|
||||
const selectedPath = getSelectedPath(editorState.selection, pathList);
|
||||
const splineCorridorJunctionCandidates = detectSplineCorridorJunctionCandidates({
|
||||
paths: pathList,
|
||||
junctions: editorState.document.splineCorridorJunctions,
|
||||
terrains: terrainList.filter((terrain) => terrain.enabled)
|
||||
});
|
||||
const splineCorridorJunctionCandidates =
|
||||
detectSplineCorridorJunctionCandidates({
|
||||
paths: pathList,
|
||||
junctions: editorState.document.splineCorridorJunctions,
|
||||
terrains: terrainList.filter((terrain) => terrain.enabled)
|
||||
});
|
||||
const selectedPathJunctionCandidates =
|
||||
selectedPath === null
|
||||
? []
|
||||
@@ -9769,10 +9770,7 @@ export function App({
|
||||
|
||||
const handleTerrainPaintLayerChange = (value: string) => {
|
||||
setActiveTerrainPaintLayerIndex(
|
||||
clampTerrainPaintLayerIndex(
|
||||
Number(value),
|
||||
selectedTerrain?.layers.length
|
||||
)
|
||||
clampTerrainPaintLayerIndex(Number(value), selectedTerrain?.layers.length)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -10113,7 +10111,9 @@ export function App({
|
||||
|
||||
const handleDeleteSplineCorridorJunction = (junctionId: string) => {
|
||||
try {
|
||||
store.executeCommand(createDeleteSplineCorridorJunctionCommand(junctionId));
|
||||
store.executeCommand(
|
||||
createDeleteSplineCorridorJunctionCommand(junctionId)
|
||||
);
|
||||
setStatusMessage("Deleted spline corridor junction.");
|
||||
} catch (error) {
|
||||
setStatusMessage(getErrorMessage(error));
|
||||
@@ -21868,7 +21868,10 @@ export function App({
|
||||
<div className="terrain-layer-list">
|
||||
{selectedTerrain.layers.map((layer, layerIndex) => (
|
||||
<div key={layerIndex} className="form-field">
|
||||
<label className="label" htmlFor={`terrain-layer-material-${layerIndex}`}>
|
||||
<label
|
||||
className="label"
|
||||
htmlFor={`terrain-layer-material-${layerIndex}`}
|
||||
>
|
||||
{getTerrainLayerLabel(layerIndex)}
|
||||
</label>
|
||||
<div className="inline-actions">
|
||||
@@ -21895,7 +21898,9 @@ export function App({
|
||||
type="button"
|
||||
data-testid={`terrain-layer-remove-${layerIndex}`}
|
||||
disabled={layerIndex === 0}
|
||||
onClick={() => handleDeleteTerrainLayer(layerIndex)}
|
||||
onClick={() =>
|
||||
handleDeleteTerrainLayer(layerIndex)
|
||||
}
|
||||
>
|
||||
Remove
|
||||
</button>
|
||||
@@ -22865,10 +22870,9 @@ export function App({
|
||||
handlePathRoadChange(
|
||||
{
|
||||
...selectedPath.road,
|
||||
heightOffset:
|
||||
normalizeScenePathRoadHeightOffset(
|
||||
Number(event.currentTarget.value)
|
||||
)
|
||||
heightOffset: normalizeScenePathRoadHeightOffset(
|
||||
Number(event.currentTarget.value)
|
||||
)
|
||||
},
|
||||
"Updated Path road height offset."
|
||||
)
|
||||
@@ -22922,7 +22926,8 @@ export function App({
|
||||
</label>
|
||||
{(["left", "right"] as const).map((side) => {
|
||||
const edge = selectedPath.road.edges[side];
|
||||
const edgeLabel = side === "left" ? "Left Edge" : "Right Edge";
|
||||
const edgeLabel =
|
||||
side === "left" ? "Left Edge" : "Right Edge";
|
||||
|
||||
return (
|
||||
<div className="form-section" key={side}>
|
||||
@@ -23094,10 +23099,7 @@ export function App({
|
||||
{selectedPathJunctionCandidates
|
||||
.slice(0, 6)
|
||||
.map((candidate, candidateIndex) => (
|
||||
<div
|
||||
className="compact-card"
|
||||
key={candidate.id}
|
||||
>
|
||||
<div className="compact-card" key={candidate.id}>
|
||||
<div className="compact-card__header">
|
||||
<strong>
|
||||
Candidate {candidateIndex + 1}
|
||||
@@ -23292,11 +23294,13 @@ export function App({
|
||||
handleSplineCorridorJunctionChange(
|
||||
createSplineCorridorJunction({
|
||||
...junction,
|
||||
edge: createScenePathRoadEdgeSettings({
|
||||
...junction.edge,
|
||||
enabled:
|
||||
event.currentTarget.checked
|
||||
})
|
||||
edge: createScenePathRoadEdgeSettings(
|
||||
{
|
||||
...junction.edge,
|
||||
enabled:
|
||||
event.currentTarget.checked
|
||||
}
|
||||
)
|
||||
}),
|
||||
event.currentTarget.checked
|
||||
? "Enabled spline corridor junction edge."
|
||||
@@ -23315,11 +23319,13 @@ export function App({
|
||||
handleSplineCorridorJunctionChange(
|
||||
createSplineCorridorJunction({
|
||||
...junction,
|
||||
edge: createScenePathRoadEdgeSettings({
|
||||
...junction.edge,
|
||||
collisionEnabled:
|
||||
event.currentTarget.checked
|
||||
})
|
||||
edge: createScenePathRoadEdgeSettings(
|
||||
{
|
||||
...junction.edge,
|
||||
collisionEnabled:
|
||||
event.currentTarget.checked
|
||||
}
|
||||
)
|
||||
}),
|
||||
event.currentTarget.checked
|
||||
? "Enabled spline corridor junction edge collision."
|
||||
@@ -23338,12 +23344,14 @@ export function App({
|
||||
handleSplineCorridorJunctionChange(
|
||||
createSplineCorridorJunction({
|
||||
...junction,
|
||||
edge: createScenePathRoadEdgeSettings({
|
||||
...junction.edge,
|
||||
kind: normalizeScenePathRoadEdgeKind(
|
||||
event.currentTarget.value
|
||||
)
|
||||
})
|
||||
edge: createScenePathRoadEdgeSettings(
|
||||
{
|
||||
...junction.edge,
|
||||
kind: normalizeScenePathRoadEdgeKind(
|
||||
event.currentTarget.value
|
||||
)
|
||||
}
|
||||
)
|
||||
}),
|
||||
"Updated spline corridor junction edge profile."
|
||||
)
|
||||
@@ -23370,12 +23378,17 @@ export function App({
|
||||
handleSplineCorridorJunctionChange(
|
||||
createSplineCorridorJunction({
|
||||
...junction,
|
||||
edge: createScenePathRoadEdgeSettings({
|
||||
...junction.edge,
|
||||
width: normalizeScenePathRoadEdgeWidth(
|
||||
Number(event.currentTarget.value)
|
||||
)
|
||||
})
|
||||
edge: createScenePathRoadEdgeSettings(
|
||||
{
|
||||
...junction.edge,
|
||||
width:
|
||||
normalizeScenePathRoadEdgeWidth(
|
||||
Number(
|
||||
event.currentTarget.value
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}),
|
||||
"Updated spline corridor junction edge width."
|
||||
)
|
||||
@@ -23395,15 +23408,17 @@ export function App({
|
||||
handleSplineCorridorJunctionChange(
|
||||
createSplineCorridorJunction({
|
||||
...junction,
|
||||
edge: createScenePathRoadEdgeSettings({
|
||||
...junction.edge,
|
||||
height:
|
||||
normalizeScenePathRoadEdgeHeight(
|
||||
Number(
|
||||
event.currentTarget.value
|
||||
edge: createScenePathRoadEdgeSettings(
|
||||
{
|
||||
...junction.edge,
|
||||
height:
|
||||
normalizeScenePathRoadEdgeHeight(
|
||||
Number(
|
||||
event.currentTarget.value
|
||||
)
|
||||
)
|
||||
)
|
||||
})
|
||||
}
|
||||
)
|
||||
}),
|
||||
"Updated spline corridor junction edge height."
|
||||
)
|
||||
@@ -23420,13 +23435,15 @@ export function App({
|
||||
handleSplineCorridorJunctionChange(
|
||||
createSplineCorridorJunction({
|
||||
...junction,
|
||||
edge: createScenePathRoadEdgeSettings({
|
||||
...junction.edge,
|
||||
materialId:
|
||||
normalizeScenePathRoadEdgeMaterialId(
|
||||
event.currentTarget.value
|
||||
)
|
||||
})
|
||||
edge: createScenePathRoadEdgeSettings(
|
||||
{
|
||||
...junction.edge,
|
||||
materialId:
|
||||
normalizeScenePathRoadEdgeMaterialId(
|
||||
event.currentTarget.value
|
||||
)
|
||||
}
|
||||
)
|
||||
}),
|
||||
"Updated spline corridor junction edge material."
|
||||
)
|
||||
@@ -23904,8 +23921,8 @@ export function App({
|
||||
Edit authored point positions directly. Keep at least{" "}
|
||||
{MIN_SCENE_PATH_POINT_COUNT} points. <code>Shift+W</code>{" "}
|
||||
adds a point after the selected control point, or appends
|
||||
to the end when the whole path is selected. When a point is
|
||||
selected, <code>Shift+D</code> adds one and starts grab
|
||||
to the end when the whole path is selected. When a point
|
||||
is selected, <code>Shift+D</code> adds one and starts grab
|
||||
mode.
|
||||
</div>
|
||||
{selectedPathPointIndex === null ? null : (
|
||||
@@ -29382,8 +29399,7 @@ export function App({
|
||||
updateCustomMaterial(
|
||||
materialInspectorCustomMaterial,
|
||||
{
|
||||
albedoColorHex:
|
||||
event.currentTarget.value,
|
||||
albedoColorHex: event.currentTarget.value,
|
||||
swatchColorHex: event.currentTarget.value
|
||||
},
|
||||
"Update material albedo"
|
||||
|
||||
@@ -854,8 +854,11 @@ button:disabled {
|
||||
}
|
||||
|
||||
.material-item__preview--default {
|
||||
background:
|
||||
linear-gradient(135deg, rgba(255, 255, 255, 0.92), rgba(230, 235, 239, 0.98));
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(255, 255, 255, 0.92),
|
||||
rgba(230, 235, 239, 0.98)
|
||||
);
|
||||
}
|
||||
|
||||
.material-item__text {
|
||||
@@ -1824,11 +1827,7 @@ button:disabled {
|
||||
rgba(255, 255, 255, 0.22),
|
||||
transparent 46%
|
||||
),
|
||||
linear-gradient(
|
||||
180deg,
|
||||
rgba(255, 255, 255, 0.12),
|
||||
rgba(244, 246, 250, 0.2)
|
||||
);
|
||||
linear-gradient(180deg, rgba(255, 255, 255, 0.12), rgba(244, 246, 250, 0.2));
|
||||
backdrop-filter: blur(12px) saturate(0.72);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,11 @@ import {
|
||||
getStarterEnvironmentTextureUrl,
|
||||
isStarterEnvironmentImageAsset
|
||||
} from "./starter-environment-assets";
|
||||
import { createProjectAssetStorageKey, type ImageAssetMetadata, type ImageAssetRecord } from "./project-assets";
|
||||
import {
|
||||
createProjectAssetStorageKey,
|
||||
type ImageAssetMetadata,
|
||||
type ImageAssetRecord
|
||||
} from "./project-assets";
|
||||
import {
|
||||
type ProjectAssetStorage,
|
||||
type ProjectAssetStorageFileRecord,
|
||||
@@ -49,7 +53,10 @@ function getFileExtension(sourceName: string): string {
|
||||
return match === null ? "" : match[1].toLowerCase();
|
||||
}
|
||||
|
||||
function inferImageMimeType(sourceName: string, fallbackMimeType: string): string {
|
||||
function inferImageMimeType(
|
||||
sourceName: string,
|
||||
fallbackMimeType: string
|
||||
): string {
|
||||
if (fallbackMimeType.trim().startsWith("image/")) {
|
||||
return fallbackMimeType.trim();
|
||||
}
|
||||
@@ -73,7 +80,9 @@ function inferImageMimeType(sourceName: string, fallbackMimeType: string): strin
|
||||
case "webp":
|
||||
return "image/webp";
|
||||
default:
|
||||
throw new Error(`Unsupported image asset format for ${sourceName}. Use a browser-supported image file or .exr/.hdr.`);
|
||||
throw new Error(
|
||||
`Unsupported image asset format for ${sourceName}. Use a browser-supported image file or .exr/.hdr.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,27 +92,45 @@ function isHdrFormat(sourceName: string): boolean {
|
||||
}
|
||||
|
||||
function getImportedFilePath(file: File): string {
|
||||
const relativePath = typeof file.webkitRelativePath === "string" ? file.webkitRelativePath.trim() : "";
|
||||
const relativePath =
|
||||
typeof file.webkitRelativePath === "string"
|
||||
? file.webkitRelativePath.trim()
|
||||
: "";
|
||||
const sourcePath = relativePath.length > 0 ? relativePath : file.name.trim();
|
||||
return sourcePath.replace(/\\/gu, "/");
|
||||
}
|
||||
|
||||
function createDataUrlForStoredFile(file: ProjectAssetStorageFileRecord): string {
|
||||
function createDataUrlForStoredFile(
|
||||
file: ProjectAssetStorageFileRecord
|
||||
): string {
|
||||
const byteArray = new Uint8Array(file.bytes);
|
||||
let binary = "";
|
||||
const chunkSize = 0x8000;
|
||||
|
||||
for (let index = 0; index < byteArray.length; index += chunkSize) {
|
||||
binary += String.fromCharCode(...byteArray.subarray(index, index + chunkSize));
|
||||
binary += String.fromCharCode(
|
||||
...byteArray.subarray(index, index + chunkSize)
|
||||
);
|
||||
}
|
||||
|
||||
const base64 = typeof btoa === "function" ? btoa(binary) : Buffer.from(file.bytes).toString("base64");
|
||||
const base64 =
|
||||
typeof btoa === "function"
|
||||
? btoa(binary)
|
||||
: Buffer.from(file.bytes).toString("base64");
|
||||
return `data:${file.mimeType};base64,${base64}`;
|
||||
}
|
||||
|
||||
function createTransientResourceUrl(file: ProjectAssetStorageFileRecord): { revoke: () => void; url: string } {
|
||||
if (typeof URL.createObjectURL === "function" && typeof Blob !== "undefined") {
|
||||
const objectUrl = URL.createObjectURL(new Blob([file.bytes], { type: file.mimeType }));
|
||||
function createTransientResourceUrl(file: ProjectAssetStorageFileRecord): {
|
||||
revoke: () => void;
|
||||
url: string;
|
||||
} {
|
||||
if (
|
||||
typeof URL.createObjectURL === "function" &&
|
||||
typeof Blob !== "undefined"
|
||||
) {
|
||||
const objectUrl = URL.createObjectURL(
|
||||
new Blob([file.bytes], { type: file.mimeType })
|
||||
);
|
||||
|
||||
return {
|
||||
url: objectUrl,
|
||||
@@ -155,8 +182,14 @@ function loadImageElement(sourceUrl: string): Promise<HTMLImageElement> {
|
||||
|
||||
function detectImageHasAlpha(image: HTMLImageElement): boolean {
|
||||
const canvas = document.createElement("canvas");
|
||||
const sampleWidth = Math.max(1, Math.min(64, image.naturalWidth || image.width));
|
||||
const sampleHeight = Math.max(1, Math.min(64, image.naturalHeight || image.height));
|
||||
const sampleWidth = Math.max(
|
||||
1,
|
||||
Math.min(64, image.naturalWidth || image.width)
|
||||
);
|
||||
const sampleHeight = Math.max(
|
||||
1,
|
||||
Math.min(64, image.naturalHeight || image.height)
|
||||
);
|
||||
const context = canvas.getContext("2d", {
|
||||
willReadFrequently: true
|
||||
});
|
||||
@@ -191,7 +224,12 @@ function extractImageAssetMetadata(
|
||||
const width = image.naturalWidth || image.width;
|
||||
const height = image.naturalHeight || image.height;
|
||||
|
||||
if (!Number.isFinite(width) || width <= 0 || !Number.isFinite(height) || height <= 0) {
|
||||
if (
|
||||
!Number.isFinite(width) ||
|
||||
width <= 0 ||
|
||||
!Number.isFinite(height) ||
|
||||
height <= 0
|
||||
) {
|
||||
throw new Error("Imported image assets must have measurable dimensions.");
|
||||
}
|
||||
|
||||
@@ -199,7 +237,9 @@ function extractImageAssetMetadata(
|
||||
const aspectRatio = width / height;
|
||||
|
||||
if (options.backgroundWarnings && Math.abs(aspectRatio - 2) > 0.15) {
|
||||
warnings.push("Background images work best as a 2:1 equirectangular panorama.");
|
||||
warnings.push(
|
||||
"Background images work best as a 2:1 equirectangular panorama."
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -217,7 +257,9 @@ function extractHdrTextureMetadata(texture: DataTexture): ImageAssetMetadata {
|
||||
const warnings: string[] = [];
|
||||
|
||||
if (Math.abs(width / height - 2) > 0.15) {
|
||||
warnings.push("Background images work best as a 2:1 equirectangular panorama.");
|
||||
warnings.push(
|
||||
"Background images work best as a 2:1 equirectangular panorama."
|
||||
);
|
||||
}
|
||||
|
||||
return { kind: "image", width, height, hasAlpha: false, warnings };
|
||||
@@ -395,7 +437,10 @@ async function loadImageAssetFromPackageRecord(
|
||||
: createTransientResourceUrl(previewFileRecord);
|
||||
|
||||
try {
|
||||
const texture = await loadHdrTexture(transientResourceUrl.url, asset.sourceName);
|
||||
const texture = await loadHdrTexture(
|
||||
transientResourceUrl.url,
|
||||
asset.sourceName
|
||||
);
|
||||
return createLoadedHdrImageAsset(
|
||||
asset,
|
||||
texture,
|
||||
@@ -413,7 +458,9 @@ async function loadImageAssetFromPackageRecord(
|
||||
if (previewResourceUrl !== transientResourceUrl) {
|
||||
previewResourceUrl.revoke();
|
||||
}
|
||||
throw new Error(`Image asset reload failed for ${asset.sourceName}: ${getErrorDetail(error)}`);
|
||||
throw new Error(
|
||||
`Image asset reload failed for ${asset.sourceName}: ${getErrorDetail(error)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -428,7 +475,9 @@ async function loadImageAssetFromPackageRecord(
|
||||
);
|
||||
} catch (error) {
|
||||
transientResourceUrl.revoke();
|
||||
throw new Error(`Image asset reload failed for ${asset.sourceName}: ${getErrorDetail(error)}`);
|
||||
throw new Error(
|
||||
`Image asset reload failed for ${asset.sourceName}: ${getErrorDetail(error)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -452,11 +501,18 @@ export async function importBackgroundImageAssetFromFile(
|
||||
texture = await loadHdrTexture(transientResourceUrl.url, sourceName);
|
||||
} catch (error) {
|
||||
transientResourceUrl.revoke();
|
||||
throw new Error(`Image import failed for ${sourceName}: ${getErrorDetail(error)}`);
|
||||
throw new Error(
|
||||
`Image import failed for ${sourceName}: ${getErrorDetail(error)}`
|
||||
);
|
||||
}
|
||||
|
||||
const metadata = extractHdrTextureMetadata(texture);
|
||||
asset = createImageAssetRecord(sourceName, mimeType, bytes.byteLength, metadata);
|
||||
asset = createImageAssetRecord(
|
||||
sourceName,
|
||||
mimeType,
|
||||
bytes.byteLength,
|
||||
metadata
|
||||
);
|
||||
loadedAsset = createLoadedHdrImageAsset(
|
||||
asset,
|
||||
texture,
|
||||
@@ -472,11 +528,18 @@ export async function importBackgroundImageAssetFromFile(
|
||||
image = await loadImageElement(transientResourceUrl.url);
|
||||
} catch (error) {
|
||||
transientResourceUrl.revoke();
|
||||
throw new Error(`Image import failed for ${sourceName}: ${getErrorDetail(error)}`);
|
||||
throw new Error(
|
||||
`Image import failed for ${sourceName}: ${getErrorDetail(error)}`
|
||||
);
|
||||
}
|
||||
|
||||
const metadata = extractImageAssetMetadata(image);
|
||||
asset = createImageAssetRecord(sourceName, mimeType, bytes.byteLength, metadata);
|
||||
asset = createImageAssetRecord(
|
||||
sourceName,
|
||||
mimeType,
|
||||
bytes.byteLength,
|
||||
metadata
|
||||
);
|
||||
loadedAsset = createLoadedImageAsset(
|
||||
asset,
|
||||
image,
|
||||
@@ -576,11 +639,15 @@ export async function loadImageAssetFromStorage(
|
||||
try {
|
||||
storedAsset = await storage.getAsset(asset.storageKey);
|
||||
} catch (error) {
|
||||
throw new Error(`Image asset reload failed for ${asset.sourceName}: ${getErrorDetail(error)}`);
|
||||
throw new Error(
|
||||
`Image asset reload failed for ${asset.sourceName}: ${getErrorDetail(error)}`
|
||||
);
|
||||
}
|
||||
|
||||
if (storedAsset === null) {
|
||||
throw new Error(`Missing stored binary data for imported image asset ${asset.sourceName}.`);
|
||||
throw new Error(
|
||||
`Missing stored binary data for imported image asset ${asset.sourceName}.`
|
||||
);
|
||||
}
|
||||
|
||||
return loadImageAssetFromPackageRecord(asset, storedAsset);
|
||||
|
||||
@@ -43,7 +43,9 @@ export function createCreateCustomMaterialCommand(
|
||||
const currentDocument = context.getDocument();
|
||||
|
||||
if (nextMaterial.kind !== "custom") {
|
||||
throw new Error("Only custom materials can be created by this command.");
|
||||
throw new Error(
|
||||
"Only custom materials can be created by this command."
|
||||
);
|
||||
}
|
||||
|
||||
if (currentDocument.materials[nextMaterial.id] !== undefined) {
|
||||
|
||||
@@ -42,7 +42,9 @@ export function createSetCustomMaterialTextureCommand(
|
||||
}
|
||||
|
||||
if (options.asset !== undefined && options.asset.id !== options.assetId) {
|
||||
throw new Error("Imported material texture asset id must match assetId.");
|
||||
throw new Error(
|
||||
"Imported material texture asset id must match assetId."
|
||||
);
|
||||
}
|
||||
|
||||
const existingAsset =
|
||||
|
||||
@@ -50,7 +50,9 @@ export function createUpdateCustomMaterialCommand(
|
||||
}
|
||||
|
||||
if (previousMaterial === null) {
|
||||
previousMaterial = cloneMaterialDef(currentMaterial) as CustomMaterialDef;
|
||||
previousMaterial = cloneMaterialDef(
|
||||
currentMaterial
|
||||
) as CustomMaterialDef;
|
||||
}
|
||||
|
||||
const nextAlbedoColorHex =
|
||||
|
||||
@@ -6109,11 +6109,7 @@ function readSplineCorridorJunctionConnectionValue(
|
||||
pathId: expectString(value.pathId, `${label}.pathId`),
|
||||
progress: readOptionalFiniteNumber(value.progress, `${label}.progress`, 0),
|
||||
clipDistance: normalizeSplineCorridorJunctionClipDistance(
|
||||
readOptionalFiniteNumber(
|
||||
value.clipDistance,
|
||||
`${label}.clipDistance`,
|
||||
1.5
|
||||
)
|
||||
readOptionalFiniteNumber(value.clipDistance, `${label}.clipDistance`, 1.5)
|
||||
)
|
||||
};
|
||||
}
|
||||
@@ -6162,7 +6158,11 @@ function readSplineCorridorJunctionValue(
|
||||
? undefined
|
||||
: isSplineCorridorJunctionShapeMode(value.shapeMode)
|
||||
? normalizeSplineCorridorJunctionShapeMode(value.shapeMode)
|
||||
: expectLiteralString(value.shapeMode, "straight", `${label}.shapeMode`),
|
||||
: expectLiteralString(
|
||||
value.shapeMode,
|
||||
"straight",
|
||||
`${label}.shapeMode`
|
||||
),
|
||||
edge: readScenePathRoadEdgeSettingsValue(value.edge, `${label}.edge`),
|
||||
connections: value.connections.map((connectionValue, index) =>
|
||||
readSplineCorridorJunctionConnectionValue(
|
||||
|
||||
@@ -69,7 +69,9 @@ export function getTerrainLayerTexture(
|
||||
: (textureLookup(material) ?? getFallbackTerrainLayerTexture());
|
||||
}
|
||||
|
||||
export function getTerrainLayerPreviewColor(material: MaterialDef | null): number {
|
||||
export function getTerrainLayerPreviewColor(
|
||||
material: MaterialDef | null
|
||||
): number {
|
||||
return material === null
|
||||
? new Color("#aea79a").getHex()
|
||||
: new Color(material.swatchColorHex).getHex();
|
||||
@@ -86,7 +88,9 @@ function createPaddedTerrainLayerTextures(
|
||||
);
|
||||
}
|
||||
|
||||
function createPaddedTerrainLayerColors(layerColors: readonly number[]): Color[] {
|
||||
function createPaddedTerrainLayerColors(
|
||||
layerColors: readonly number[]
|
||||
): Color[] {
|
||||
const fallbackColor = getTerrainLayerPreviewColor(null);
|
||||
|
||||
return Array.from(
|
||||
|
||||
@@ -4341,7 +4341,10 @@ export class RuntimeHost {
|
||||
}
|
||||
|
||||
private createRuntimeRoadSurfaceMaterial(path: RuntimePath): Material {
|
||||
return this.createRuntimeRoadGeneratedMaterial(path.road.material, 0x5f5747);
|
||||
return this.createRuntimeRoadGeneratedMaterial(
|
||||
path.road.material,
|
||||
0x5f5747
|
||||
);
|
||||
}
|
||||
|
||||
private createRuntimeRoadEdgeMaterial(
|
||||
|
||||
Reference in New Issue
Block a user