Skip to content

Commit

Permalink
Merge branch 'master' into rendering-common
Browse files Browse the repository at this point in the history
  • Loading branch information
hyazinthh committed May 16, 2024
2 parents 3e7fc9d + ef0679d commit bc48c95
Show file tree
Hide file tree
Showing 7 changed files with 385 additions and 178 deletions.
4 changes: 4 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
### 5.4.10
- [OpenVR] changed GL texture submit to 2 textures (previously side by side, issue with Quest 3)
- [GL] Improved querying of supported sample counts
- [GL] Fixed double disposal of Context
- [GLFW] Fixed OpenTK context interop
- [Vulkan] Fixed conservative raster validation error

### 5.4.9
- [LodRenderer] Handle exceptions in background threads
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@
<Compile Include="Sampler\TextureFilter.fs" />
<Compile Include="Sampler\SamplerState.fs" />
<Compile Include="ArrayVisitor.fs" />
<Compile Include="IndexedGeometry.fs" />
<Compile Include="DefaultSemantic.fs" />
<Compile Include="IndexedGeometry.fs" />
<Compile Include="Camera\Frustum.fs" />
<Compile Include="Camera\CameraView.fs" />
<Compile Include="Camera\Camera.fs" />
Expand Down
253 changes: 200 additions & 53 deletions src/Aardvark.Rendering.Common/IndexedGeometry.fs
Original file line number Diff line number Diff line change
@@ -1,29 +1,67 @@
namespace Aardvark.Rendering

open System
open System.Collections.Generic
open Aardvark.Base

[<AutoOpen>]
module private SymDictExtensions =

type SymbolDict<'T> with
member inline x.CopyOrNull() =
if isNull x then null else x.Copy()

module private IndexHelpers =

let addOffset (offset : int) (index : Array) : Array =
[<AutoOpen>]
module private Operations =
let private indexOperations =
let d = Dictionary<Type, obj * obj * obj>()
d.[typeof<int32>] <- (id<int32>, id<int32>, (fun (x: int32) (y: int32) -> x + y) :> obj)
d.[typeof<int16>] <- (int32<int16>, int16<int32>, (fun (x: int16) (y: int32) -> x + int16 y) :> obj)
d.[typeof<uint32>] <- (int32<uint32>, uint32<int32>, (fun (x: uint32) (y: int32) -> x + uint32 y) :> obj)
d.[typeof<uint16>] <- (int32<uint16>, uint16<int32>, (fun (x: uint16) (y: int32) -> x + uint16 y) :> obj)
d

let private getIndexOps<'T> : obj * obj * obj =
match indexOperations.TryGetValue typeof<'T> with
| (true, ops) -> ops
| _ ->
raise <| ArgumentException($"Unsupported index type {typeof<'T>}.")

let toIntConverter<'T> =
let f, _, _ = getIndexOps<'T>
unbox<'T -> int> f

let ofIntConverter<'T> : int -> 'T =
let _, f, _ = getIndexOps<'T>
unbox<int -> 'T> f

let addIntOp<'T> : 'T -> int -> 'T =
let _, _, f = getIndexOps<'T>
unbox<'T -> int -> 'T> f

