Browse Source

Implemented early bounding box culling for dense isometric rendering.

David Piuva 5 years ago
parent
commit
a7e7682748
1 changed files with 40 additions and 9 deletions
  1. 40 9
      Source/SDK/sandbox/sprite/spriteAPI.cpp

+ 40 - 9
Source/SDK/sandbox/sprite/spriteAPI.cpp

@@ -796,6 +796,30 @@ static FVector3D unpackNormals(FVector4D packedNormals) {
 	return FVector3D(packedNormals.x, packedNormals.y, packedNormals.z);
 }
 
+// Get the pixel bound from a projected vertex point in floating pixel coordinates
+static IRect boundFromVertex(const FVector3D& screenProjection) {
+	return IRect((int)(screenProjection.x), (int)(screenProjection.y), 1, 1);
+}
+
+// Returns true iff the box might be seen using a pessimistic test
+static IRect boundingBoxToRectangle(const FVector3D& minBound, const FVector3D& maxBound, const Transform3D& objectToScreenSpace) {
+	FVector3D points[8] = {
+	  FVector3D(minBound.x, minBound.y, minBound.z),
+	  FVector3D(maxBound.x, minBound.y, minBound.z),
+	  FVector3D(minBound.x, maxBound.y, minBound.z),
+	  FVector3D(maxBound.x, maxBound.y, minBound.z),
+	  FVector3D(minBound.x, minBound.y, maxBound.z),
+	  FVector3D(maxBound.x, minBound.y, maxBound.z),
+	  FVector3D(minBound.x, maxBound.y, maxBound.z),
+	  FVector3D(maxBound.x, maxBound.y, maxBound.z)
+	};
+	IRect result = boundFromVertex(objectToScreenSpace.transformPoint(points[0]));
+	for (int p = 1; p < 8; p++) {
+		result = IRect::merge(result, boundFromVertex(objectToScreenSpace.transformPoint(points[p])));
+	}
+	return result;
+}
+
 // Pre-conditions:
 //   * All images must exist and have the same dimensions
 //   * All triangles in model must be contained within the image bounds after being projected using view
@@ -804,26 +828,33 @@ static FVector3D unpackNormals(FVector4D packedNormals) {
 // worldOrigin is the perceived world's origin in target pixel coordinates
 // modelToWorldSpace is used to place the model freely in the world
 static IRect renderModel(Model model, OrthoView view, ImageF32 depthBuffer, ImageRgbaU8 diffuseTarget, ImageRgbaU8 normalTarget, FVector2D worldOrigin, Transform3D modelToWorldSpace) {
+	// Combine position transforms
+	Transform3D objectToScreenSpace = modelToWorldSpace * Transform3D(FVector3D(worldOrigin.x, worldOrigin.y, 0.0f), view.worldSpaceToScreenDepth);
+
 	// Get the model's 3D bound
 	FVector3D minBound, maxBound;
 	model_getBoundingBox(model, minBound, maxBound);
-	// TODO: Quick culling test based on the 3D bounding box using only 8 points.
-
-	int pointCount = model_getNumberOfPoints(model);
+	IRect pessimisticBound = boundingBoxToRectangle(minBound, maxBound, objectToScreenSpace);
+	// Get the target image bound
 	IRect clipBound = image_getBound(depthBuffer);
+	// Fast culling test
+	if (!IRect::overlaps(pessimisticBound, clipBound)) {
+		// Nothing drawn, no dirty rectangle
+		return IRect();
+	}
 
+	// TODO: Reuse memory in a thread-safe way
+	// Allocate memory for projected positions
+	int pointCount = model_getNumberOfPoints(model);
 	Array<FVector3D> projectedPoints(pointCount, FVector3D()); // pixel X, pixel Y, mini-tile height
 
-	// Combine transforms
-	Transform3D objectToWorldSpace = modelToWorldSpace * Transform3D(FVector3D(worldOrigin.x, worldOrigin.y, 0.0f), view.worldSpaceToScreenDepth);
-
 	// Transform positions and return the dirty box
 	IRect dirtyBox = IRect(clipBound.width(), clipBound.height(), -clipBound.width(), -clipBound.height());
 	for (int point = 0; point < pointCount; point++) {
-		FVector3D screenProjection = objectToWorldSpace.transformPoint(model_getPoint(model, point));
+		FVector3D screenProjection = objectToScreenSpace.transformPoint(model_getPoint(model, point));
 		projectedPoints[point] = screenProjection;
 		// Expand the dirty bound
-		dirtyBox = IRect::merge(dirtyBox, IRect((int)(screenProjection.x), (int)(screenProjection.y), 1, 1));
+		dirtyBox = IRect::merge(dirtyBox, boundFromVertex(screenProjection));
 	}
 
 	// Skip early if the more precise culling test fails
@@ -831,7 +862,7 @@ static IRect renderModel(Model model, OrthoView view, ImageF32 depthBuffer, Imag
 		// Nothing drawn, no dirty rectangle
 		return IRect();
 	}
-	
+
 	// Combine normal transforms
 	FMatrix3x3 normalToWorldSpace = view.normalToWorldSpace;