Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Feature: LOD Sectors #158

Draft
wants to merge 14 commits into
base: master
Choose a base branch
from
4 changes: 4 additions & 0 deletions CadRevealComposer.Tests/Operations/DrawCallEstimatorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public void ConeAndCylinder()
var geometry = new APrimitive[]
{
new Cone(
Matrix4x4.Identity,
0f,
0f,
Vector3.One,
Expand All @@ -40,6 +41,7 @@ public void ConeAndCylinder()
new BoundingBox(-Vector3.One, Vector3.One)
),
new GeneralCylinder(
Matrix4x4.Identity,
0f,
0f,
Vector3.One,
Expand All @@ -64,6 +66,7 @@ public void SolidClosedGeneralConeTorusAndClosedCylinder()
var geometry = new APrimitive[]
{
new Cone(
Matrix4x4.Identity,
0f,
0f,
Vector3.One,
Expand All @@ -85,6 +88,7 @@ public void SolidClosedGeneralConeTorusAndClosedCylinder()
new BoundingBox(-Vector3.One, Vector3.One)
),
new GeneralCylinder(
Matrix4x4.Identity,
0f,
0f,
Vector3.One,
Expand Down
2 changes: 1 addition & 1 deletion CadRevealComposer.Tests/Utils/MeshTools/MeshToolsTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace CadRevealComposer.Tests.Utils.MeshTools;

using CadRevealFbxProvider.BatchUtils;
using CadRevealComposer.Utils.MeshTools;
using System.Numerics;
using Tessellation;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
namespace CadRevealComposer.Tests;

using CadRevealComposer.Utils.MeshTools;

[TestFixture]
public class VertexCacheOptimizerTests
{
[Test]
public void OptimizeVertexCacheFifo_WithValidInput_GivesValuesFromReferenceImplementation()
{
// @formatter:off
// csharpier-ignore-start
uint[] input = {
0,1,2,3,1,0,6,5,0,0,2,6,6,2,7,0,2,6,7,1,3,3,4,7,8,13,11,9,10,8,10,12,14,11,13,12,14,13,15,15,10,14,9,8,11,11,12,9,16,17,18,18,19,16,20,21,22,22,23,20,20,23,16,16,19,20,18,17,22,22,21,18,18,21,20,20,19,18,16,23,22,22,17,16
};

uint[] expectedFifo =
{
0,1,2,3,1,0,6,5,0,0,2,6,0,2,6,7,1,3,3,4,7,6,2,7,8,13,11,9,10,8,9,8,11,11,13,12,14,13,15,11,12,9,10,12,14,15,10,14,16,17,18,18,19,16,20,23,16,16,19,20,16,23,22,22,17,16,18,17,22,22,21,18,18,21,20,20,19,18,20,21,22,22,23,20
};
// csharpier-ignore-end
// @formatter:on

uint[] output = new uint[input.Length];
VertexCacheOptimizer.OptimizeVertexCacheFifo(output, input, 24, 16);
Assert.That(output, Is.Not.EqualTo(input));
Assert.That(expectedFifo, Is.EqualTo(output));
}

[Test]
public void OptimizeVertexCache_WithValidInput_GivesValuesFromReferenceImplementation()
{
// @formatter:off
// csharpier-ignore-start
uint[] input = {
0,1,2,3,1,0,6,5,0,0,2,6,6,2,7,0,2,6,7,1,3,3,4,7,8,13,11,9,10,8,10,12,14,11,13,12,14,13,15,15,10,14,9,8,11,11,12,9,16,17,18,18,19,16,20,21,22,22,23,20,20,23,16,16,19,20,18,17,22,22,21,18,18,21,20,20,19,18,16,23,22,22,17,16
};
uint[] expectedNotFifo = {
0,1,2,3,1,0,7,1,3,3,4,7,6,2,7,0,2,6,0,2,6,6,5,0,8,13,11,11,13,12,9,8,11,11,12,9,9,10,8,10,12,14,14,13,15,15,10,14,16,17,18,18,17,22,22,17,16,18,19,16,16,23,22,22,21,18,20,23,16,22,23,20,16,19,20,20,21,22,20,19,18,18,21,20
};
// csharpier-ignore-end
// @formatter:on
uint[] output = new uint[input.Length];
VertexCacheOptimizer.OptimizeVertexCache(output, input, 24);
Assert.That(output, Is.Not.EqualTo(input));
Assert.That(output, Is.EqualTo(expectedNotFifo));
}
}
1 change: 1 addition & 0 deletions CadRevealComposer/CadRevealComposer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

<ItemGroup>
<PackageReference Include="Ben.StringIntern" Version="0.1.8" />
<PackageReference Include="geometry3Sharp" Version="1.0.324" />
<PackageReference Include="JetBrains.Profiler.Api" Version="1.3.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SharpGLTF.Core" Version="1.0.0-alpha0029" />
Expand Down
93 changes: 88 additions & 5 deletions CadRevealComposer/CadRevealComposerRunner.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
namespace CadRevealComposer;

using CadRevealFbxProvider.BatchUtils;
using CadRevealComposer.Utils.MeshTools;
using Configuration;
using IdProviders;
using ModelFormatProvider;
using Operations;
using Operations.SectorSplitting;
using Primitives;
using Shadow;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
Expand Down Expand Up @@ -128,7 +129,7 @@ TreeIndexGenerator treeIndexGenerator
Console.WriteLine($"Split into {sectors.Length} sectors in {stopwatch.Elapsed}");
stopwatch.Restart();

var sectorInfos = sectors.Select(s => SerializeSector(s, outputDirectory.FullName)).ToArray();
var sectorInfos = sectors.SelectMany(s => SerializeSector(s, outputDirectory.FullName)).ToArray();

Console.WriteLine($"Serialized {sectors.Length} sectors in {stopwatch.Elapsed}");
stopwatch.Restart();
Expand Down Expand Up @@ -167,16 +168,41 @@ private static void PrintSectorStats(ImmutableArray<SceneCreator.SectorInfo> sec
"μ DLsize",
"v DLsize"
);

var shadowSectors = sectorsWithDownloadSize
.Where(x => x.Filename != null && x.Filename!.StartsWith("shadow"))
.ToArray();
var realSectors = sectorsWithDownloadSize.Except(shadowSectors).ToArray();

Console.WriteLine($"Found {realSectors.Count()} real sectors and {shadowSectors.Count()} shadow sectors");

// Add stuff you would like for a quick overview here:
using (new TeamCityLogBlock("Sector Stats"))
{
Console.WriteLine($"Sector Count: {sectorsWithDownloadSize.Length}");
Console.WriteLine($"Total sector Count: {sectorsWithDownloadSize.Length}");
Console.WriteLine($"Total real sector Count: {realSectors.Length}");
Console.WriteLine($"Total shadow sector Count: {shadowSectors.Length}");

Console.WriteLine(
$"Sum all sectors .glb size megabytes: {BytesToMegabytes(sectorsWithDownloadSize.Sum(x => x.DownloadSize)):F2}MB"
);
Console.WriteLine(
$"Sum all real sectors .glb size megabytes: {BytesToMegabytes(realSectors.Sum(x => x.DownloadSize)):F2}MB"
);
Console.WriteLine(
$"Sum all sectors .glb size megabytes: {BytesToMegabytes(shadowSectors.Sum(x => x.DownloadSize)):F2}MB"
);

Console.WriteLine(
$"Total Estimated Triangle Count: {sectorsWithDownloadSize.Sum(x => x.EstimatedTriangleCount)}"
);
Console.WriteLine(
$"Real Sectors Estimated Triangle Count: {realSectors.Sum(x => x.EstimatedTriangleCount)}"
);
Console.WriteLine(
$"Shadow Sectors Estimated Triangle Count: {shadowSectors.Sum(x => x.EstimatedTriangleCount)}"
);

Console.WriteLine($"Depth Stats:");
Console.WriteLine(
$"|{headers.Item1, 5}|{headers.Item2, 7}|{headers.Item3, 10}|{headers.Item4, 11}|{headers.Item5, 10}|{headers.Item6, 10}|{headers.Item7, 10}|{headers.Item8, 17}|{headers.Item9, 10}|{headers.Item10, 8}|"
Expand Down Expand Up @@ -218,7 +244,7 @@ private static void PrintSectorStats(ImmutableArray<SceneCreator.SectorInfo> sec
}
}

private static SceneCreator.SectorInfo SerializeSector(InternalSector p, string outputDirectory)
private static IEnumerable<SceneCreator.SectorInfo> SerializeSector(InternalSector p, string outputDirectory)
{
var (estimatedTriangleCount, estimatedDrawCalls) = DrawCallEstimator.Estimate(p.Geometries);

Expand All @@ -239,8 +265,65 @@ private static SceneCreator.SectorInfo SerializeSector(InternalSector p, string
);

if (sectorFilename != null)
{
SceneCreator.ExportSectorGeometries(sectorInfo.Geometries, sectorFilename, outputDirectory);
return sectorInfo;

var shadowSectorFilename = "shadow_" + sectorFilename;

var shadowSectorInfo = CreateShadowSector(p, shadowSectorFilename);
SceneCreator.ExportSectorGeometries(shadowSectorInfo.Geometries, shadowSectorFilename, outputDirectory);

yield return shadowSectorInfo;
}
yield return sectorInfo;
}

private static SceneCreator.SectorInfo CreateShadowSector(InternalSector realSector, string shadowSectorFilename)
{
var shadowGeometries = CreateShadowGeometries(realSector.Geometries);

var (estimatedTriangleCount, estimatedDrawCalls) = DrawCallEstimator.Estimate(shadowGeometries);

var shadowSectorInfo = new SceneCreator.SectorInfo(
SectorId: realSector.SectorId + 10000, // TODO
ParentSectorId: realSector.ParentSectorId,
Depth: realSector.Depth,
Path: realSector.Path,
Filename: shadowSectorFilename,
EstimatedTriangleCount: estimatedTriangleCount,
EstimatedDrawCalls: estimatedDrawCalls,
MinNodeDiagonal: realSector.MinNodeDiagonal,
MaxNodeDiagonal: realSector.MaxNodeDiagonal,
Geometries: shadowGeometries,
SubtreeBoundingBox: realSector.SubtreeBoundingBox,
GeometryBoundingBox: realSector.GeometryBoundingBox
);

return shadowSectorInfo;
}

private static APrimitive[] CreateShadowGeometries(APrimitive[] realGeometries)
{
var shadowGeometry = new List<APrimitive>();

// TODO: Investigate, can everything made into a box be instances now?

foreach (var geometry in realGeometries)
{
switch (geometry)
{
// Skip circles, rings and quads because they are only used as caps, and shadow boxes do not need them
case Circle:
case GeneralRing:
case Quad:
continue;
default:
shadowGeometry.Add(ShadowCreator.CreateShadow(geometry));
break;
}
}

return shadowGeometry.ToArray();
}

private static IEnumerable<SceneCreator.SectorInfo> CalculateDownloadSizes(
Expand Down
4 changes: 4 additions & 0 deletions CadRevealComposer/Primitives/APrimitive.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ BoundingBox AxisAlignedBoundingBox
) : APrimitive(TreeIndex, Color, AxisAlignedBoundingBox);

public sealed record Cone(
Matrix4x4 InstanceMatrix,
float Angle,
float ArcAngle,
Vector3 CenterA,
Expand All @@ -30,6 +31,7 @@ BoundingBox AxisAlignedBoundingBox
) : APrimitive(TreeIndex, Color, AxisAlignedBoundingBox);

public sealed record EccentricCone(
Matrix4x4 InstanceMatrix,
Vector3 CenterA,
Vector3 CenterB,
Vector3 Normal,
Expand All @@ -41,6 +43,7 @@ BoundingBox AxisAlignedBoundingBox
) : APrimitive(TreeIndex, Color, AxisAlignedBoundingBox);

public sealed record EllipsoidSegment(
Matrix4x4 InstanceMatrix,
float HorizontalRadius,
float VerticalRadius,
float Height,
Expand All @@ -52,6 +55,7 @@ BoundingBox AxisAlignedBoundingBox
) : APrimitive(TreeIndex, Color, AxisAlignedBoundingBox);

public sealed record GeneralCylinder(
Matrix4x4 InstanceMatrix,
float Angle,
float ArcAngle,
Vector3 CenterA,
Expand Down
28 changes: 28 additions & 0 deletions CadRevealComposer/Shadow/ConeShadowCreator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
namespace CadRevealComposer.Shadow;

using Primitives;
using System;
using System.Numerics;
using Utils;

public static class ConeShadowCreator
{
public static APrimitive CreateShadow(this Cone cone)
{
if (!cone.InstanceMatrix.DecomposeAndNormalize(out _, out var rotation, out var position))
{
throw new Exception("Failed to decompose matrix to transform. Input Matrix: " + cone.InstanceMatrix);
}

var coneHeight = Vector3.Distance(cone.CenterA, cone.CenterB);
var radius = float.Max(cone.RadiusA, cone.RadiusB);
var shadowConeScale = new Vector3(radius * 2, radius * 2, coneHeight);

var shadowBoxMatrix =
Matrix4x4.CreateScale(shadowConeScale)
* Matrix4x4.CreateFromQuaternion(rotation)
* Matrix4x4.CreateTranslation(position);

return new Box(shadowBoxMatrix, cone.TreeIndex, cone.Color, cone.AxisAlignedBoundingBox);
}
}
27 changes: 27 additions & 0 deletions CadRevealComposer/Shadow/CylinderShadowCreator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
namespace CadRevealComposer.Shadow;

using Primitives;
using System;
using System.Numerics;
using Utils;

public static class CylinderShadowCreator
{
public static APrimitive CreateShadow(this GeneralCylinder cylinder)
{
if (!cylinder.InstanceMatrix.DecomposeAndNormalize(out _, out var rotation, out var position))
{
throw new Exception("Failed to decompose matrix to transform. Input Matrix: " + cylinder.InstanceMatrix);
}

var cylinderHeight = Vector3.Distance(cylinder.CenterA, cylinder.CenterB);
var newScale = new Vector3(cylinder.Radius * 2, cylinder.Radius * 2, cylinderHeight);

var shadowBoxMatrix =
Matrix4x4.CreateScale(newScale)
* Matrix4x4.CreateFromQuaternion(rotation)
* Matrix4x4.CreateTranslation(position);

return new Box(shadowBoxMatrix, cylinder.TreeIndex, cylinder.Color, cylinder.AxisAlignedBoundingBox);
}
}
28 changes: 28 additions & 0 deletions CadRevealComposer/Shadow/EccentricConeCreator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
namespace CadRevealComposer.Shadow;

using Primitives;
using System;
using System.Numerics;
using Utils;

public static class EccentricConeCreator
{
public static APrimitive CreateShadow(this EccentricCone cone)
{
if (!cone.InstanceMatrix.DecomposeAndNormalize(out _, out var rotation, out var position))
{
throw new Exception("Failed to decompose matrix to transform. Input Matrix: " + cone.InstanceMatrix);
}

var coneHeight = Vector3.Distance(cone.CenterA, cone.CenterB);
var radius = float.Max(cone.RadiusA, cone.RadiusB);
var shadowConeScale = new Vector3(radius * 2, radius * 2, coneHeight);

var shadowBoxMatrix =
Matrix4x4.CreateScale(shadowConeScale)
* Matrix4x4.CreateFromQuaternion(rotation)
* Matrix4x4.CreateTranslation(position);

return new Box(shadowBoxMatrix, cone.TreeIndex, cone.Color, cone.AxisAlignedBoundingBox);
}
}
37 changes: 37 additions & 0 deletions CadRevealComposer/Shadow/EllipsoidSegmentShadowCreator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
namespace CadRevealComposer.Shadow;

using Primitives;
using System;
using System.Numerics;
using Utils;

public static class EllipsoidSegmentShadowCreator
{
public static APrimitive CreateShadow(this EllipsoidSegment ellipsoidSegment)
{
if (!ellipsoidSegment.InstanceMatrix.DecomposeAndNormalize(out _, out var rotation, out var position))
{
throw new Exception(
"Failed to decompose matrix to transform. Input Matrix: " + ellipsoidSegment.InstanceMatrix
);
}

var shadowConeScale = new Vector3(
ellipsoidSegment.HorizontalRadius * 2,
ellipsoidSegment.HorizontalRadius * 2,
ellipsoidSegment.Height
);

var shadowBoxMatrix =
Matrix4x4.CreateScale(shadowConeScale)
* Matrix4x4.CreateFromQuaternion(rotation)
* Matrix4x4.CreateTranslation(position);

return new Box(
shadowBoxMatrix,
ellipsoidSegment.TreeIndex,
ellipsoidSegment.Color,
ellipsoidSegment.AxisAlignedBoundingBox
);
}
}
Loading