Und noch ein zweites kleines Update
Anbei wie ich die Regel für das Splitten und Mergen der Planes umgesetzt habe, mithilfe von "LODSpheres", abgespeichert in einer Lookup-Tabelle.
Ich verwende aktuell Variante zwei (die untere aus dem Bild), d.h. ich prüfe eine Kollision gegen den nächsten Vektor jeder Plane, die ich mithilfe von joeydee's Algorithmus ermittelt habe. In dem Falle ist das dann ein einfacher Vergleich der Distanz Vektor->Kamera gegen die ganzen Radien der LODSpheres.
Klappt sehr gut, dank der Sphere spart man sich viele Splits im Vergleich zu meiner bisherigen Strategie. Und man vermeidet gerade bei näherer Distanz dass zu viel gesplittet wird was tendenziell außerhalb des Blickfelds gerät.
Ich bin nicht sicher ob die AABB Prüfung effektiver wäre (im Infinity-Forum schlägt dies jemand vor, mit dem Hinweis dass AABB Checks auf dauer günstiger wären). Das würde bedeuten dass ich für
a) Für jede Plane den bzw. die Durchschnittswinkel zum Mittelpunkt berechne, dann die 4 Node-Kanten um eben diesen Winkel rotiere und als separate Kantenwerte im Quadtree-Node speichere (für den späteren AABBvsSphere Test).
b) Bei jedem Prüfvorgang die Position der Kamera um eben den gleichen Winkel rotiere, und dann besagten AABBvsSphere durchführe. Letzteren habe ich schon
auf Basis dieses Eintrags nachprogrammiert, der tut.
Ein Vorteil auf den mich der Kollege auf dem anderen Forum hinwies liegt auf der Hand. Wenn ich als AABB Koordinaten nicht rMin und rMax heranziege, sondern die echten Daten die sich in Kombination mit dem Noise ergeben haben, dann erhält man ein genaueres LOD, weil dann die BoundingBox z.B. auch tiefe Täler aufgrund des Noise richtig berücksichten kann. Weiterhin muss man die AABB Eckpunkte nur einmal berechnen und kann sie dann im QuadTree für jeden Node mitspeichern. D.h. die späteren Berechnungen sind dann wirklich nur eine Kamerarotation pro Plane und der AABBvsSphere Test. Vermutlich wirklich günstiger als immer wieder den nächsten Punkt zu berechnen.
Mein Problem ist, die Durchschnittswinkel der Plane bekomme ich noch ausgerechnet (entweder als signed -180 bis +180 oder 0 bis 360). Mein Ziel wäre z.B. alle Koordinaten nach oben (Vector3.Up, Y-Achse) zeigen zu lassen.
Ich berechne dabei den Winkel des Plane-Mittelpunktes zur Y Achse (Vector3.Up) und Z-Achse (Vector3.Forward), das klappt noch soweit, aber dann kriege ich die korrekte Rotation nicht hin (die Planes zeigen nicht durchgängig nach oben, spätestens nach einigen Splits zeigen sie in ganz verschiedene Richtungen). Man sollte meinen es müsste ganz einfach sein?!
Ich versuche es wie folgt:
WSBBPatchAlignedVectorRMax und WSBBPatchAlignedVectorRMin sind die Kanten der BoundingBox (noch nicht AABB tranformiert). WSBBPatchAlignedVectorRMax ist die Plane-Kante mit Radius rMax, WSBBPatchAlignedVectorRMin ist die Plane-Kante mit Radius rMin. Lasse ich diese zeichnen sieht das so aus.
In WSBBAverageAngleX,Y und Z speichere ich den Winkel zum Plane-Center-Vektor (damit düfte ich den Average Winkel der Plane erhalten) gegen die einzelnen Koordinaten Vektoren Up, Right, Forward.
Nach meinem Verständnis muss ich dann die 8 Punkte der Plane um die Z-Achse um den Winkel -WSBBAverageAngleY rotieren sowie um die X-Achse um den Winkel WSBBAverageAngleZ. Dann muessten die Koordinaten, so mein naives Verstaendnis, doch nach oben zeigen? Hab ich irgendwo einen Denkfehler, mache ich es mir zu einfach?
Der (unschöne) Code:
Die Klasse zum prüfen des Winkels:
Code: Alles auswählen
public class VectorServiceProvider : MonoBehaviour {
static public float SignedAngle(Vector3 a, Vector3 b){
// the vector that we want to measure an angle from
Vector3 referenceForward = a; /* some vector that is not Vector3.up */
// the vector perpendicular to referenceForward (90 degrees clockwise)
// (used to determine if angle is positive or negative)
Vector3 referenceRight= Vector3.Cross(Vector3.up, referenceForward);
// the vector of interest
Vector3 newDirection = b; /* some vector that we're interested in */
// Get the angle in degrees between 0 and 180
float angle = Vector3.Angle(newDirection, referenceForward);
// Determine if the degree value should be negative. Here, a positive value
// from the dot product means that our vector is on the right of the reference vector
// whereas a negative value means we're on the left.
float sign = Mathf.Sign(Vector3.Dot(newDirection, referenceRight));
float finalAngle = sign * angle;
return finalAngle;
}
}
Die Stelle wo ich den Winkel prüfe und rotiere:
quadtreeTerrain.WSBBAverageAngleX = VectorServiceProvider.SignedAngle(Vector3.right,quadtreeTerrain.centerVector);
quadtreeTerrain.WSBBAverageAngleY = VectorServiceProvider.SignedAngle(Vector3.up,quadtreeTerrain.centerVector);
quadtreeTerrain.WSBBAverageAngleZ = VectorServiceProvider.SignedAngle(Vector3.forward,quadtreeTerrain.centerVector);
Debug.Log ("centervector=" + quadtreeTerrain.centerVector + " avAngleX=" + quadtreeTerrain.WSBBAverageAngleX + " avAngleY=" + quadtreeTerrain.WSBBAverageAngleY + " avAngleZ=" + quadtreeTerrain.WSBBAverageAngleZ);
quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMax1 = quadtreeTerrain.WSBBPatchAlignedVectorRMax1;
quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMax2 = quadtreeTerrain.WSBBPatchAlignedVectorRMax2;
quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMax3 = quadtreeTerrain.WSBBPatchAlignedVectorRMax3;
quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMax4 = quadtreeTerrain.WSBBPatchAlignedVectorRMax4;
quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMin1 = quadtreeTerrain.WSBBPatchAlignedVectorRMin1;
quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMin2 = quadtreeTerrain.WSBBPatchAlignedVectorRMin2;
quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMin3 = quadtreeTerrain.WSBBPatchAlignedVectorRMin3;
quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMin4 = quadtreeTerrain.WSBBPatchAlignedVectorRMin4;
quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMax1 = Quaternion.AngleAxis (-quadtreeTerrain.WSBBAverageAngleY, Vector3.forward) * quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMax1;
quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMax2 = Quaternion.AngleAxis (-quadtreeTerrain.WSBBAverageAngleY, Vector3.forward) * quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMax2;
quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMax3 = Quaternion.AngleAxis (-quadtreeTerrain.WSBBAverageAngleY, Vector3.forward) * quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMax3;
quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMax4 = Quaternion.AngleAxis (-quadtreeTerrain.WSBBAverageAngleY, Vector3.forward) * quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMax4;
quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMin1 = Quaternion.AngleAxis (-quadtreeTerrain.WSBBAverageAngleY, Vector3.forward) * quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMin1;
quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMin2 = Quaternion.AngleAxis (-quadtreeTerrain.WSBBAverageAngleY, Vector3.forward) * quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMin2;
quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMin3 = Quaternion.AngleAxis (-quadtreeTerrain.WSBBAverageAngleY, Vector3.forward) * quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMin3;
quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMin4 = Quaternion.AngleAxis (-quadtreeTerrain.WSBBAverageAngleY, Vector3.forward) * quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMin4;
quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMax1 = Quaternion.AngleAxis (-quadtreeTerrain.WSBBAverageAngleZ, Vector3.right) * quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMax1;
quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMax2 = Quaternion.AngleAxis (-quadtreeTerrain.WSBBAverageAngleZ, Vector3.right) * quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMax2;
quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMax3 = Quaternion.AngleAxis (-quadtreeTerrain.WSBBAverageAngleZ, Vector3.right) * quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMax3;
quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMax4 = Quaternion.AngleAxis (-quadtreeTerrain.WSBBAverageAngleZ, Vector3.right) * quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMax4;
quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMin1 = Quaternion.AngleAxis (-quadtreeTerrain.WSBBAverageAngleZ, Vector3.right) * quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMin1;
quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMin2 = Quaternion.AngleAxis (-quadtreeTerrain.WSBBAverageAngleZ, Vector3.right) * quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMin2;
quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMin3 = Quaternion.AngleAxis (-quadtreeTerrain.WSBBAverageAngleZ, Vector3.right) * quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMin3;
quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMin4 = Quaternion.AngleAxis (-quadtreeTerrain.WSBBAverageAngleZ, Vector3.right) * quadtreeTerrain.WSBBPatchAlignedTransformedVectorRMin4;
Und das gruselige Ergebnis der eigentlich :) nach Norden ausgerichteten BoundingBoxes (einmal ohne Split, und dann nach einigen Splits, Chaos pur... :D
)