item surfaces

This commit is contained in:
wass08
2026-02-18 16:06:23 +09:00
parent a121460965
commit c0676f684e
8 changed files with 288 additions and 16 deletions
+6
View File
@@ -15,6 +15,11 @@ const assetSchema = z.object({
offset: z.tuple([z.number(), z.number(), z.number()]).default([0, 0, 0]),
rotation: z.tuple([z.number(), z.number(), z.number()]).default([0, 0, 0]),
scale: z.tuple([z.number(), z.number(), z.number()]).default([1, 1, 1]),
surface: z
.object({
height: z.number(), // where things rest
})
.optional(), // undefined = can't place things on it
})
export type AssetInput = z.input<typeof assetSchema>
@@ -26,6 +31,7 @@ export const ItemNode = BaseNode.extend({
position: z.tuple([z.number(), z.number(), z.number()]).default([0, 0, 0]),
rotation: z.tuple([z.number(), z.number(), z.number()]).default([0, 0, 0]),
side: z.enum(['front', 'back']).optional(),
children: z.array(z.string()).default([]),
// Wall attachment properties (only used when asset.attachTo is "wall" or "wall-side")
wallId: z.string().optional(),
+13 -9
View File
@@ -35,15 +35,19 @@ export const ItemSystem = () => {
mesh.position.z = (wallThickness / 2) * side;
}
} else if (!item.asset.attachTo) {
// Floor item: elevate by slab height (using full footprint overlap)
const levelId = resolveLevelId(item, nodes)
const slabElevation = spatialGridManager.getSlabElevationForItem(
levelId,
item.position,
item.asset.dimensions,
item.rotation,
)
mesh.position.y = slabElevation + item.position[1]
// If parented to another item (surface placement), R3F handles positioning via the hierarchy
const parentNode = item.parentId ? nodes[item.parentId as AnyNodeId] : undefined
if (parentNode?.type !== 'item') {
// Floor item: elevate by slab height (using full footprint overlap)
const levelId = resolveLevelId(item, nodes)
const slabElevation = spatialGridManager.getSlabElevationForItem(
levelId,
item.position,
item.asset.dimensions,
item.rotation,
)
mesh.position.y = slabElevation + item.position[1]
}
}
clearDirty(id as AnyNodeId)
@@ -7,6 +7,7 @@ import { positionLocal, smoothstep, time } from 'three/tsl'
import { DoubleSide, MeshStandardNodeMaterial } from 'three/webgpu'
import { useNodeEvents } from '../../../hooks/use-node-events'
import { resolveCdnUrl } from '../../../lib/asset-url'
import { NodeRenderer } from '../node-renderer'
// Shared materials to avoid creating new instances for every mesh
const defaultMaterial = new MeshStandardNodeMaterial({
@@ -43,6 +44,9 @@ export const ItemRenderer = ({ node }: { node: ItemNode }) => {
<Suspense fallback={<PreviewModel node={node} />}>
<ModelRenderer node={node} />
</Suspense>
{node.children?.map((childId) => (
<NodeRenderer key={childId} nodeId={childId as ItemNode['id']} />
))}
</group>
)
}