let createIndex (indexType: Type) (count: int) =
let arr = Array.CreateInstance(indexType, count)
arr.Visit { new ArrayVisitor<Array>() with
member x.Run(data: 'T[]) =
let conv = ofIntConverter<'T>
for i = 0 to count - 1 do
data.[i] <- conv i
arr
}

let addOffset (startAt: int) (offset: int) (index: Array) : Array =
if isNull index then
null
else
match index with
| :? array<int32> as x -> x |> Array.map ((+) offset) :> Array
| :? array<int16> as x -> x |> Array.map ((+) (int16 offset)) :> Array
| :? array<uint32> as x -> x |> Array.map ((+) (uint32 offset)) :> Array
| :? array<uint16> as x -> x |> Array.map ((+) (uint16 offset)) :> Array
| _ ->
raise <| ArgumentException($"Unsupported index type {index.GetType()}.")

let inline private getIntConverter<'T>() : 'T -> int32 =
if typeof<'T> = typeof<int32> then unbox<int32>
elif typeof<'T> = typeof<int16> then unbox<int16> >> int
elif typeof<'T> = typeof<uint32> then unbox<uint32> >> int
elif typeof<'T> = typeof<uint16> then unbox<uint16> >> int
else
raise <| ArgumentException($"Unsupported index type {typeof<'T>}.")
index.Visit { new ArrayVisitor<Array>() with
member x.Run(data: 'T[]) =
let addInt = addIntOp<'T>
for i = startAt to data.Length - 1 do
data.[i] <- addInt data.[i] offset
index
}

let applyIndex (index : Array) (data : Array) =
if isNull index || isNull data then
Expand All @@ -32,7 +70,7 @@ module private IndexHelpers =
index.Visit
{ new ArrayVisitor<Array>() with
member x.Run(index : 'i[]) =
let toInt = getIntConverter<'i>()
let toInt = toIntConverter<'i>
data.Visit
{ new ArrayVisitor<Array>() with
member x.Run(data : 'a[]) =
Expand Down Expand Up @@ -160,46 +198,101 @@ module IndexedGeometryMode =

type IndexedGeometry =
class
/// Primitive topology of the geometry.
val mutable public Mode : IndexedGeometryMode

/// Index array (null for non-indexed geometry).
val mutable public IndexArray : Array

/// Per-vertex attributes (can be null).
val mutable public IndexedAttributes : SymbolDict<Array>

/// Single value attributes (can be null).
val mutable public SingleAttributes : SymbolDict<obj>

member x.IsIndexed = not (isNull x.IndexArray)
/// Indicates whether the geometry is indexed.
member x.IsIndexed =
not (isNull x.IndexArray)

/// Indicates whether the geometry has a non-zero face vertex count.
member inline x.IsEmpty =
x.FaceVertexCount = 0

/// Indicates whether the geometry is valid (i.e. it has a position attribute and all attribute arrays are of sufficient length)
member inline x.IsValid =
if isNull x.IndexedAttributes then false
else
match x.IndexedAttributes.TryGetValue DefaultSemantic.Positions with
| (true, positions) -> x.IndexedAttributes |> Seq.forall (fun attr -> attr.Value.Length >= positions.Length)
| _ -> false

/// Total number of vertices in the geometry.
member x.VertexCount =
if isNull x.IndexedAttributes then 0
else
match Seq.tryHead x.IndexedAttributes.Values with
| Some att -> att.Length
| _ -> 0

match x.IndexedAttributes.TryGetValue DefaultSemantic.Positions with
| (true, positions) -> positions.Length
| _ ->
match x.IndexedAttributes |> Seq.tryHead with
| Some (KeyValue(_, att)) -> att.Length
| _ -> 0

/// Effective number of vertices in the geometry (i.e. the index count if indexed and the vertex count if non-indexed).
member x.FaceVertexCount =
if isNull x.IndexArray then
x.VertexCount
else
x.IndexArray.Length

/// Number of faces in the geometry.
member x.FaceCount =
IndexedGeometryMode.faceCount x.Mode x.FaceVertexCount

///<summary>Creates a copy.</summary>
///<param name="shallowCopy">If true, the index and attribute arrays are reused instead of being copied.</param>
member x.Clone(shallowCopy: bool) =
let indices =
if isNull x.IndexArray || shallowCopy then
x.IndexArray
else
x.IndexArray.Copy()

let indexedAttributes =
if isNull x.IndexedAttributes then
null
else
let d = SymbolDict<Array>(initialCapacity = x.IndexedAttributes.Count)
for (KeyValue(sem, attr)) in x.IndexedAttributes do
d.[sem] <- if shallowCopy then attr else attr.Copy()
d

let singleAttributes =
if isNull x.SingleAttributes then null
else x.SingleAttributes.Copy()

IndexedGeometry(x.Mode, indices, indexedAttributes, singleAttributes)

/// Creates a copy. The copy is shallow as the index and attribute arrays are reused instead of being copied.
member x.Clone() =
IndexedGeometry(
x.Mode,
x.IndexArray,
(if isNull x.IndexedAttributes then null else x.IndexedAttributes.Copy()),
(if isNull x.SingleAttributes then null else x.SingleAttributes.Copy())
)
x.Clone(true)

member x.ToIndexed() =
/// Returns an indexed copy of the geometry.
/// If it is already indexed, it is returned unmodified.
member x.ToIndexed(indexType: Type) =
if isNull x.IndexArray then
let res = x.Clone()
let count = x.FaceVertexCount
res.IndexArray <- Array.init count id
res
let copy = x.Clone()
copy.IndexArray <- IndexHelpers.createIndex indexType x.FaceVertexCount
copy
else
x

/// Returns an indexed copy of the geometry.
/// If it is already indexed, it is returned unmodified.
member x.ToIndexed() =
x.ToIndexed typeof<int32>

/// Returns a non-indexed copy of the geometry.
/// If it is already non-indexed, it is returned unmodified.
member x.ToNonIndexed() =
if isNull x.IndexArray then
x
Expand All @@ -217,6 +310,8 @@ type IndexedGeometry =

res

/// Returns a copy of the geometry with a non-stripped primitive topology.
/// If the topology is not line or triangle strips, the geometry is returned unmodified.
member x.ToNonStripped() =
match x.Mode with
| IndexedGeometryMode.LineStrip ->
Expand All @@ -234,47 +329,65 @@ type IndexedGeometry =
| _ ->
x

/// Returns a union of the geometry with another.
/// The geometries must have the same attributes and primitive topology.
member x.Union(y : IndexedGeometry) =
if x.Mode <> y.Mode then
raise <| ArgumentException("IndexedGeometryMode must match.")

let acceptedTopologies = [
IndexedGeometryMode.PointList
IndexedGeometryMode.LineList
IndexedGeometryMode.TriangleList
IndexedGeometryMode.QuadList
]
IndexedGeometryMode.PointList
IndexedGeometryMode.LineList
IndexedGeometryMode.TriangleList
IndexedGeometryMode.QuadList
]

if not <| List.contains x.Mode acceptedTopologies then
raise <| ArgumentException($"IndexedGeometryMode must be one of {acceptedTopologies}.")

if x.IsIndexed <> y.IsIndexed then
let a = if x.IsIndexed then x else y.ToIndexed()
let b = if y.IsIndexed then y else x.ToIndexed()
let a = if x.IsIndexed then x else x.ToIndexed <| y.IndexArray.GetType().GetElementType()
let b = if y.IsIndexed then y else y.ToIndexed <| x.IndexArray.GetType().GetElementType()
a.Union b

else
let indices =
try
y.IndexArray
|> IndexHelpers.addOffset x.VertexCount
|> ArrayHelpers.concat x.IndexArray
with
| exn ->
raise <| ArgumentException($"Invalid indices: {exn.Message}")
if x.IsIndexed then
try
y.IndexArray
|> ArrayHelpers.concat x.IndexArray
|> IndexHelpers.addOffset x.IndexArray.Length x.VertexCount
with
| exn ->
raise <| ArgumentException($"Invalid indices: {exn.Message}")
else
null

let singleAttributes =
if isNull x.SingleAttributes then y.SingleAttributes
elif isNull y.SingleAttributes then x.SingleAttributes
if isNull x.SingleAttributes then y.SingleAttributes.CopyOrNull()
elif isNull y.SingleAttributes then x.SingleAttributes.CopyOrNull()
else
let r = SymbolDict<obj>()
for (KeyValue(sem, att)) in x.SingleAttributes do r.[sem] <- att
for (KeyValue(sem, att)) in y.SingleAttributes do r.[sem] <- att

for (KeyValue(sem, a)) in x.SingleAttributes do
match y.SingleAttributes.TryGetValue sem with
| (true, b) when a <> b ->
raise <| ArgumentException($"Conflicting single value attribute {sem}.")
| _ ->
r.[sem] <- a

for (KeyValue(sem, a)) in y.SingleAttributes do
match x.SingleAttributes.TryGetValue sem with
| (true, b) when a <> b ->
raise <| ArgumentException($"Conflicting single value attribute {sem}.")
| _ ->
r.[sem] <- a

r

let indexedAttributes =
if isNull x.IndexedAttributes then y.IndexedAttributes
elif isNull y.IndexedAttributes then x.IndexedAttributes
if isNull x.IndexedAttributes then y.IndexedAttributes.CopyOrNull()
elif isNull y.IndexedAttributes then x.IndexedAttributes.CopyOrNull()
else
let r = SymbolDict<Array>()

Expand Down Expand Up @@ -320,17 +433,51 @@ type IndexedGeometry =
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module IndexedGeometry =

/// Returns the primitive topology of the given geometry.
let inline mode (g : IndexedGeometry) = g.Mode

/// Returns the index array of the given geometry (null if non-indexed).
let inline indexArray (g : IndexedGeometry) = g.IndexArray

/// Returns the per-vertex attributes of the given geometry (can be null).
let inline indexedAttributes (g : IndexedGeometry) = g.IndexedAttributes

/// Returns the single value attributes of the given geometry (can be null).
let inline singleAttributes (g : IndexedGeometry) = g.SingleAttributes

/// Returns whether the given geometry has a non-zero face vertex count.
let inline isEmpty (g : IndexedGeometry) = g.IsEmpty

/// Returns whether the given geometry is valid (i.e. it has a position attribute and all attribute arrays are of sufficient length)
let inline isValid (g : IndexedGeometry) = g.IsValid

/// Returns whether the given geometry is indexed.
let inline isIndexed (g : IndexedGeometry) = g.IsIndexed

/// Returns the total number of vertices in the given geometry.
let inline vertexCount (g : IndexedGeometry) = g.VertexCount

/// Returns the effective number of vertices in the given geometry (i.e. the index count if indexed and the vertex count if non-indexed).
let inline faceVertexCount (g : IndexedGeometry) = g.FaceVertexCount

/// Returns the number of faces in the given geometry.
let inline faceCount (g : IndexedGeometry) = g.FaceCount

/// Returns a shallow copy of the given geometry (index and attribute arrays are reused).
let inline clone (g : IndexedGeometry) = g.Clone()

/// Returns an indexed copy of the given geometry.
/// If it is already indexed, it is returned unmodified.
let inline toIndexed (g : IndexedGeometry) = g.ToIndexed()

/// Returns a non-indexed copy of the geometry.
/// If it is already non-indexed, it is returned unmodified.
let inline toNonIndexed (g : IndexedGeometry) = g.ToNonIndexed()

/// Returns a copy of the geometry with a non-stripped primitive topology.
/// If the topology is not line or triangle strips, the geometry is returned unmodified.
let inline toNonStripped (g : IndexedGeometry) = g.ToNonStripped()

/// Returns a union of two geometries with another.
/// The geometries must have the same attributes and primitive topology.
let inline union (a : IndexedGeometry) (b : IndexedGeometry) = a.Union b
Loading

0 comments on commit bc48c95

Please sign in to comment.