protected static void viewProjFromLightAlignedBB(ref SSAABB bb, ref Matrix4 lightTransform, ref Vector3 lightY, out Matrix4 viewMatrix, out Matrix4 projMatrix) { // Use center of AABB in regular coordinates to get the view matrix Vector3 targetLightSpace = bb.Center(); Vector3 eyeLightSpace = new Vector3(targetLightSpace.X, targetLightSpace.Y, bb.Min.Z); Vector3 viewTarget = Vector3.Transform(targetLightSpace, lightTransform.Inverted()); Vector3 viewEye = Vector3.Transform(eyeLightSpace, lightTransform.Inverted()); Vector3 viewUp = lightY; viewMatrix = Matrix4.LookAt(viewEye, viewTarget, viewUp); // Finish the projection matrix Vector3 diff = bb.Diff(); float width, height, nearZ, farZ; width = diff.X; height = diff.Y; nearZ = 1f; farZ = 1f + diff.Z; projMatrix = Matrix4.CreateOrthographic(width, height, nearZ, farZ); }
/// <summary> /// Computes vertex positions and normals (based on the state of runtime joint hierarchy). /// Updates VBO with the result. /// </summary> /// <returns>AABB of the vertices.</returns> public SSAABB ComputeVertices() { SSAABB aabb = new SSAABB(float.PositiveInfinity, float.NegativeInfinity); for (int v = 0; v < m_runtimeMesh.numVertices; ++v) { // position Vector3 pos = m_runtimeMesh.computeVertexPos(v); m_vertices [v].Position = pos; aabb.UpdateMin(pos); aabb.UpdateMax(pos); // normal m_vertices [v].Normal = m_runtimeMesh.computeVertexNormal(v); } vbo.UpdateBufferData(m_vertices); return(aabb); }
// Ray to AABB (AxisAlignedBoundingBox) // http://gamedev.stackexchange.com/questions/18436/most-efficient-aabb-vs-ray-collision-algorithms public static bool intersectRayAABox2(SSRay ray, SSAABB box, ref float tnear, ref float tfar) { Vector3d T_1 = new Vector3d(); Vector3d T_2 = new Vector3d(); // vectors to hold the T-values for every direction double t_near = double.MinValue; // maximums defined in float.h double t_far = double.MaxValue; for (int i = 0; i < 3; i++) //we test slabs in every direction { if (ray.dir[i] == 0) // ray parallel to planes in this direction { if ((ray.pos[i] < box.Min[i]) || (ray.pos[i] > box.Max[i])) { return(false); // parallel AND outside box : no intersection possible } } else // ray not parallel to planes in this direction { T_1[i] = (box.Min[i] - ray.pos[i]) / ray.dir[i]; T_2[i] = (box.Max[i] - ray.pos[i]) / ray.dir[i]; if (T_1[i] > T_2[i]) // we want T_1 to hold values for intersection with near plane { var swp = T_2; // swap T_1 = swp; T_2 = T_1; } if (T_1[i] > t_near) { t_near = T_1[i]; } if (T_2[i] < t_far) { t_far = T_2[i]; } if ((t_near > t_far) || (t_far < 0)) { return(false); } } } tnear = (float)t_near; tfar = (float)t_far; // put return values in place return(true); // if we made it here, there was an intersection - YAY }
public override void renderMesh(SSRenderConfig renderConfig) { // apply animation channels _hierarchy.applySkeletalControllers(_channelControllers); SSAABB totalAABB = new SSAABB(float.PositiveInfinity, float.NegativeInfinity); foreach (var sub in _renderSubMeshes) { SSAABB aabb = sub.ComputeVertices(); sub.renderMesh(renderConfig); totalAABB.ExpandBy(aabb); } // update the bounding sphere var sphere = totalAABB.ToSphere(); base.boundingSphereCenter = sphere.center; base.boundingSphereRadius = sphere.radius; NotifyMeshPositionOrSizeChanged(); }
} // fn public static bool intersectRayAABox1(SSRay ray, SSAABB box, ref float tnear, ref float tfar) { // r.dir is unit direction vector of ray Vector3 dirfrac = new Vector3(); float t; dirfrac.X = 1.0f / ray.dir.X; dirfrac.Y = 1.0f / ray.dir.Y; dirfrac.Z = 1.0f / ray.dir.Z; // lb is the corner of AABB with minimal coordinates - left bottom, rt is maximal corner // r.org is origin of ray float t1 = (box.Min.X - ray.pos.X) * dirfrac.X; float t2 = (box.Max.X - ray.pos.X) * dirfrac.X; float t3 = (box.Min.Y - ray.pos.Y) * dirfrac.Y; float t4 = (box.Max.Y - ray.pos.Y) * dirfrac.Y; float t5 = (box.Min.Z - ray.pos.Z) * dirfrac.Z; float t6 = (box.Max.Z - ray.pos.Z) * dirfrac.Z; float tmin = Math.Max(Math.Max(Math.Min(t1, t2), Math.Min(t3, t4)), Math.Min(t5, t6)); float tmax = Math.Min(Math.Min(Math.Max(t1, t2), Math.Max(t3, t4)), Math.Max(t5, t6)); // if tmax < 0, ray (line) is intersecting AABB, but whole AABB is behing us if (tmax < 0) { t = tmax; return(false); } // if tmin > tmax, ray doesn't intersect AABB if (tmin > tmax) { t = tmax; return(false); } t = tmin; return(true); }
public bool IntersectsAABB(SSAABB aabb) { return(aabb.IntersectsSphere(this)); }
protected void ComputeProjections( List <SSObject> objects, Matrix4 cameraView, Matrix4 cameraProj, float fov, float aspect, float cameraNearZ, float cameraFarZ) { if (m_light.GetType() != typeof(SSDirectionalLight)) { throw new NotSupportedException(); } SSDirectionalLight dirLight = (SSDirectionalLight)m_light; // light-aligned unit vectors Vector3 lightZ = dirLight.Direction.Normalized(); Vector3 lightX, lightY; OpenTKHelper.TwoPerpAxes(lightZ, out lightX, out lightY); // transform matrix from regular space into light aligned space Matrix4 lightTransform = new Matrix4( lightX.X, lightX.Y, lightX.Z, 0f, lightY.X, lightY.Y, lightY.Z, 0f, lightZ.X, lightZ.Y, lightZ.Z, 0f, 0f, 0f, 0f, 0f ); // Step 0: camera projection matrix (nearZ and farZ modified) for each frustum split float prevFarZ = cameraNearZ; for (int i = 0; i < c_numberOfSplits; ++i) { // generate frustum splits using Practical Split Scheme (GPU Gems 3, 10.2.1) float iRatio = (float)(i + 1) / (float)c_numberOfSplits; float cLog = cameraNearZ * (float)Math.Pow(cameraFarZ / cameraNearZ, iRatio); float cUni = cameraNearZ + (cameraFarZ - cameraNearZ) * iRatio; float nextFarZ = LogVsLinearSplitFactor * cLog + (1f - LogVsLinearSplitFactor) * cUni; float nextNearZ = prevFarZ; // exported to the shader m_viewSplits [i] = nextFarZ; // create a view proj matrix with the nearZ, farZ values for the current split m_frustumViewProjMatrices[i] = cameraView * Matrix4.CreatePerspectiveFieldOfView(fov, aspect, nextNearZ, nextFarZ); // create light-aligned AABBs of frustums m_frustumLightBB [i] = SSAABB.FromFrustum(ref lightTransform, ref m_frustumViewProjMatrices [i]); prevFarZ = nextFarZ; } #if true // Optional scene-dependent optimization for (int i = 0; i < c_numberOfSplits; ++i) { m_objsLightBB[i] = new SSAABB(float.PositiveInfinity, float.NegativeInfinity); m_splitFrustums[i] = new SSFrustumCuller(ref m_frustumViewProjMatrices[i]); m_shrink[i] = false; } foreach (var obj in objects) { // pass through all shadow casters and receivers if (obj.renderState.toBeDeleted || obj.localBoundingSphereRadius <= 0f || !obj.renderState.visible || !obj.renderState.receivesShadows) { continue; } else { for (int i = 0; i < c_numberOfSplits; ++i) { if (m_splitFrustums[i].isSphereInsideFrustum(obj.worldBoundingSphere)) { // determine AABB in light coordinates of the objects so far m_shrink[i] = true; Vector3 lightAlignedPos = Vector3.Transform(obj.worldBoundingSphereCenter, lightTransform); Vector3 rad = new Vector3(obj.worldBoundingSphereRadius); Vector3 localMin = lightAlignedPos - rad; Vector3 localMax = lightAlignedPos + rad; m_objsLightBB[i].UpdateMin(localMin); m_objsLightBB[i].UpdateMax(localMax); } } } } #endif for (int i = 0; i < c_numberOfSplits; ++i) { if (m_shrink [i]) { m_resultLightBB[i].Min = Vector3.ComponentMax(m_frustumLightBB [i].Min, m_objsLightBB [i].Min); m_resultLightBB [i].Max = Vector3.ComponentMin(m_frustumLightBB [i].Max, m_objsLightBB [i].Max); } else { m_resultLightBB [i] = m_frustumLightBB [i]; } } for (int i = 0; i < c_numberOfSplits; ++i) { // Obtain view + projection + crop matrix, need it later Matrix4 shadowView, shadowProj; viewProjFromLightAlignedBB(ref m_resultLightBB [i], ref lightTransform, ref lightY, out shadowView, out shadowProj); m_shadowViewProjMatrices[i] = shadowView * shadowProj * c_cropMatrices[i]; // obtain view + projection + clio + bias m_shadowViewProjBiasMatrices[i] = m_shadowViewProjMatrices[i] * c_biasMatrix; // There is, currently, no mathematically derived solution to how much Poisson spread scaling // you need for each split. Current improvisation combines 1) increasing spread for the near // splits; reducing spread for the far splits and 2) reducing spread for splits with larger // light-aligned areas; increasing spread for splits with smaller light-aligned areas m_poissonScaling [i] = m_resultLightBB [i].Diff().Xy / (100f * (float)Math.Pow(3.0, i - 1)); } // Combine all splits' BB into one and extend it to include shadow casters closer to light SSAABB castersLightBB = new SSAABB(float.PositiveInfinity, float.NegativeInfinity); for (int i = 0; i < c_numberOfSplits; ++i) { castersLightBB.Combine(ref m_resultLightBB [i]); } // extend Z of the AABB to cover shadow-casters closer to the light foreach (var obj in objects) { if (obj.renderState.toBeDeleted || obj.localBoundingSphereRadius <= 0f || !obj.renderState.visible || !obj.renderState.castsShadow) { continue; } else { Vector3 lightAlignedPos = Vector3.Transform(obj.worldBoundingSphereCenter, lightTransform); Vector3 rad = new Vector3(obj.worldBoundingSphereRadius); Vector3 localMin = lightAlignedPos - rad; Vector3 localMax = lightAlignedPos + rad; if (localMin.Z < castersLightBB.Min.Z) { if (OpenTKHelper.RectsOverlap(castersLightBB.Min.Xy, castersLightBB.Max.Xy, localMin.Xy, localMax.Xy)) { castersLightBB.Min.Z = localMin.Z; } } } } // Generate frustum culler from the BB extended towards light to include shadow casters Matrix4 frustumView, frustumProj; viewProjFromLightAlignedBB(ref castersLightBB, ref lightTransform, ref lightY, out frustumView, out frustumProj); Matrix4 frustumMatrix = frustumView * frustumProj; FrustumCuller = new SSFrustumCuller(ref frustumMatrix); }
private void ComputeProjections(List <SSObject> objects, SSLightBase light, Matrix4 cameraView, Matrix4 cameraProj) { if (light.GetType() != typeof(SSDirectionalLight)) { throw new NotSupportedException(); } SSDirectionalLight dirLight = (SSDirectionalLight)light; // light-aligned unit vectors Vector3 lightZ = dirLight.Direction.Normalized(); Vector3 lightX, lightY; OpenTKHelper.TwoPerpAxes(lightZ, out lightX, out lightY); // transform matrix from regular space into light aligned space Matrix4 lightTransform = new Matrix4( lightX.X, lightX.Y, lightX.Z, 0f, lightY.X, lightY.Y, lightY.Z, 0f, lightZ.X, lightZ.Y, lightZ.Z, 0f, 0f, 0f, 0f, 0f ); // Find AABB of frustum corners in light coordinates Matrix4 cameraViewProj = cameraView * cameraProj; SSAABB frustumLightBB = SSAABB.FromFrustum(ref lightTransform, ref cameraViewProj); bool shrink = false; SSAABB objsLightBB = new SSAABB(float.PositiveInfinity, float.NegativeInfinity); #if true // (optional) scene dependent optimization // Trim the light-bounding box by the shadow receivers (only in light-space x,y,maxz) SSFrustumCuller cameraFrustum = new SSFrustumCuller(ref cameraViewProj); foreach (var obj in objects) { // pass through all shadow casters and receivers if (obj.renderState.toBeDeleted || !obj.renderState.visible || !obj.renderState.receivesShadows || obj.localBoundingSphereRadius <= 0f) { continue; } else if (cameraFrustum.isSphereInsideFrustum(obj.worldBoundingSphere)) { // determine AABB in light coordinates of the objects so far shrink = true; Vector3 lightAlignedPos = Vector3.Transform(obj.worldBoundingSphereCenter, lightTransform); Vector3 rad = new Vector3(obj.worldBoundingSphereRadius); Vector3 localMin = lightAlignedPos - rad; Vector3 localMax = lightAlignedPos + rad; objsLightBB.UpdateMin(localMin); objsLightBB.UpdateMax(localMax); } } #endif // Optimize the light-frustum-projection bounding box by the object-bounding-box SSAABB resultLightBB = new SSAABB(float.PositiveInfinity, float.NegativeInfinity); if (shrink) { // shrink the XY & far-Z coordinates.. resultLightBB.Min.Xy = Vector2.ComponentMax(frustumLightBB.Min.Xy, objsLightBB.Min.Xy); resultLightBB.Min.Z = objsLightBB.Min.Z; resultLightBB.Max = Vector3.ComponentMin(frustumLightBB.Max, objsLightBB.Max); } else { resultLightBB = frustumLightBB; } // View and projection matrices, used by the scene later viewProjFromLightAlignedBB(ref resultLightBB, ref lightTransform, ref lightY, out m_shadowViewMatrix, out m_shadowProjMatrix); // Now extend Z of the result AABB to cover shadow-casters closer to the light inside the // original box foreach (var obj in objects) { if (obj.renderState.toBeDeleted || !obj.renderState.visible || !obj.renderState.castsShadow || obj.localBoundingSphereRadius <= 0f) { continue; } Vector3 lightAlignedPos = Vector3.Transform(obj.worldBoundingSphereCenter, lightTransform); Vector3 rad = new Vector3(obj.worldBoundingSphereRadius); Vector3 localMin = lightAlignedPos - rad; if (localMin.Z < resultLightBB.Min.Z) { Vector3 localMax = lightAlignedPos + rad; if (OpenTKHelper.RectsOverlap(resultLightBB.Min.Xy, resultLightBB.Max.Xy, localMin.Xy, localMax.Xy)) { resultLightBB.Min.Z = localMin.Z; } } } // Generate frustum culler from the BB extended towards light to include shadow casters Matrix4 frustumView, frustumProj; viewProjFromLightAlignedBB(ref resultLightBB, ref lightTransform, ref lightY, out frustumView, out frustumProj); Matrix4 frustumMatrix = frustumView * frustumProj; FrustumCuller = new SSFrustumCuller(ref frustumMatrix); }
private SSSkeletalAnimationMD5 readAnimation() { Match[] matches; // header seekEntry("MD5Version", "10"); seekEntry("commandline", SSMD5Parser._quotedStrRegex); matches = seekEntry("numFrames", SSMD5Parser._uintRegex); var numFrames = Convert.ToInt32(matches [1].Value); matches = seekEntry("numJoints", SSMD5Parser._uintRegex); var numJoints = Convert.ToInt32(matches [1].Value); matches = seekEntry("frameRate", SSMD5Parser._uintRegex); var frameRate = Convert.ToInt32(matches [1].Value); matches = seekEntry("numAnimatedComponents", SSMD5Parser._uintRegex); int numAnimatedComponents = Convert.ToInt32(matches [1].Value); var floatComponents = new float[numAnimatedComponents]; // hierarchy seekEntry("hierarchy", "{"); var hierarchy = new SSSkeletalJoint[numJoints]; var flags = new byte[numJoints]; for (int j = 0; j < numJoints; ++j) { hierarchy [j] = readHierarchyEntry(out flags [j]); } seekEntry("}"); // bounds seekEntry("bounds", "{"); var bounds = new SSAABB[numFrames]; for (int f = 0; f < numFrames; ++f) { bounds [f] = readBounds(); } seekEntry("}"); // base frame seekEntry("baseframe", "{"); for (int j = 0; j < numJoints; ++j) { hierarchy[j].bindPoseLocation = readBaseFrame(); } seekEntry("}"); // frames var frames = new SSSkeletalJointLocation[numFrames][]; for (int f = 0; f < numFrames; ++f) { matches = seekEntry("frame", SSMD5Parser._uintRegex, "{"); int frameIdx = Convert.ToInt32(matches [1].Value); frames [frameIdx] = readFrameJoints(flags, hierarchy, floatComponents); seekEntry("}"); } return(new SSSkeletalAnimationMD5(frameRate, hierarchy, frames, bounds)); }