float WorldLightDistanceFalloff(worldlight_t light, Vector3 delta, bool noRadiusCheck) { float falloff = 0.0f; switch (light.Type) { case EmitType.SURFACE: // Cull out stuff that's too far if (light.radius != 0) { if (Vector3.Dot(delta, delta) > (light.radius * light.radius)) return 0.0f; } return InvRSquared(delta); case EmitType.SKYLIGHT: return 1.0f; case EmitType.QUAKELIGHT: // X - r; falloff = light.Linear_Attn - (float)Math.Sqrt(Vector3.Dot(delta, delta)); if (falloff < 0f) return 0f; return falloff; case EmitType.SKYAMBIENT: return 1.0f; case EmitType.SPOTLIGHT: case EmitType.POINT: // directional & positional float dist2 = Vector3.Dot(delta, delta); float dist = (float)Math.Sqrt(dist2); // Cull out stuff that's too far if (!noRadiusCheck && (light.radius != 0) && (dist > light.radius)) return 0f; return 1f / (light.Constant_Attn + light.Linear_Attn * dist + light.Quadratic_Attn * dist2); } return 1f; }
float WorldLightAngle(worldlight_t wl, Vector3 lnormal, Vector3 snormal, Vector3 delta) { float dot = 0, dot2 = 0, ratio = 0; switch (wl.Type) { case EmitType.SURFACE: dot = Vector3.Dot(snormal, delta); if (dot < 0) return 0; dot2 = -Vector3.Dot(delta, lnormal); if (dot2 <= 0.1f / 10) return 0; // behind light surface return dot * dot2; case EmitType.POINT: dot = Vector3.Dot(snormal, delta); if (dot < 0) return 0; return dot; case EmitType.SPOTLIGHT: dot = Vector3.Dot(snormal, delta); if (dot < 0) return 0; dot2 = -Vector3.Dot(delta, lnormal); if (dot2 <= wl.stopdot2) return 0; // outside light cone ratio = dot; if (dot2 >= wl.stopdot) return ratio; // inside inner cone if ((wl.exponent == 1 || wl.exponent == 0)) { ratio *= (dot2 - wl.stopdot2) / (wl.stopdot - wl.stopdot2); } else { ratio *= (float)Math.Pow((dot2 - wl.stopdot2) / (wl.stopdot - wl.stopdot2), wl.exponent); } return ratio; case EmitType.SKYLIGHT: dot2 = -Vector3.Dot(snormal, lnormal); if (dot2 < 0) return 0; return dot2; case EmitType.QUAKELIGHT: // linear falloff dot = Vector3.Dot(snormal, delta); if (dot < 0) return 0; return dot; case EmitType.SKYAMBIENT: // not supported return 1; } return 0; }
void ComputeAmbientFromSurface(int surfid, worldlight_t skyLight,ref Vector3 color) { if (surfid != -1) { // If we hit the sky, use the sky ambient if ((world.faces_t[surfid].face.texinfo.flags & SurfFlags.SURF_SKY) == SurfFlags.SURF_SKY) { if (skyLight != null) // add in sky ambient color = skyLight.Intensity; } else { Vector3 reflectivity = world.faces_t[surfid].face.texinfo.texdata_t.reflectivity; color.X *= reflectivity.X; color.Y *= reflectivity.Y; color.Z *= reflectivity.Z; } } }
//----------------------------------------------------------------------------- // This method returns the effective intensity of a light as seen from // a particular point. PVS is used to speed up the task. //----------------------------------------------------------------------------- float LightIntensityAndDirectionAtpoint(worldlight_t light, Vector3 mid, int flags, ref Vector3 direction) { // Special case lights switch (light.Type) { case EmitType.SKYLIGHT: // There can be more than one skylight, but we should only // ever be affected by one of them (multiple ones are created from // a single light in vrad) // check to see if you can hit the sky texture Vector3 end = ViewParams.VectorMA(mid, -65500, light.Normal); trace_t trace = ClipMap.Instance.Box_Trace(mid, end, Vector3.Zero, Vector3.Zero, 0, (int)(brushflags.CONTENTS_SOLID | brushflags.CONTENTS_MOVEABLE | brushflags.CONTENTS_SLIME | brushflags.CONTENTS_OPAQUE)); // Here, we didn't hit the sky, so we must be in shadow if (((SurfFlags)trace.surfaceFlags & SurfFlags.SURF_SKY) != SurfFlags.SURF_SKY) return 0.0f; // fudge delta and dist for skylights direction.X = direction.Y = direction.Z = 0; return 1.0f; case EmitType.SKYAMBIENT: // always ignore these return 0.0f; } // all other lights // check distance direction = light.Origin - mid; float ratio = WorldLightDistanceFalloff(light, direction, (flags & 2) != 0); // Add in light style component //ratio *= light.Style; // Early out for really low-intensity lights // That way we don't need to ray-cast or normalize float intensity = Math.Max(light.Intensity[0], light.Intensity[1]); intensity = Math.Max(intensity, light.Intensity[2]); // This is about 1/256 if (intensity * ratio < 1f / 256f) return 0.0f; float dist = direction.Length(); direction.Normalize(); if ((flags & 1) == 1) // LIGHT_NO_OCCLUSION_CHECK return ratio; trace_t pm = ClipMap.Instance.Box_Trace(mid, light.Origin, Vector3.Zero, Vector3.Zero, 0, 0x1 | 0x40); // hack if ((1f - pm.fraction) * dist > 8) return 0; return ratio; }
static void LoadWorldLights(BinaryReader br, Header header) { br.BaseStream.Seek(header.lumps[15].fileofs, SeekOrigin.Begin); int numWorldlights = header.lumps[15].filelen / 88; if (header.lumps[15].filelen % 88 != 0) Common.Instance.WriteLine("LoadWorldLights: WARNING: Funny lump size"); worldlight_t[] lights = new worldlight_t[numWorldlights]; for (int i = 0; i < numWorldlights; i++) { worldlight_t light = new worldlight_t(); light.Origin = new Vector3(br.ReadSingle(),br.ReadSingle(),br.ReadSingle()); light.Intensity = new Vector3(br.ReadSingle(),br.ReadSingle(),br.ReadSingle()); light.Normal = new Vector3(br.ReadSingle(),br.ReadSingle(),br.ReadSingle()); // for surfaces and spotlights light.Cluster = br.ReadInt32(); light.Type = (EmitType)br.ReadInt32(); light.Style = br.ReadInt32(); light.stopdot = br.ReadSingle(); // start of penumbra for emit_spotlight light.stopdot2 = br.ReadSingle(); // end of penumbra for emit_spotlight light.exponent = br.ReadSingle(); light.radius = br.ReadSingle(); // cutoff distance // falloff for emit_spotlight + emit_point: // 1 / (constant_attn + linear_attn * dist + quadratic_attn * dist^2) light.Constant_Attn = br.ReadSingle(); light.Linear_Attn = br.ReadSingle(); light.Quadratic_Attn = br.ReadSingle(); light.Flags = br.ReadInt32(); light.Texinfo = br.ReadInt32(); light.Owner = br.ReadInt32(); // entity that this light it relative to // Fixup for backward compatability if (light.Type == EmitType.SPOTLIGHT) { if (light.Quadratic_Attn == 0.0f && light.Linear_Attn == 0.0f && light.Constant_Attn == 0.0f) light.Quadratic_Attn = 1.0f; if (light.exponent == 0.0f) light.exponent = 1.0f; } else if (light.Type == EmitType.POINT) { // To match earlier lighting, use quadratic... if (light.Quadratic_Attn == 0.0f && light.Linear_Attn == 0.0f && light.Constant_Attn == 0.0f) light.Quadratic_Attn = 1.0f; } if (light.radius < 1) light.radius = 0; lights[i] = light; } world.worldlights = lights; }