Compare commits

...

1 Commits

Author SHA1 Message Date
David Kekesi 9902da8b5e poc 2025-07-17 11:43:04 +02:00
6 changed files with 214 additions and 18 deletions
@@ -1,12 +1,12 @@
using Autodesk.Revit.DB;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Instances;
using Speckle.Connectors.Common.Operations.Receive;
using Speckle.Converters.Common;
using Speckle.Converters.RevitShared.Settings;
using Speckle.Objects.Other;
using Speckle.Sdk;
using Speckle.Sdk.Common;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Models.Extensions;
using Speckle.Sdk.Models.GraphTraversal;
@@ -57,7 +57,8 @@ public class RevitMaterialBaker
if (targetRenderMaterialProxy is null)
{
var layerParents = context.GetAscendants().Where(parent => parent is Layer);
//var layerParents = context.GetAscendants().Where(parent => parent is Layer);
var layerParents = context.GetAscendants();
var layer = layerParents.FirstOrDefault(layer =>
unpackedRoot.RenderMaterialProxies.Any(rmp => rmp.objects.Contains(layer.applicationId!))
@@ -102,6 +103,48 @@ public class RevitMaterialBaker
}
}
public void MapInstanceRenderMaterials(
RootObjectUnpackerResult unpackedRoot,
IReadOnlyCollection<LocalToGlobalMap> localToGlobalMaps
)
{
if (unpackedRoot.RenderMaterialProxies is null)
{
return;
}
foreach (var context in unpackedRoot.ObjectsToConvert)
{
if (context.Current.applicationId is null)
{
continue;
}
var targetRenderMaterialProxy = unpackedRoot.RenderMaterialProxies.FirstOrDefault(rmp =>
rmp.objects.Contains(context.Current.applicationId)
);
if (targetRenderMaterialProxy is null)
{
var maps = localToGlobalMaps.Where(m => m.AtomicObject.applicationId == context.Current.applicationId).ToList();
var map = localToGlobalMaps.FirstOrDefault(m => m.AtomicObject.applicationId == context.Current.applicationId);
var instance = map?.InstanceChain.FirstOrDefault(i =>
unpackedRoot.RenderMaterialProxies.Any(rmp => rmp.objects.Contains(i))
);
if (instance is not null)
{
var instanceRenderMaterialProxy = unpackedRoot.RenderMaterialProxies.First(rmp =>
rmp.objects.Contains(instance)
);
targetRenderMaterialProxy = instanceRenderMaterialProxy;
targetRenderMaterialProxy.objects.Add(context.Current.applicationId!);
}
}
}
}
/// <summary>
/// Will bake render materials in the revit document.
/// </summary>
@@ -44,7 +44,7 @@ public sealed class RevitHostObjectBuilder(
IReceiveConversionHandler conversionHandler
) : IHostObjectBuilder, IDisposable
{
public Task<HostObjectBuilderResult> Build(
/*public Task<HostObjectBuilderResult> Build(
Base rootObject,
string projectName,
string modelName,
@@ -53,7 +53,33 @@ public sealed class RevitHostObjectBuilder(
) =>
threadContext.RunOnMainAsync(
() => Task.FromResult(BuildSync(rootObject, projectName, modelName, onOperationProgressed, cancellationToken))
);*/
public Task<HostObjectBuilderResult> Build(
Base rootObject,
string projectName,
string modelName,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
{
bool x = false;
#if REVIT2026
x = converterSettings.Current.Document.IsInEditMode();
#endif
/*bool mod = converterSettings.Current.Document.IsModified;
bool ro = converterSettings.Current.Document.IsReadOnly;
bool mof = converterSettings.Current.Document.IsModifiable;*/
if (x)
{
throw new ConversionException($"We are in modify mode!!!!!");
}
return threadContext.RunOnMainAsync(
() => Task.FromResult(BuildSync(rootObject, projectName, modelName, onOperationProgressed, cancellationToken))
);
}
private HostObjectBuilderResult BuildSync(
Base rootObject,
@@ -122,7 +148,7 @@ public sealed class RevitHostObjectBuilder(
)
{
var id = localToGlobalMap.AtomicObject.id;
var originalAppId = localToGlobalMap.AtomicObject.applicationId ?? id;
//var originalAppId = localToGlobalMap.AtomicObject.applicationId ?? id;
// Apply transformations...
ITransformable? newTransformable = null;
@@ -136,13 +162,13 @@ public sealed class RevitHostObjectBuilder(
localToGlobalMap.AtomicObject.id = id;
// create modified ID and store mapping <- fixes CNX-1707 but causes us material mapping headache!!!
string modifiedAppId = $"{originalAppId}_{Guid.NewGuid().ToString("N")[..8]}";
/*string modifiedAppId = $"{originalAppId}_{Guid.NewGuid().ToString("N")[..8]}";
if (originalAppId != null)
{
originalToModifiedIds[originalAppId] = modifiedAppId;
}
localToGlobalMap.AtomicObject.applicationId = modifiedAppId;
localToGlobalMap.AtomicObject.applicationId = modifiedAppId;*/
localToGlobalMap.Matrix = new HashSet<Matrix4x4>();
}
}
@@ -167,6 +193,7 @@ public sealed class RevitHostObjectBuilder(
if (unpackedRoot.RenderMaterialProxies != null)
{
transactionManager.StartTransaction(true, "Baking materials");
materialBaker.MapInstanceRenderMaterials(unpackedRoot, localToGlobalMaps);
materialBaker.MapLayersRenderMaterials(unpackedRoot);
var map = materialBaker.BakeMaterials(unpackedRoot.RenderMaterialProxies, baseGroupName);
foreach (var kvp in map)
@@ -203,12 +230,12 @@ public sealed class RevitHostObjectBuilder(
}
// 4 - Paint solids
{
/*{
using var _ = activityFactory.Start("Painting solids");
transactionManager.StartTransaction(true, "Painting solids");
PostBakePaint(conversionResults.postBakePaintTargets);
transactionManager.CommitTransaction();
}
}*/
// 5 - Create group
{
@@ -16,6 +16,7 @@ using Speckle.Sdk.Common.Exceptions;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Models.GraphTraversal;
using Speckle.Sdk.Models.Instances;
namespace Speckle.Connectors.Rhino.Operations.Receive;
@@ -118,6 +119,21 @@ public class RhinoHostObjectBuilder : IHostObjectBuilder
_colorBaker.ParseColors(unpackedRoot.ColorProxies);
}
foreach (var a in atomicObjectsWithoutInstanceComponentsWithPath)
{
var id = a.current.applicationId;
if (id == null)
{
continue;
}
if (!_materialBaker.ObjectIdAndMaterialIndexMap.TryGetValue(id, out int mIndex))
{
var mati = FindMissingMaterialIndex(id, instanceComponentsWithPath);
_materialBaker.ObjectIdAndMaterialIndexMap[id] = mati;
}
}
// 4 - Bake layers
// See [CNX-325: Rhino: Change receive operation order to increase performance](https://linear.app/speckle/issue/CNX-325/rhino-change-receive-operation-order-to-increase-performance)
onOperationProgressed.Report(new("Baking layers (redraw disabled)", null));
@@ -332,6 +348,63 @@ public class RhinoHostObjectBuilder : IHostObjectBuilder
return _converterSettings.Current.Document.Objects.Add(obj, atts);
}
private int FindMissingMaterialIndex(
string? objectId,
ICollection<(Collection[] path, IInstanceComponent instance)> instances
)
{
var ids = GetInstanceAscendants(objectId, instances);
foreach (var id in ids)
{
if (_materialBaker.ObjectIdAndMaterialIndexMap.TryGetValue(id, out int mIndex))
{
return mIndex;
}
}
return -1;
}
private List<string> GetInstanceAscendants(
string? objectId,
ICollection<(Collection[] path, IInstanceComponent instance)> instances
)
{
var ascendants = new List<string>();
while (objectId != null)
{
ascendants.Add(objectId);
objectId = GetFirstParent(objectId, instances);
}
return ascendants;
}
private string? GetFirstParent(
string objectId,
ICollection<(Collection[] path, IInstanceComponent instance)> instances
)
{
// Step 1: Find the definition ID for the given objectId
string? defId = instances
?.Select(i => i.instance)
.OfType<InstanceDefinitionProxy>()
.FirstOrDefault(d => d.objects.Contains(objectId))
?.applicationId;
if (defId == null)
{
return null;
}
// Step 2: Find the first InstanceProxy with matching definitionId
return instances
?.Select(i => i.instance)
.OfType<InstanceProxy>()
.FirstOrDefault(p => p.definitionId == defId)
?.applicationId;
}
private List<Guid> BakeObjectsAsFallbackGroup(
IEnumerable<(GeometryBase, Base)> fallbackConversionResult,
Base originatingObject,
@@ -364,4 +437,50 @@ public class RhinoHostObjectBuilder : IHostObjectBuilder
return objectIds;
}
//*******************************************************************
public void MapLayersRenderMaterials(RootObjectUnpackerResult unpackedRoot)
{
if (unpackedRoot.RenderMaterialProxies is null)
{
return;
}
foreach (var context in unpackedRoot.ObjectsToConvert)
{
if (context.Current.applicationId is null)
{
continue;
}
var targetRenderMaterialProxy = unpackedRoot.RenderMaterialProxies.FirstOrDefault(rmp =>
rmp.objects.Contains(context.Current.applicationId)
);
if (targetRenderMaterialProxy is null)
{
var layerParents = context.GetAscendants();
var layer = layerParents.FirstOrDefault(layer =>
unpackedRoot.RenderMaterialProxies.Any(rmp => rmp.objects.Contains(layer.applicationId!))
);
if (layer is not null)
{
var layerRenderMaterialProxy = unpackedRoot.RenderMaterialProxies.First(rmp =>
rmp.objects.Contains(layer.applicationId!)
);
targetRenderMaterialProxy = layerRenderMaterialProxy;
}
}
if (targetRenderMaterialProxy is null)
{
continue; // exit fast, no proxy, we can't do much more.
}
// We mutate the existing proxy list that comes from source application. Because we do not keep track of parent-child relationship of objects in terms of render materials.
targetRenderMaterialProxy.objects.Add(context.Current.applicationId!);
}
}
}
@@ -1,4 +1,4 @@
using Speckle.DoubleNumerics;
using Speckle.DoubleNumerics;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.GraphTraversal;
@@ -19,4 +19,5 @@ public class LocalToGlobalMap
public TraversalContext TraversalContext { get; set; }
public Base AtomicObject { get; set; }
public IReadOnlyCollection<Matrix4x4> Matrix { get; set; }
public List<string> InstanceChain { get; set; }
}
@@ -1,4 +1,4 @@
using Speckle.DoubleNumerics;
using Speckle.DoubleNumerics;
using Speckle.InterfaceGenerator;
using Speckle.Sdk.Dependencies;
using Speckle.Sdk.Models;
@@ -80,6 +80,7 @@ public class LocalToGlobalUnpacker : ILocalToGlobalUnpacker
objectAtRelative,
objectAtRelative,
new HashSet<Matrix4x4>(),
new List<string>(),
localToGlobalMaps
);
}
@@ -93,6 +94,7 @@ public class LocalToGlobalUnpacker : ILocalToGlobalUnpacker
(TraversalContext tc, Base obj) objectAtRelative,
(TraversalContext tc, Base obj) searchForDefinition,
HashSet<Matrix4x4> matrices,
List<string> instanceChain,
HashSet<LocalToGlobalMap> localToGlobalMaps
)
{
@@ -105,25 +107,29 @@ public class LocalToGlobalUnpacker : ILocalToGlobalUnpacker
);
if (definitionProxy is null)
{
localToGlobalMaps.Add(
new LocalToGlobalMap(
new TraversalContext(objectAtRelative.obj, objectAtRelative.tc.PropName, objectAtRelative.tc.Parent),
objectAtRelative.obj,
matrices
)
);
var map = new LocalToGlobalMap(
new TraversalContext(objectAtRelative.obj, objectAtRelative.tc.PropName, objectAtRelative.tc.Parent),
objectAtRelative.obj,
matrices
)
{
InstanceChain = instanceChain
};
localToGlobalMaps.Add(map);
return;
}
var instances = instanceProxies.Where(ic => ic.instanceProxy.definitionId == definitionProxy.applicationId);
foreach (var instance in instances)
{
HashSet<Matrix4x4> newMatrices = [.. matrices, instance.instanceProxy.transform]; // Do not mutate the list!
List<string> newInstanceChain = [.. instanceChain, instance.instanceProxy.applicationId];
UnpackMatrix(
instanceDefinitionProxies,
instanceProxies,
objectAtRelative,
instance,
newMatrices,
newInstanceChain,
localToGlobalMaps
);
}
@@ -1,4 +1,4 @@
using Speckle.Sdk.Transports;
using Speckle.Sdk.Transports;
namespace Speckle.Connectors.Common.Operations;