public void update(float absoluteTimeS) { float periodicT = absoluteTimeS + _periodicTOffset; var laserParams = _laser.parameters; // update variables _interferenceOffset = laserParams.middleInterferenceUFunc(periodicT); _periodicIntensity = laserParams.intensityPeriodicFunction(periodicT); _periodicIntensity *= laserParams.intensityModulation(periodicT); // evaluate start position for this update Vector3 beamOriginLocal = Vector3.Zero; if (_laser.beamOriginPresets != null && _laser.beamOriginPresets.Count > 1) { var presetIdx = _beamId; if (presetIdx >= _laser.beamOriginPresets.Count) { presetIdx %= _laser.beamOriginPresets.Count; } beamOriginLocal = _laser.beamOriginPresets [presetIdx]; } _beamStartWorld = _laser.txfmSourceToWorld(beamOriginLocal); _beamEndWorld = _laser.txfmTargetToWorld(Vector3.Zero); Vector3 beamDirWorld = (_beamEndWorld - _beamStartWorld).Normalized(); Vector3 laserXaxisWorld, laserYaxisWorld; OpenTKHelper.TwoPerpAxes(beamDirWorld, out laserXaxisWorld, out laserYaxisWorld); // local placement to start start placement in world coordinates Vector3 localPlacement = laserParams.getBeamPlacementVector(_beamId, laserParams.numBeams, absoluteTimeS); Vector3 startPlacement = localPlacement * laserParams.beamStartPlacementScale; Vector3 startPlacementWorldOffset = laserXaxisWorld * startPlacement.X + laserYaxisWorld * startPlacement.Y + beamDirWorld * startPlacement.Z; _beamStartWorld += startPlacementWorldOffset; // end position in world coordinates including drift; before intersection test float driftX = laserParams.driftXFunc(periodicT); float driftY = laserParams.driftYFunc(periodicT); var driftMod = laserParams.driftModulationFunc(periodicT); Vector3 driftedEndPlacement = laserParams.beamDestSpread * localPlacement + new Vector3(driftX, driftY, 0f) * driftMod; Vector3 endPlacementWorldOffset = laserXaxisWorld * driftedEndPlacement.X + laserYaxisWorld * driftedEndPlacement.Y + beamDirWorld * driftedEndPlacement.Z; _beamEndWorld += endPlacementWorldOffset; _hitsAnObstacle = false; // intersects with any of the intersecting objects if (_laser.beamObstacles != null) { //if (false) { // TODO note the code below is slow. Wen you start having many lasers // this will cause problems. Consider using BVH for ray tests or analyzing // intersection math. float closestDistance = float.PositiveInfinity; foreach (var obj in _laser.beamObstacles) { float distanceToInterect; var ray = new SSRay(_beamStartWorld, (_beamEndWorld - _beamStartWorld).Normalized()); if (obj.Intersect(ref ray, out distanceToInterect)) { if (distanceToInterect < closestDistance) { closestDistance = distanceToInterect; _hitsAnObstacle = true; _beamEndWorld = _beamStartWorld + ray.dir * closestDistance; } } } } }
public static void SimpleShadowmapProjection( List <SSObject> objects, SSLight light, FrustumCuller frustum, // can be null (disabled) SSCamera camera, out float width, out float height, out float nearZ, out float farZ, out Vector3 viewEye, out Vector3 viewTarget, out Vector3 viewUp) { if (light.Type != SSLight.LightType.Directional) { throw new NotSupportedException(); } // light-aligned unit vectors Vector3 lightZ = light.Direction.Normalized(); Vector3 lightX, lightY; OpenTKHelper.TwoPerpAxes(lightZ, out lightX, out lightY); // Step 1: light-direction aligned AABB of the visible objects Vector3 projBBMin = new Vector3(float.PositiveInfinity); Vector3 projBBMax = new Vector3(float.NegativeInfinity); foreach (var obj in objects) { if (obj.renderState.toBeDeleted || !obj.renderState.visible || !obj.renderState.castsShadow) { continue; } else if (frustum == null || (obj.boundingSphere != null && frustum.isSphereInsideFrustum(obj.Pos, obj.ScaledRadius))) { // determine AABB in light coordinates of the objects so far Vector3 lightAlignedPos = OpenTKHelper.ProjectCoord(obj.Pos, lightX, lightY, lightZ); Vector3 rad = new Vector3(obj.ScaledRadius); Vector3 localMin = lightAlignedPos - rad; Vector3 localMax = lightAlignedPos + rad; projBBMin = Vector3.ComponentMin(projBBMin, localMin); projBBMax = Vector3.ComponentMax(projBBMax, localMax); } } if (frustum != null) { // then we need to do a second pass, including shadow-casters that // are between the camera-frusum and the light // compute the camera's position in lightspace, because we need to // include everything "closer" that the midline of the camera frustum Vector3 lightAlignedCameraPos = OpenTKHelper.ProjectCoord(camera.Pos, lightX, lightY, lightZ); float minZTest = lightAlignedCameraPos.Z; // TODO what happens if all objects are exluded? // Step 2: Extend Z of AABB to cover objects "between" current AABB and the light foreach (var obj in objects) { if (obj.renderState.toBeDeleted || !obj.renderState.visible || !obj.renderState.castsShadow) { continue; } Vector3 lightAlignedPos = OpenTKHelper.ProjectCoord(obj.Pos, lightX, lightY, lightZ); Vector3 rad = new Vector3(obj.ScaledRadius); Vector3 localMin = lightAlignedPos - rad; Vector3 localMax = lightAlignedPos + rad; if (OpenTKHelper.RectsOverlap(projBBMin.Xy, projBBMax.Xy, localMin.Xy, localMax.Xy) && localMin.Z < minZTest) { projBBMin = Vector3.ComponentMin(projBBMin, localMin); projBBMax = Vector3.ComponentMax(projBBMax, localMax); } } } // Finish the projection matrix // Use center of AABB in regular coordinates to get the view matrix Vector3 centerAligned = (projBBMin + projBBMax) / 2f; viewTarget = centerAligned.X * lightX + centerAligned.Y * lightY + centerAligned.Z * lightZ; float farEnough = (centerAligned.Z - projBBMin.Z) + 1f; viewEye = viewTarget - farEnough * lightZ; viewUp = lightY; width = projBBMax.X - projBBMin.X; height = projBBMax.Y - projBBMin.Y; nearZ = 1f; farZ = 1f + (projBBMax.Z - projBBMin.Z); }