/// <summary> /// Exports materials for host object. /// </summary> /// <param name="exporterIFC">The ExporterIFC object.</param> /// <param name="hostObject">The host object.</param> /// <param name="elemHnds">The host IFC handles.</param> /// <param name="geometryElement">The geometry element.</param> /// <param name="productWrapper">The ProductWrapper.</param> /// <param name="levelId">The level id.</param> /// <param name="direction">The IFCLayerSetDirection.</param> /// <param name="containsBRepGeometry">True if the geometry contains BRep geoemtry. If so, we will export an IfcMaterialList</param> /// <returns>True if exported successfully, false otherwise.</returns> public static bool ExportHostObjectMaterials(ExporterIFC exporterIFC, HostObject hostObject, IList <IFCAnyHandle> elemHnds, GeometryElement geometryElement, ProductWrapper productWrapper, ElementId levelId, Toolkit.IFCLayerSetDirection direction, bool containsBRepGeometry, IFCAnyHandle typeHnd = null) { if (hostObject == null) { return(true); //nothing to do } if (elemHnds == null || (elemHnds.Count == 0)) { return(true); //nothing to do } IFCFile file = exporterIFC.GetFile(); using (IFCTransaction tr = new IFCTransaction(file)) { //if (productWrapper != null) // productWrapper.ClearFinishMaterials(); double scaledOffset = 0.0, scaledWallWidth = 0.0, wallHeight = 0.0; Wall wall = hostObject as Wall; if (wall != null) { scaledWallWidth = UnitUtil.ScaleLength(wall.Width); scaledOffset = -scaledWallWidth / 2.0; BoundingBoxXYZ boundingBox = wall.get_BoundingBox(null); if (boundingBox != null) { wallHeight = boundingBox.Max.Z - boundingBox.Min.Z; } } List <ElementId> matIds; IFCAnyHandle primaryMaterialHnd; IFCAnyHandle materialLayerSet = ExporterUtil.CollectMaterialLayerSet(exporterIFC, hostObject, productWrapper, out matIds, out primaryMaterialHnd); // For IFC4 RV, material layer may still be created even if the geometry is Brep/Tessellation if ((containsBRepGeometry && matIds.Count > 0) && !(ExporterCacheManager.ExportOptionsCache.ExportAs4ReferenceView && !IFCAnyHandleUtil.IsNullOrHasNoValue(materialLayerSet))) { foreach (IFCAnyHandle elemHnd in elemHnds) { CategoryUtil.CreateMaterialAssociation(exporterIFC, elemHnd, matIds); } } if (!IFCAnyHandleUtil.IsNullOrHasNoValue(materialLayerSet)) { // If there is an override for creating IfcMaterial instead of IfcMaterialLayer for a single material/layer, assign the material directly to type or element(s) if (IFCAnyHandleUtil.IsTypeOf(materialLayerSet, IFCEntityType.IfcMaterial)) { if (typeHnd != null) { CategoryUtil.CreateMaterialAssociation(exporterIFC, typeHnd, materialLayerSet); } //else //{ foreach (IFCAnyHandle elemHnd in elemHnds) { CategoryUtil.CreateMaterialAssociation(exporterIFC, elemHnd, materialLayerSet); } //} } else { // IfcMaterialLayerSetUsage is not supported for IfcWall, only IfcWallStandardCase. IFCAnyHandle layerSetUsage = null; for (int ii = 0; ii < elemHnds.Count; ii++) { IFCAnyHandle elemHnd = elemHnds[ii]; if (IFCAnyHandleUtil.IsNullOrHasNoValue(elemHnd)) { continue; } SpaceBoundingElementUtil.RegisterSpaceBoundingElementHandle(exporterIFC, elemHnd, hostObject.Id, levelId); // Even if it is Tessellated geometry in IFC4RV, the material layer will still be assigned if (containsBRepGeometry && !ExporterCacheManager.ExportOptionsCache.ExportAs4ReferenceView) { continue; } HashSet <IFCAnyHandle> relDecomposesSet = IFCAnyHandleUtil.GetRelDecomposes(elemHnd); IList <IFCAnyHandle> subElemHnds = null; if (relDecomposesSet != null && relDecomposesSet.Count == 1) { IFCAnyHandle relAggregates = relDecomposesSet.First(); if (IFCAnyHandleUtil.IsTypeOf(relAggregates, IFCEntityType.IfcRelAggregates)) { subElemHnds = IFCAnyHandleUtil.GetAggregateInstanceAttribute <List <IFCAnyHandle> >(relAggregates, "RelatedObjects"); } } bool hasSubElems = (subElemHnds != null && subElemHnds.Count != 0); bool isRoof = IFCAnyHandleUtil.IsTypeOf(elemHnd, IFCEntityType.IfcRoof); bool isDisallowedWallType = (IFCAnyHandleUtil.IsTypeOf(elemHnd, IFCEntityType.IfcWall) && !ExporterCacheManager.ExportOptionsCache.ExportAs4); // Create IfcMaterialLayerSetUsage unless we have sub-elements, are exporting a Roof, or are exporting a pre-IFC4 IfcWall. if (!hasSubElems && !isRoof && !isDisallowedWallType) { bool materialAlreadyAssoc = false; if (typeHnd != null) { CategoryUtil.CreateMaterialAssociation(exporterIFC, typeHnd, materialLayerSet); //materialAlreadyAssoc = true; } if (ExporterCacheManager.ExportOptionsCache.ExportAs4ReferenceView) { if (!materialAlreadyAssoc) { CategoryUtil.CreateMaterialAssociation(exporterIFC, elemHnd, materialLayerSet); } } else { if (layerSetUsage == null) { bool flipDirSense = true; if (wall != null) { // if we have flipped the center curve on export, we need to take that into account here. // We flip the center curve on export if it is an arc and it has a negative Z direction. LocationCurve locCurve = wall.Location as LocationCurve; if (locCurve != null) { Curve curve = locCurve.Curve; Transform lcs = Transform.Identity; bool curveFlipped = GeometryUtil.MustFlipCurve(lcs, curve); flipDirSense = !(wall.Flipped ^ curveFlipped); } } else if (hostObject is CeilingAndFloor) { flipDirSense = false; } double offsetFromReferenceLine = flipDirSense ? -scaledOffset : scaledOffset; IFCDirectionSense sense = flipDirSense ? IFCDirectionSense.Negative : IFCDirectionSense.Positive; layerSetUsage = IFCInstanceExporter.CreateMaterialLayerSetUsage(file, materialLayerSet, direction, sense, offsetFromReferenceLine); } ExporterCacheManager.MaterialLayerRelationsCache.Add(layerSetUsage, elemHnd); } } else { if (hasSubElems) { foreach (IFCAnyHandle subElemHnd in subElemHnds) { // TODO: still need to figure out the best way to create type for the sub elements because at this time a lot of information is not available, e.g. // the Revit Element to get the type, other information for name, GUID, etc. if (!IFCAnyHandleUtil.IsNullOrHasNoValue(subElemHnd)) { CategoryUtil.CreateMaterialAssociation(exporterIFC, subElemHnd, materialLayerSet); } } } else if (!isRoof) { if (typeHnd != null) { CategoryUtil.CreateMaterialAssociation(exporterIFC, typeHnd, materialLayerSet); } else { CategoryUtil.CreateMaterialAssociation(exporterIFC, elemHnd, materialLayerSet); } } else if (primaryMaterialHnd != null) { if (typeHnd != null) { CategoryUtil.CreateMaterialAssociation(exporterIFC, typeHnd, materialLayerSet); } else { CategoryUtil.CreateMaterialAssociation(exporterIFC, elemHnd, materialLayerSet); } } } } } } tr.Commit(); return(true); } }
/// <summary> /// Exports materials for host object. /// </summary> /// <param name="exporterIFC">The ExporterIFC object.</param> /// <param name="hostObject">The host object.</param> /// <param name="elemHnds">The host IFC handles.</param> /// <param name="geometryElement">The geometry element.</param> /// <param name="productWrapper">The ProductWrapper.</param> /// <param name="levelId">The level id.</param> /// <param name="direction">The IFCLayerSetDirection.</param> /// <param name="containsBRepGeometry">True if the geometry contains BRep geoemtry. If so, we will export an IfcMaterialList</param> /// <returns>True if exported successfully, false otherwise.</returns> public static bool ExportHostObjectMaterials(ExporterIFC exporterIFC, HostObject hostObject, IList <IFCAnyHandle> elemHnds, GeometryElement geometryElement, ProductWrapper productWrapper, ElementId levelId, Toolkit.IFCLayerSetDirection direction, bool containsBRepGeometry) { if (hostObject == null) { return(true); //nothing to do } if (elemHnds == null || (elemHnds.Count == 0)) { return(true); //nothing to do } IFCFile file = exporterIFC.GetFile(); using (IFCTransaction tr = new IFCTransaction(file)) { //if (productWrapper != null) // productWrapper.ClearFinishMaterials(); double scaledOffset = 0.0, scaledWallWidth = 0.0, wallHeight = 0.0; Wall wall = hostObject as Wall; if (wall != null) { scaledWallWidth = UnitUtil.ScaleLength(wall.Width); scaledOffset = -scaledWallWidth / 2.0; BoundingBoxXYZ boundingBox = wall.get_BoundingBox(null); if (boundingBox != null) { wallHeight = boundingBox.Max.Z - boundingBox.Min.Z; } } //ElementId typeElemId = hostObject.GetTypeId(); //IFCAnyHandle materialLayerSet = ExporterCacheManager.MaterialSetCache.Find(typeElemId); //// Roofs with no components are only allowed one material. We will arbitrarily choose the thickest material. //IFCAnyHandle primaryMaterialHnd = ExporterCacheManager.MaterialSetCache.FindPrimaryMaterialHnd(typeElemId); //if (IFCAnyHandleUtil.IsNullOrHasNoValue(materialLayerSet)) //{ // HostObjAttributes hostObjAttr = hostObject.Document.GetElement(typeElemId) as HostObjAttributes; // if (hostObjAttr == null) // return true; //nothing to do // List<ElementId> matIds = new List<ElementId>(); // List<double> widths = new List<double>(); // List<MaterialFunctionAssignment> functions = new List<MaterialFunctionAssignment>(); // ElementId baseMatId = CategoryUtil.GetBaseMaterialIdForElement(hostObject); // CompoundStructure cs = hostObjAttr.GetCompoundStructure(); // if (cs != null) // { // //TODO: Vertically compound structures are not yet supported by export. // if (!cs.IsVerticallyHomogeneous() && !MathUtil.IsAlmostZero(wallHeight)) // cs = cs.GetSimpleCompoundStructure(wallHeight, wallHeight / 2.0); // for (int i = 0; i < cs.LayerCount; ++i) // { // ElementId matId = cs.GetMaterialId(i); // if (matId != ElementId.InvalidElementId) // { // matIds.Add(matId); // } // else // { // matIds.Add(baseMatId); // } // widths.Add(cs.GetLayerWidth(i)); // // save layer function into ProductWrapper, // // it's used while exporting "Function" of Pset_CoveringCommon // functions.Add(cs.GetLayerFunction(i)); // } // } // if (matIds.Count == 0) // { // matIds.Add(baseMatId); // widths.Add(cs != null ? cs.GetWidth() : 0); // functions.Add(MaterialFunctionAssignment.None); // } // // We can't create IfcMaterialLayers without creating an IfcMaterialLayerSet. So we will simply collate here. // IList<IFCAnyHandle> materialHnds = new List<IFCAnyHandle>(); // IList<int> widthIndices = new List<int>(); // double thickestLayer = 0.0; // for (int ii = 0; ii < matIds.Count; ++ii) // { // // Require positive width for IFC2x3 and before, and non-negative width for IFC4. // if (widths[ii] < -MathUtil.Eps()) // continue; // bool almostZeroWidth = MathUtil.IsAlmostZero(widths[ii]); // if (!ExporterCacheManager.ExportOptionsCache.ExportAs4 && almostZeroWidth) // continue; // if (almostZeroWidth) // widths[ii] = 0.0; // IFCAnyHandle materialHnd = CategoryUtil.GetOrCreateMaterialHandle(exporterIFC, matIds[ii]); // if (primaryMaterialHnd == null || (widths[ii] > thickestLayer)) // { // primaryMaterialHnd = materialHnd; // thickestLayer = widths[ii]; // } // widthIndices.Add(ii); // materialHnds.Add(materialHnd); // if ((productWrapper != null) && (functions[ii] == MaterialFunctionAssignment.Finish1 || functions[ii] == MaterialFunctionAssignment.Finish2)) // { // productWrapper.AddFinishMaterial(materialHnd); // } // } // int numLayersToCreate = widthIndices.Count; // if (numLayersToCreate == 0) // return false; // if (!containsBRepGeometry) // { // IList<IFCAnyHandle> layers = new List<IFCAnyHandle>(numLayersToCreate); // for (int ii = 0; ii < numLayersToCreate; ii++) // { // int widthIndex = widthIndices[ii]; // double scaledWidth = UnitUtil.ScaleLength(widths[widthIndex]); // IFCAnyHandle materialLayer = IFCInstanceExporter.CreateMaterialLayer(file, materialHnds[ii], scaledWidth, null); // layers.Add(materialLayer); // } // string layerSetName = exporterIFC.GetFamilyName(); // materialLayerSet = IFCInstanceExporter.CreateMaterialLayerSet(file, layers, layerSetName); // ExporterCacheManager.MaterialSetCache.Register(typeElemId, materialLayerSet); // ExporterCacheManager.MaterialSetCache.RegisterPrimaryMaterialHnd(typeElemId, primaryMaterialHnd); // } List <ElementId> matIds; IFCAnyHandle primaryMaterialHnd; IFCAnyHandle materialLayerSet = ExporterUtil.CollectMaterialLayerSet(exporterIFC, hostObject, productWrapper, out matIds, out primaryMaterialHnd); if (containsBRepGeometry && matIds.Count > 0) { foreach (IFCAnyHandle elemHnd in elemHnds) { CategoryUtil.CreateMaterialAssociation(exporterIFC, elemHnd, matIds); } } if (!IFCAnyHandleUtil.IsNullOrHasNoValue(materialLayerSet)) { // IfcMaterialLayerSetUsage is not supported for IfcWall, only IfcWallStandardCase. IFCAnyHandle layerSetUsage = null; for (int ii = 0; ii < elemHnds.Count; ii++) { IFCAnyHandle elemHnd = elemHnds[ii]; if (IFCAnyHandleUtil.IsNullOrHasNoValue(elemHnd)) { continue; } SpaceBoundingElementUtil.RegisterSpaceBoundingElementHandle(exporterIFC, elemHnd, hostObject.Id, levelId); if (containsBRepGeometry) { continue; } HashSet <IFCAnyHandle> relDecomposesSet = IFCAnyHandleUtil.GetRelDecomposes(elemHnd); IList <IFCAnyHandle> subElemHnds = null; if (relDecomposesSet != null && relDecomposesSet.Count == 1) { IFCAnyHandle relAggregates = relDecomposesSet.First(); if (IFCAnyHandleUtil.IsTypeOf(relAggregates, IFCEntityType.IfcRelAggregates)) { subElemHnds = IFCAnyHandleUtil.GetAggregateInstanceAttribute <List <IFCAnyHandle> >(relAggregates, "RelatedObjects"); } } bool hasSubElems = (subElemHnds != null && subElemHnds.Count != 0); bool isRoof = IFCAnyHandleUtil.IsTypeOf(elemHnd, IFCEntityType.IfcRoof); bool isDisallowedWallType = (IFCAnyHandleUtil.IsTypeOf(elemHnd, IFCEntityType.IfcWall) && !ExporterCacheManager.ExportOptionsCache.ExportAs4); // Create IfcMaterialLayerSetUsage unless we have sub-elements, are exporting a Roof, or are exporting a pre-IFC4 IfcWall. if (!hasSubElems && !isRoof && !isDisallowedWallType) { if (layerSetUsage == null) { bool flipDirSense = true; if (wall != null) { // if we have flipped the center curve on export, we need to take that into account here. // We flip the center curve on export if it is an arc and it has a negative Z direction. LocationCurve locCurve = wall.Location as LocationCurve; if (locCurve != null) { Curve curve = locCurve.Curve; Plane defPlane = new Plane(XYZ.BasisX, XYZ.BasisY, XYZ.Zero); bool curveFlipped = GeometryUtil.MustFlipCurve(defPlane, curve); flipDirSense = !(wall.Flipped ^ curveFlipped); } } else if (hostObject is Floor) { flipDirSense = false; } double offsetFromReferenceLine = flipDirSense ? -scaledOffset : scaledOffset; IFCDirectionSense sense = flipDirSense ? IFCDirectionSense.Negative : IFCDirectionSense.Positive; layerSetUsage = IFCInstanceExporter.CreateMaterialLayerSetUsage(file, materialLayerSet, direction, sense, offsetFromReferenceLine); } ExporterCacheManager.MaterialLayerRelationsCache.Add(layerSetUsage, elemHnd); } else { if (hasSubElems) { foreach (IFCAnyHandle subElemHnd in subElemHnds) { if (!IFCAnyHandleUtil.IsNullOrHasNoValue(subElemHnd)) { ExporterCacheManager.MaterialLayerRelationsCache.Add(materialLayerSet, subElemHnd); } } } else if (!isRoof) { ExporterCacheManager.MaterialLayerRelationsCache.Add(materialLayerSet, elemHnd); } else if (primaryMaterialHnd != null) { ExporterCacheManager.MaterialLayerRelationsCache.Add(primaryMaterialHnd, elemHnd); } } } } tr.Commit(); return(true); } }