var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var builders_exports = {};
__export(builders_exports, {
  DEFAULT_COLOR: () => DEFAULT_COLOR,
  DEFAULT_OPACITY: () => DEFAULT_OPACITY,
  DEFAULT_WIREFRAME_COLOR: () => DEFAULT_WIREFRAME_COLOR,
  createBoundingBoxMesh: () => createBoundingBoxMesh,
  createPrimitiveObject: () => createPrimitiveObject
});
module.exports = __toCommonJS(builders_exports);
var import_boxel_core = require("@playdrop/boxel-core");
var import_primitives = require("./primitives.js");
var import_textures = require("./textures.js");
var import_mesher = require("./voxels/mesher.js");
const DEFAULT_COLOR = 3900150;
const DEFAULT_OPACITY = 0.45;
const DEFAULT_WIREFRAME_COLOR = 6333946;
const BOX_FACES = ["east", "west", "top", "bottom", "north", "south"];
const OVERLAY_THICKNESS = 0.25;
function createBoundingBoxMesh(context, size) {
  const geometry = (0, import_primitives.createBoxGeometry)(context.THREE, size);
  const material = new context.THREE.MeshStandardMaterial({
    color: DEFAULT_COLOR,
    transparent: true,
    opacity: DEFAULT_OPACITY,
    metalness: 0,
    roughness: 0.75
  });
  const mesh = new context.THREE.Mesh(geometry, material);
  const edges = new context.THREE.EdgesGeometry(geometry);
  const lineMaterial = new context.THREE.LineBasicMaterial({ color: DEFAULT_WIREFRAME_COLOR });
  const wireframe = new context.THREE.LineSegments(edges, lineMaterial);
  mesh.add(wireframe);
  mesh.userData = {
    ...mesh.userData ?? {},
    size: [...size],
    placeholder: true
  };
  return mesh;
}
function createPlainBoxObject(context, primitive, alignment) {
  const materialDef = context.materialsById.get(primitive.material);
  const color = materialDef?.baseColor ?? [59, 130, 246];
  const geometry = (0, import_primitives.createBoxGeometry)(context.THREE, primitive.size);
  const meshMaterial = new context.THREE.MeshStandardMaterial({
    color: (0, import_textures.toThreeColorRGB)(context, color),
    metalness: 0,
    roughness: 1,
    transparent: materialDef?.opacity !== void 0 && materialDef.opacity < 1,
    opacity: materialDef?.opacity !== void 0 ? materialDef.opacity : 1
  });
  if (materialDef?.emissiveColor) {
    meshMaterial.emissive.copy((0, import_textures.toThreeColorRGB)(context, materialDef.emissiveColor));
    meshMaterial.emissiveIntensity = 1;
  }
  meshMaterial.needsUpdate = true;
  const mesh = new context.THREE.Mesh(geometry, meshMaterial);
  mesh.name = primitive.id;
  mesh.userData = {
    ...mesh.userData ?? {},
    primitiveId: primitive.id,
    size: [...primitive.size]
  };
  if (alignment === "grounded") {
    mesh.position.y = primitive.size[1] / 2;
  }
  return mesh;
}
function createTexturedBoxObject(context, primitive, alignment) {
  const geometry = (0, import_primitives.createBoxGeometry)(context.THREE, primitive.size);
  const materials = BOX_FACES.map((face) => (0, import_textures.createTextureMaterial)(context, (0, import_boxel_core.resolveFaceTextureId)(primitive, face)));
  const renderMode = primitive.renderMode === "cutout" ? "cutout" : "plain";
  materials.forEach((material) => {
    if (renderMode === "cutout") {
      material.transparent = false;
      material.alphaTest = 0.5;
      material.side = context.THREE.DoubleSide;
    } else {
      material.transparent = true;
      material.alphaTest = 0;
      material.side = context.THREE.FrontSide;
    }
    material.depthWrite = true;
    material.needsUpdate = true;
  });
  const mesh = new context.THREE.Mesh(geometry, materials);
  mesh.name = primitive.id;
  mesh.userData = {
    ...mesh.userData ?? {},
    primitiveId: primitive.id,
    size: [...primitive.size]
  };
  if (alignment === "grounded") {
    mesh.position.y = primitive.size[1] / 2;
  }
  const overlayTextureIds = BOX_FACES.map((face) => (0, import_boxel_core.resolveOverlayFaceTextureId)(primitive, face));
  const hasOverlay = overlayTextureIds.some((id) => typeof id === "string" && id.trim().length > 0);
  if (hasOverlay) {
    const overlaySize = [
      primitive.size[0] + OVERLAY_THICKNESS * 2,
      primitive.size[1] + OVERLAY_THICKNESS * 2,
      primitive.size[2] + OVERLAY_THICKNESS * 2
    ];
    const overlayGeometry = (0, import_primitives.createBoxGeometry)(context.THREE, overlaySize);
    const overlayMaterials = overlayTextureIds.map((textureId) => {
      if (!textureId) {
        const emptyMaterial = new context.THREE.MeshStandardMaterial({
          transparent: true,
          opacity: 0,
          depthWrite: false,
          metalness: 0,
          roughness: 1
        });
        emptyMaterial.visible = false;
        emptyMaterial.needsUpdate = true;
        return emptyMaterial;
      }
      const material = (0, import_textures.createTextureMaterial)(context, textureId);
      material.transparent = true;
      material.alphaTest = 0.5;
      material.side = context.THREE.DoubleSide;
      material.depthWrite = false;
      material.needsUpdate = true;
      return material;
    });
    const overlayMesh = new context.THREE.Mesh(overlayGeometry, overlayMaterials);
    overlayMesh.name = `${primitive.id}_overlay`;
    overlayMesh.renderOrder = mesh.renderOrder + 1;
    overlayMesh.userData = {
      ...mesh.userData ?? {},
      primitiveId: primitive.id,
      overlay: true,
      size: [...overlaySize]
    };
    mesh.add(overlayMesh);
  }
  return mesh;
}
function createVoxelObject(context, primitive, alignment) {
  const hasData = Array.isArray(primitive.voxels) && primitive.voxels.length > 0;
  const size = primitive.size ?? [1, 1, 1];
  const dims = [
    Math.max(1, Math.round(size[0])),
    Math.max(1, Math.round(size[1])),
    Math.max(1, Math.round(size[2]))
  ];
  const worldSize = [dims[0], dims[1], dims[2]];
  if (!hasData) {
    const mesh2 = createBoundingBoxMesh(context, worldSize);
    mesh2.userData = {
      ...mesh2.userData ?? {},
      primitiveId: primitive.id,
      size: [...worldSize]
    };
    if (alignment === "grounded") {
      mesh2.position.y = worldSize[1] / 2;
    }
    return mesh2;
  }
  const meshResult = (0, import_mesher.buildVoxelMesh)({
    voxels: primitive.voxels,
    dimensions: dims,
    size: worldSize
  });
  if (meshResult.indices.length === 0 || meshResult.positions.length === 0) {
    const mesh2 = createBoundingBoxMesh(context, worldSize);
    mesh2.userData = {
      ...mesh2.userData ?? {},
      primitiveId: primitive.id,
      size: [...worldSize]
    };
    if (alignment === "grounded") {
      mesh2.position.y = worldSize[1] / 2;
    }
    return mesh2;
  }
  const paletteIds = primitive.palette ?? [];
  const materialCache = /* @__PURE__ */ new Map();
  const materialIndexMap = /* @__PURE__ */ new Map();
  const materials = [];
  const geometry = new context.THREE.BufferGeometry();
  geometry.setAttribute("position", new context.THREE.Float32BufferAttribute(meshResult.positions, 3));
  geometry.setAttribute("normal", new context.THREE.Float32BufferAttribute(meshResult.normals, 3));
  geometry.setAttribute("uv", new context.THREE.Float32BufferAttribute(meshResult.uvs, 2));
  if (meshResult.indices.length > 0) {
    const vertexCount = meshResult.positions.length / 3;
    const useUint32 = vertexCount > 65535;
    const indexArray = useUint32 ? meshResult.indices : new Uint16Array(meshResult.indices);
    geometry.setIndex(new context.THREE.BufferAttribute(indexArray, 1));
  }
  geometry.computeBoundingBox();
  geometry.computeBoundingSphere();
  meshResult.groups.forEach((groupData) => {
    const paletteIndex = groupData.material;
    let material = materialCache.get(paletteIndex);
    if (!material) {
      const materialId = paletteIds[paletteIndex - 1];
      const materialDef = materialId ? context.materialsById.get(materialId) : void 0;
      const baseColor = materialDef?.baseColor ?? [59, 130, 246];
      const emissive = materialDef?.emissiveColor;
      const opacity = materialDef?.opacity ?? 1;
      material = new context.THREE.MeshStandardMaterial({
        color: (0, import_textures.toThreeColorRGB)(context, baseColor),
        metalness: 0,
        roughness: 1,
        transparent: opacity < 1,
        opacity
      });
      if (emissive) {
        material.emissive.copy((0, import_textures.toThreeColorRGB)(context, emissive));
        material.emissiveIntensity = 1;
      }
      material.needsUpdate = true;
      materialCache.set(paletteIndex, material);
    }
    let materialIndex = materialIndexMap.get(paletteIndex);
    if (materialIndex === void 0) {
      materialIndex = materials.length;
      materials.push(material);
      materialIndexMap.set(paletteIndex, materialIndex);
    }
    geometry.addGroup(groupData.start, groupData.count, materialIndex);
  });
  const meshMaterial = materials.length === 1 ? materials[0] : materials;
  const mesh = new context.THREE.Mesh(geometry, meshMaterial);
  mesh.name = primitive.id;
  mesh.userData = {
    ...mesh.userData ?? {},
    primitiveId: primitive.id,
    size: [...worldSize]
  };
  if (alignment === "grounded") {
    mesh.position.y = size[1] / 2;
  }
  return mesh;
}
function createComboObject(context, primitive, alignment, visited) {
  const group = new context.THREE.Group();
  group.name = primitive.id;
  const sockets = [];
  const slotEntries = [];
  primitive.slots.forEach((slot) => {
    const childPrimitive = context.primitivesById.get(slot.primitive);
    if (!childPrimitive) {
      console.warn("[boxel-three] combo slot references missing primitive", slot.primitive);
      return;
    }
    const object = createPrimitiveObject(context, childPrimitive, alignment, new Set(visited));
    if (!object)
      return;
    const childSize = [
      childPrimitive.size?.[0] ?? 0,
      childPrimitive.size?.[1] ?? 0,
      childPrimitive.size?.[2] ?? 0
    ];
    slotEntries.push({ slot, object, size: childSize });
  });
  if (slotEntries.length === 0) {
    const fallbackSize = [
      primitive.size?.[0] ?? 1,
      primitive.size?.[1] ?? 1,
      primitive.size?.[2] ?? 1
    ];
    const bounding = createBoundingBoxMesh(context, fallbackSize);
    bounding.userData = {
      ...bounding.userData ?? {},
      primitiveId: primitive.id,
      size: [...fallbackSize]
    };
    if (alignment === "grounded") {
      bounding.position.y = fallbackSize[1] / 2;
    }
    return bounding;
  }
  let minX = Infinity;
  let minY = Infinity;
  let minZ = Infinity;
  let maxX = -Infinity;
  let maxY = -Infinity;
  let maxZ = -Infinity;
  slotEntries.forEach(({ slot, size }) => {
    const halfSize = [size[0] / 2, size[1] / 2, size[2] / 2];
    minX = Math.min(minX, slot.offset[0] - halfSize[0]);
    minY = Math.min(minY, slot.offset[1] - halfSize[1]);
    minZ = Math.min(minZ, slot.offset[2] - halfSize[2]);
    maxX = Math.max(maxX, slot.offset[0] + halfSize[0]);
    maxY = Math.max(maxY, slot.offset[1] + halfSize[1]);
    maxZ = Math.max(maxZ, slot.offset[2] + halfSize[2]);
  });
  const derivedSize = [
    Math.max(0, maxX - minX),
    Math.max(0, maxY - minY),
    Math.max(0, maxZ - minZ)
  ];
  slotEntries.forEach(({ slot, object }) => {
    object.position.set(slot.offset[0], slot.offset[1], slot.offset[2]);
    object.userData = {
      ...object.userData ?? {},
      comboSlotId: slot.id,
      comboParentId: primitive.id
    };
    group.add(object);
    sockets.push([slot.offset[0], slot.offset[1], slot.offset[2]]);
  });
  const comboSize = [
    primitive.size?.[0] ?? derivedSize[0],
    primitive.size?.[1] ?? derivedSize[1],
    primitive.size?.[2] ?? derivedSize[2]
  ];
  group.userData = {
    ...group.userData ?? {},
    primitiveId: primitive.id,
    size: [...comboSize],
    sockets
  };
  if (alignment === "grounded") {
    group.position.y = comboSize[1] / 2;
  }
  return group;
}
function createPrimitiveObject(context, primitive, alignment, visited = /* @__PURE__ */ new Set()) {
  if (visited.has(primitive.id)) {
    console.warn("[boxel-three] detected recursive primitive reference", primitive.id);
    return null;
  }
  visited.add(primitive.id);
  switch (primitive.kind) {
    case "plain_box":
      return createPlainBoxObject(context, primitive, alignment);
    case "textured_box":
      return createTexturedBoxObject(context, primitive, alignment);
    case "voxel_box":
      return createVoxelObject(context, primitive, alignment);
    case "combo_box":
      return createComboObject(context, primitive, alignment, visited);
    default:
      return null;
  }
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  DEFAULT_COLOR,
  DEFAULT_OPACITY,
  DEFAULT_WIREFRAME_COLOR,
  createBoundingBoxMesh,
  createPrimitiveObject
});
