using DatReaderWriter.Types; using System; using System.Numerics; namespace AcDream.Core.Rendering.Wb { /// /// Stateless utility methods for deterministic scenery placement. /// public static class SceneryHelpers { /// /// Displaces a scenery object into a pseudo-randomized location /// public static Vector3 Displace(ObjectDesc obj, uint ix, uint iy, uint iq) { float x; float y; float z = obj.BaseLoc.Origin.Z; var loc = obj.BaseLoc; if (obj.DisplaceX <= 0) x = loc.Origin.X; else x = (float)(unchecked((uint)(1813693831u * iy - (iq + 45773u) * (1360117743u * iy * ix + 1888038839u) - 1109124029u * ix)) * 2.3283064e-10 * obj.DisplaceX + loc.Origin.X); if (obj.DisplaceY <= 0) y = loc.Origin.Y; else y = (float)(unchecked((uint)(1813693831u * iy - (iq + 72719u) * (1360117743u * iy * ix + 1888038839u) - 1109124029u * ix)) * 2.3283064e-10 * obj.DisplaceY + loc.Origin.Y); var quadrantVal = unchecked((uint)(1813693831u * iy - ix * (1870387557u * iy + 1109124029u) - 402451965u)) * 2.3283064e-10f; if (quadrantVal >= 0.75) return new Vector3(y, -x, z); if (quadrantVal >= 0.5) return new Vector3(-x, -y, z); if (quadrantVal >= 0.25) return new Vector3(-y, x, z); return new Vector3(x, y, z); } /// /// Returns the scale for a scenery object /// public static float ScaleObj(ObjectDesc obj, uint x, uint y, uint k) { var minScale = obj.MinScale; var maxScale = obj.MaxScale; if (minScale == maxScale) return maxScale; return (float)(Math.Pow(maxScale / minScale, unchecked((uint)(1813693831u * y - (k + 32593u) * (1360117743u * y * x + 1888038839u) - 1109124029u * x)) * 2.3283064e-10) * minScale); } /// /// Returns the rotation for a scenery object as a Quaternion. /// public static Quaternion RotateObj(ObjectDesc obj, uint x, uint y, uint k, Vector3 loc) { var baseOrientation = new Quaternion(obj.BaseLoc.Orientation.X, obj.BaseLoc.Orientation.Y, obj.BaseLoc.Orientation.Z, obj.BaseLoc.Orientation.W); if (obj.MaxRotation <= 0.0f) return baseOrientation; var degrees = (float)(unchecked((uint)(1813693831u * y - (k + 63127u) * (1360117743u * y * x + 1888038839u) - 1109124029u * x)) * 2.3283064e-10 * obj.MaxRotation); return SetHeading(baseOrientation, degrees); } /// /// Aligns an object to a plane normal, returning Quaternion. /// public static Quaternion ObjAlign(ObjectDesc obj, Vector3 normal, float z, Vector3 loc) { var baseOrientation = new Quaternion(obj.BaseLoc.Orientation.X, obj.BaseLoc.Orientation.Y, obj.BaseLoc.Orientation.Z, obj.BaseLoc.Orientation.W); var negNormal = -normal; var headingDeg = (450.0f - (MathF.Atan2(negNormal.Y, negNormal.X) * 180f / MathF.PI)) % 360f; return SetHeading(baseOrientation, headingDeg); } public static Quaternion SetHeading(Quaternion orientation, float degrees) { var rads = degrees * MathF.PI / 180f; var matrix = Matrix4x4.CreateFromQuaternion(orientation); var heading = new Vector3(MathF.Sin(rads), MathF.Cos(rads), matrix.M23 + matrix.M13); var normal = Vector3.Normalize(heading); // Avoid attempting to rotate if normal is too small if (normal.LengthSquared() < 0.0001f) return orientation; var headingDeg = MathF.Atan2(normal.Y, normal.X) * 180f / MathF.PI; var zDeg = 450.0f - headingDeg; var zRot = -(zDeg % 360.0f) * MathF.PI / 180f; var xRot = MathF.Asin(normal.Z); return Quaternion.CreateFromYawPitchRoll(xRot, 0, zRot); } /// /// Returns true if floor slope is within bounds for this object /// public static bool CheckSlope(ObjectDesc obj, float zNormal) { return zNormal >= obj.MinSlope && zNormal <= obj.MaxSlope; } } }