private static Mesh GenerateMesh(MDLFile mdl, string destPath) { var geometry = mdl.geometry; var verts = geometry.frames[0].verts; Mesh mesh = new Mesh(); var rotation = Quaternion.AngleAxis(-90, Vector3.up); Vector3[] vertices = new Vector3[verts.Length]; int[] triangles = new int[verts.Length]; for (int i = 0; i < verts.Length; i += 3) { vertices[i] = rotation * BSPFile.TransformVector(verts[i]); vertices[i + 1] = rotation * BSPFile.TransformVector(verts[i + 1]); vertices[i + 2] = rotation * BSPFile.TransformVector(verts[i + 2]); triangles[i] = i + 2; triangles[i + 1] = i + 1; triangles[i + 2] = i; } mesh.vertices = vertices; mesh.triangles = triangles; mesh.uv = geometry.uvs; mesh.RecalculateNormals(); var meshPath = destPath + "/" + mdl.name + "_mesh.asset"; AssetDatabase.CreateAsset(mesh, meshPath); return(AssetDatabase.LoadAssetAtPath <Mesh>(meshPath)); }
/// <summary> /// Create a clipping hull out of the visible hull /// </summary> /// <param name="bspFile"></param> /// <returns></returns> private Hull MakeHull0(BSPFile bspFile) { var clipNodes = new ClipNode[bspFile.Nodes.Count]; for (var i = 0; i < bspFile.Nodes.Count; ++i) { var node = bspFile.Nodes[i]; var clipNode = new ClipNode { PlaneIndex = Array.FindIndex(bspFile.Planes, plane => ReferenceEquals(plane, node.Plane)) }; for (var j = 0; j < 2; ++j) { var child = node.Children[j]; if (child.Contents >= Contents.Node) { clipNode.Children[j] = bspFile.Nodes.FindIndex(test => ReferenceEquals(test, child)); } else { clipNode.Children[j] = (int)child.Contents; } } clipNodes[i] = clipNode; } return(new Hull(0, bspFile.Nodes.Count - 1, Vector3.Zero, Vector3.Zero, clipNodes, bspFile.Planes)); }
private static BSPTexture[] AdjustTextureSizes(BSPFile bsp) { var textures = bsp.textures; var sizes = new Vector2[textures.Length]; foreach (var geometry in bsp.models[0].geometries) { var g = geometry.mesh; var tex_id = geometry.tex_id; var texture = textures[tex_id]; for (int i = 0; i < g.vertices.Length; ++i) { var uv = g.uvs[i]; float w = texture.width * uv.x; float h = texture.height * uv.y; if (w > sizes[tex_id].x || h > sizes[tex_id].y) { sizes[tex_id] = new Vector2(w, h); } } } var result = new BSPTexture[textures.Length]; for (int i = 0; i < sizes.Length; ++i) { result[i] = ResizeTextures(textures[i], sizes[i]); } return(result); }
static void GenerateCollision(BSPFile bsp, GameObject parent, BSPFace face) { GameObject levelCollision = PrefabCache.InstantiatePrefab("LevelCollision", "Assets/Prefabs"); levelCollision.transform.parent = parent.transform; levelCollision.isStatic = parent.isStatic; var verts = face.vertices; var collider = levelCollision.AddComponent <MeshCollider>(); List <Vector3> vertices = new List <Vector3>(); List <int> triangles = new List <int>(); for (int vi = 0, ti = 0; vi < verts.Length; vi += 3, ti += 3) { vertices.Add(BSPFile.TransformVector(verts[vi + 0])); vertices.Add(BSPFile.TransformVector(verts[vi + 1])); vertices.Add(BSPFile.TransformVector(verts[vi + 2])); triangles.Add(ti + 2); triangles.Add(ti + 1); triangles.Add(ti + 0); } Mesh mesh = new Mesh(); mesh.name = "Collision Mesh"; mesh.vertices = vertices.ToArray(); mesh.triangles = triangles.ToArray(); mesh.RecalculateNormals(); collider.sharedMesh = mesh; }
/// <summary> /// Create the lightmap texture for a surface /// If the surface has no lightmaps, returns null /// </summary> /// <param name="bspFile"></param> /// <param name="face"></param> /// <param name="numLightmaps"></param> /// <param name="smax"></param> /// <param name="tmax"></param> /// <returns></returns> public static Image <Rgba32> CreateLightmapTexture(BSPFile bspFile, Face face, int numLightmaps, int smax, int tmax) { if (bspFile == null) { throw new ArgumentNullException(nameof(bspFile)); } if (face == null) { throw new ArgumentNullException(nameof(face)); } if (numLightmaps < 0) { throw new ArgumentOutOfRangeException(nameof(numLightmaps)); } if (smax <= 0) { throw new ArgumentOutOfRangeException(nameof(smax)); } if (tmax <= 0) { throw new ArgumentOutOfRangeException(nameof(tmax)); } return(InternalCreateLightmapTexture(bspFile, face, numLightmaps, smax, tmax)); }
private static Image <Rgba32> InternalGenerateLightmap(BSPFile bspFile, Face face, int smax, int tmax, int lightmapIndex) { var size = smax * tmax; var colorData = new Rgba32[size]; if (bspFile.Lighting.Length > 0 && face.LightOffset != -1) { //Initialize from light data var lightmapData = new Span <Rgb24>(bspFile.Lighting, face.LightOffset + (size * lightmapIndex), size); for (var i = 0; i < size; ++i) { //Lightmap data is passed directly to shaders; shaders will handle light styles and gamma correction lightmapData[i].ToRgba32(ref colorData[i]); } } else { //Fill with fullbright for (var i = 0; i < size; ++i) { colorData[i].R = byte.MaxValue; colorData[i].G = byte.MaxValue; colorData[i].B = byte.MaxValue; colorData[i].A = byte.MaxValue; } } return(Image.LoadPixelData(colorData, smax, tmax)); }
static Mesh GenerateMesh(BSPModelGeometry geometry) { List <Vector3> vertices = new List <Vector3>(); List <int> triangles = new List <int>(); List <Vector2> uvs = new List <Vector2>(); var g = geometry.mesh; for (int vi = 0, ti = 0; vi < g.vertices.Length; vi += 3, ti += 3) { vertices.Add(BSPFile.TransformVector(g.vertices[vi + 0])); vertices.Add(BSPFile.TransformVector(g.vertices[vi + 1])); vertices.Add(BSPFile.TransformVector(g.vertices[vi + 2])); uvs.Add(g.uvs[vi + 0]); uvs.Add(g.uvs[vi + 1]); uvs.Add(g.uvs[vi + 2]); triangles.Add(ti + 2); triangles.Add(ti + 1); triangles.Add(ti + 0); } Mesh mesh = new Mesh(); mesh.name = "Brush Mesh"; mesh.vertices = vertices.ToArray(); mesh.uv = uvs.ToArray(); mesh.triangles = triangles.ToArray(); mesh.RecalculateNormals(); return(mesh); }
static void LoadBSP() { try { string path = EditorUtility.OpenFilePanelWithFilters("Open BSP", AssetUtils.GetAbsoluteAssetPath("Assets/Editor/Data/maps"), new string[] { "BSP files", "bsp" }); if (string.IsNullOrEmpty(path)) { return; } using (FileStream stream = File.OpenRead(path)) { DataStream ds = new DataStream(stream); BSPFile bsp = new BSPFile(ds); string name = Path.GetFileNameWithoutExtension(path); string destPath = "Assets/Scenes/Maps/" + name; AssetUtils.CreateFolder(destPath); var materials = GenerateMaterials(bsp, destPath); if (materials == null) // cancelled { return; } GenerateLevel(bsp, materials); } } finally { EditorUtility.ClearProgressBar(); } }
public override void SetupInstance(BSPFile bsp, entity entity, SceneEntities entities) { base.SetupInstance(bsp, entity, entities); if (string.IsNullOrEmpty(this.message)) { Debug.LogError("Can't set skill: message is null or empty"); return; } int value; if (!int.TryParse(this.message, out value)) { Debug.LogError("Can't set skill: invalid message '" + this.message + "'"); return; } if (!Enum.IsDefined(typeof(GameSkill), value)) { Debug.LogError("Can't set skill: invalid GameSkill value '" + value + "'"); return; } var trigger = entity.GetComponent <trigger_setskill>(); trigger.skill = (GameSkill)value; }
private static Image <Rgba32> InternalCreateLightmapTexture(BSPFile bspFile, Face face, int numLightmaps, int smax, int tmax) { if (numLightmaps == 0) { //Let the user decide what to do return(null); } var lightmapData = new Image <Rgba32>(numLightmaps * smax, tmax); var graphicsOptions = GraphicsOptions.Default; graphicsOptions.BlenderMode = PixelBlenderMode.Src; //Generate lightmap data for every style for (var i = 0; i < numLightmaps; ++i) { using (var styleData = InternalGenerateLightmap(bspFile, face, smax, tmax, i)) { lightmapData.Mutate(context => context.DrawImage(graphicsOptions, styleData, new SixLabors.Primitives.Point(i * smax, 0))); } } return(lightmapData); }
public override void SetupInstance(BSPFile bsp, entity entity, SceneEntities entities) { base.SetupInstance(bsp, entity, entities); var door = entity as func_door; SetupTrigger(door); SetupItems(door); SetupMovement(door); }
public static bool LoadMap(string mapFile) { if (mapFile != null && File.Exists(mapFile) && Local.InGame) { loadedMap = new BSPFile(mapFile); loadedMapFile = Local.MapFile; loadedMapName = Local.MapName; return(true); } return(false); }
static void GenerateBrush(BSPFile bsp, GameObject brush, BSPModelGeometry geometry, IList <Material> materials) { var mesh = GenerateMesh(geometry); var meshFilter = brush.GetComponent <MeshFilter>(); meshFilter.sharedMesh = mesh; var meshRenderer = brush.GetComponent <MeshRenderer>(); meshRenderer.material = materials[(int)geometry.tex_id]; }
public override void SetupInstance(BSPFile bsp, entity entity, SceneEntities entities) { base.SetupInstance(bsp, entity, entities); if (angle >= 0 && angle < 360) { entity.transform.rotation = Quaternion.AngleAxis(90 - angle, Vector3.up); } else { Debug.LogError("Unexpected angle: " + angle); } }
static void GenerateModel(BSPFile bsp, Level level, BSPModel model, IList <Material> materials, bool[] used) { if (model.entity != null) { return; } GameObject modelObj = new GameObject("Model"); modelObj.transform.parent = level.transform; modelObj.isStatic = true; GenerateGeometries(bsp, model, modelObj, materials, used); }
public override void SetupInstance(BSPFile bsp, entity entity, SceneEntities entities) { base.SetupInstance(bsp, entity, entities); var collider = entity.GetComponent <BoxCollider>(); var colliderSize = this.size; colliderSize.x += 0.4f; colliderSize.y += 0.4f; colliderSize.z += 0.4f; collider.size = colliderSize; }
public virtual void SetupInstance(BSPFile bsp, entity entity, SceneEntities entities) { if (this.target != -1) { var targetName = entities.FindTargetName(this.target); if (targetName == null) { Debug.LogError("Can't find entity with target name: " + this.target); } var target = entity.gameObject.AddComponent <EntityTarget>(); target.targetName = targetName; } var instanceFields = ReflectionUtils.ListFields(entity); var dataFields = ReflectionUtils.ListFields(this); foreach (var name in instanceFields.Keys) { FieldInfo dataField; if (dataFields.TryGetValue(name, out dataField)) { object value = dataField.GetValue(this); if (dataField.GetCustomAttribute <BSPTransformAttribute>() != null) { Type fieldType = dataField.FieldType; if (fieldType == typeof(int)) { value = BSPFile.Scale((int)value); } else if (fieldType == typeof(float)) { value = BSPFile.Scale((float)value); } else if (fieldType == typeof(Vector3)) { value = BSPFile.TransformVector((Vector3)value); } else { throw new NotImplementedException("Unexpected field type: " + fieldType); } } FieldInfo instanceField = instanceFields[name]; instanceField.SetValue(entity, value); } } }
public override void SetupInstance(BSPFile bsp, entity entity, SceneEntities entities) { base.SetupInstance(bsp, entity, entities); if (modelRef != null) { var collider = entity.GetComponent <BoxCollider>(); collider.size = this.size; } if (health > 0) { entity.gameObject.layer = LayerMask.NameToLayer("ShootTrigger"); } }
private static Mesh GenerateMesh(BSPFile bsp, string name, string destPath, TextureAtlas atlas) { List <Vector3> vertices = new List <Vector3>(); List <int> triangles = new List <int>(); List <Vector2> uvs = new List <Vector2>(); var textures = bsp.textures; int ti = 0; foreach (var geometry in bsp.models[0].geometries) { var g = geometry.mesh; var tex_id = geometry.tex_id; var texture = textures[tex_id]; for (int vi = 0; vi < g.vertices.Length; vi += 3, ti += 3) { vertices.Add(BSPFile.TransformVector(g.vertices[vi + 0])); vertices.Add(BSPFile.TransformVector(g.vertices[vi + 1])); vertices.Add(BSPFile.TransformVector(g.vertices[vi + 2])); uvs.Add(atlas.TransformUV(tex_id, g.uvs[vi + 0], texture.width, texture.height)); uvs.Add(atlas.TransformUV(tex_id, g.uvs[vi + 1], texture.width, texture.height)); uvs.Add(atlas.TransformUV(tex_id, g.uvs[vi + 2], texture.width, texture.height)); triangles.Add(ti + 2); triangles.Add(ti + 1); triangles.Add(ti + 0); } } Mesh mesh = new Mesh(); mesh.name = "Model Mesh"; mesh.vertices = vertices.ToArray(); mesh.uv = uvs.ToArray(); mesh.triangles = triangles.ToArray(); mesh.RecalculateNormals(); var meshPath = destPath + "/" + name + "_mesh.asset"; AssetDatabase.CreateAsset(mesh, meshPath); return(AssetDatabase.LoadAssetAtPath <Mesh>(meshPath)); }
private static void GenerateGeometries(BSPFile bsp, BSPModel model, GameObject parent, IList <Material> materials, bool[] used) { foreach (var geometry in model.geometries) { GameObject brush = PrefabCache.InstantiatePrefab("LevelBrush", "Assets/Prefabs"); brush.transform.parent = parent.transform; brush.isStatic = parent.isStatic; GenerateBrush(bsp, brush, geometry, materials); foreach (var face in geometry.faces) { if (!used[face.id]) { used[face.id] = true; GenerateCollision(bsp, brush.gameObject, face); } } } }
protected override void OnUpdate(TickEventArgs args) { base.OnUpdate(args); //Grab local player LocalPlayer.Reset(); ViewMatrix.Reset(); //GameRules.Reset(); PlayerResources.Reset(); ClientState.Reset(); GameDirectory.Reset(); BaseEntitites.Clear(); PlayersOld.Clear(); PlayersOld.CopyFrom(Players); Players.Clear(); Weapons.Clear(); //Load map if (ClientState.Value != null && ClientState.Value.Map.Value != null) { if (ClientState.Value.Map.Value != lastMap) { var path = Path.Combine(GameDirectory.Value, ClientState.Value.Map.Value); //try //{ lastMap = ClientState.Value.Map.Value; if (File.Exists(path)) { using (var str = new FileStream(path, FileMode.Open, FileAccess.Read)) { var bsp = new BSPFile(str); Map = bsp; } //}catch(Exception ex) //{ // Program.Logger.Error("Failed to parse map \"{0}\": {1}", path, ex.Message); //} } } } }
public void LoadMap() { if (!Utils.IsGamePathValid()) { Console.LogError("Gamepath must be fixed before loading a map."); return; } if (!Utils.IsModnameValid()) { Console.LogError("Modname should be fixed before loading a map."); } ClearMap(); //load a map file... StateManager.ChangeGameState(GameState.loading); StopAllCoroutines(); StartCoroutine(BSPFile.LoadBspRoutine(Globals.map.Text)); }
private static UnityEngine.Object CreateBSP(DataStream ds, string destPath, string name) { BSPFile bsp = new BSPFile(ds); if (bsp.models.Length != 1) { throw new ArgumentException("BSP should have 1 model only"); } var textures = AdjustTextureSizes(bsp); var atlas = new TextureAtlas(textures, 1024, 1024); var mesh = GenerateMesh(bsp, name, destPath, atlas); var skins = GenerateSkins(bsp, name, destPath, atlas); var asset = ScriptableObject.CreateInstance <MDL>(); // BSP is not quite MDL but who cares: it's a miracle I went this far with that budget asset.name = name; asset.mesh = mesh; asset.materials = skins; return(asset); }
void SetupMovement(func_door door) { Vector3 movedir; float amount; if (angle == -1) // moving up { movedir = Vector3.up; amount = this.size.y; } else if (angle == -2) // moving down { movedir = Vector2.down; amount = this.size.y; } else if (angle == 0 || angle == 90 || angle == 180 || angle == 270) { movedir = Quaternion.AngleAxis(-angle, Vector3.up) * Vector3.right; amount = angle == 0 || angle == 180 ? this.size.x : this.size.z; } else { movedir = Vector3.zero; amount = 0.0f; Debug.LogError("Unexpected angle: " + angle); } door.pos1 = door.transform.position; door.pos2 = door.pos1 + movedir * (amount - BSPFile.Scale(lip)); if ((this.spawnflags & DOOR_START_OPEN) != 0) { door.transform.position = door.pos2; // swap positions var temp = door.pos1; door.pos1 = door.pos2; door.pos2 = temp; } }
public override void SetupInstance(BSPFile bsp, entity entity, SceneEntities entities) { base.SetupInstance(bsp, entity, entities); if (this.spawnflags == 1) // not sure what this value means but it might be "crucified" { var rigidBoby = entity.GetComponent <Rigidbody>(); if (rigidBoby != null) { GameObject.DestroyImmediate(rigidBoby); } var collider = entity.GetComponent <BoxCollider>(); if (collider != null) { GameObject.DestroyImmediate(collider); } var zombie = entity as monster_zombie; zombie.crucified = true; } }
public BSPModel(string name, uint crc, BSPFile bspFile, Model subModel, Hull hull0) : base(name, crc, subModel.Mins, subModel.Maxs) { BSPFile = bspFile ?? throw new ArgumentNullException(nameof(bspFile)); SubModel = subModel ?? throw new ArgumentNullException(nameof(subModel)); var hulls = new Hull[BSPConstants.MaxHulls]; hulls[0] = new Hull(subModel.HeadNodes[0], bspFile.ClipNodes.Count - 1, hull0.ClipMins, hull0.ClipMaxs, hull0.ClipNodes, hull0.Planes); hulls[1] = new Hull(subModel.HeadNodes[1], bspFile.ClipNodes.Count - 1, PhysicsConstants.Hull1.ClipMins, PhysicsConstants.Hull1.ClipMaxs, hull0.ClipNodes, new Memory <SharpLife.Models.BSP.FileFormat.Plane>(BSPFile.Planes)); hulls[2] = new Hull(subModel.HeadNodes[2], bspFile.ClipNodes.Count - 1, PhysicsConstants.Hull2.ClipMins, PhysicsConstants.Hull2.ClipMaxs, hull0.ClipNodes, new Memory <SharpLife.Models.BSP.FileFormat.Plane>(BSPFile.Planes)); hulls[3] = new Hull(subModel.HeadNodes[3], bspFile.ClipNodes.Count - 1, PhysicsConstants.Hull3.ClipMins, PhysicsConstants.Hull3.ClipMaxs, hull0.ClipNodes, new Memory <SharpLife.Models.BSP.FileFormat.Plane>(BSPFile.Planes)); Hulls = hulls; var radius = new Vector3( Math.Abs(subModel.Mins.X) > Math.Abs(subModel.Maxs.X) ? Math.Abs(subModel.Mins.X) : Math.Abs(subModel.Maxs.X), Math.Abs(subModel.Mins.Y) > Math.Abs(subModel.Maxs.Y) ? Math.Abs(subModel.Mins.Y) : Math.Abs(subModel.Maxs.Y), Math.Abs(subModel.Mins.Z) > Math.Abs(subModel.Maxs.Z) ? Math.Abs(subModel.Mins.Z) : Math.Abs(subModel.Maxs.Z) ); Radius = radius.Length(); }
private static Material[] GenerateSkins(BSPFile bsp, string name, string destPath, TextureAtlas atlas) { var skinsPath = destPath + "/skins"; AssetUtils.CreateFolder(skinsPath); string textureName = FileUtilEx.FixFilename(name); var texturePath = skinsPath + "/" + textureName + ".png"; if (!AssetUtils.AssetPathExists(texturePath)) { atlas.WriteTexture(texturePath); AssetDatabase.Refresh(); } int index = texturePath.LastIndexOf('.'); string materialPath = texturePath.Substring(0, index) + ".mat"; if (!AssetUtils.AssetPathExists(materialPath)) { TextureImporter importer = TextureImporter.GetAtPath(texturePath) as TextureImporter; importer.textureType = TextureImporterType.Default; importer.wrapMode = TextureWrapMode.Repeat; importer.filterMode = FilterMode.Point; importer.maxTextureSize = 2048; importer.textureFormat = TextureImporterFormat.DXT1; importer.SaveAndReimport(); var material = new Material(Shader.Find("Standard")); material.mainTexture = AssetDatabase.LoadAssetAtPath <Texture2D>(texturePath); material.SetFloat("_Glossiness", 0.0f); AssetDatabase.CreateAsset(material, materialPath); } return(new Material[] { AssetDatabase.LoadAssetAtPath <Material>(materialPath) }); }
public static Image <Rgba32> GenerateLightmap(BSPFile bspFile, Face face, int smax, int tmax, int lightmapIndex) { if (bspFile == null) { throw new ArgumentNullException(nameof(bspFile)); } if (face == null) { throw new ArgumentNullException(nameof(face)); } if (smax <= 0) { throw new ArgumentOutOfRangeException(nameof(smax)); } if (tmax <= 0) { throw new ArgumentOutOfRangeException(nameof(tmax)); } return(InternalGenerateLightmap(bspFile, face, smax, tmax, lightmapIndex)); }
static GameObject GenerateEntity(BSPFile bsp, entity_t entity, IList <Material> materials, bool[] used) { var name = entity.GetType().Name; if (name.EndsWith(TYPE_PREFIX)) { name = name.Substring(0, name.Length - TYPE_PREFIX.Length); } var entityInstance = PrefabCache.InstantiatePrefab(name, "Assets/Prefabs/Entities"); if (entityInstance == null) { Debug.LogWarning("Can't load prefab: " + name); return(null); } entityInstance.isStatic = !entity.movable; if (entity.modelRef != null) { var model = entity.modelRef; entityInstance.transform.position = model.boundbox.center; if (entity.solid) { GenerateGeometries(bsp, model, entityInstance, materials, used); } } else { entityInstance.transform.position = BSPFile.TransformVector(entity.origin); } return(entityInstance); }
/// <summary> /// Builds the vertices and indices buffers for the given faces /// </summary> /// <param name="bspFile"></param> /// <param name="faces"></param> /// <param name="texture"></param> /// <param name="textureResourceSet"></param> /// <param name="defaultLightmapTexture"></param> /// <param name="lightmapBuilders"></param> /// <param name="vertices"></param> /// <param name="indices"></param> public static void BuildFacesBuffer( BSPFile bspFile, IReadOnlyList <Face> faces, MipTexture texture, ResourceSet textureResourceSet, Image <Rgba32> defaultLightmapTexture, List <LightmapBuilder> lightmapBuilders, List <BSPSurfaceData> vertices, List <uint> indices) { if (bspFile == null) { throw new ArgumentNullException(nameof(bspFile)); } if (faces == null) { throw new ArgumentNullException(nameof(faces)); } if (texture == null) { throw new ArgumentNullException(nameof(texture)); } if (textureResourceSet == null) { throw new ArgumentNullException(nameof(textureResourceSet)); } if (defaultLightmapTexture == null) { throw new ArgumentNullException(nameof(defaultLightmapTexture)); } if (lightmapBuilders == null) { throw new ArgumentNullException(nameof(lightmapBuilders)); } if (vertices == null) { throw new ArgumentNullException(nameof(vertices)); } if (indices == null) { throw new ArgumentNullException(nameof(indices)); } if (faces.Count == 0) { throw new ArgumentException("Cannot create a face buffer when no faces are provided", nameof(faces)); } if (lightmapBuilders.Count == 0) { throw new ArgumentException("You must provide at least one lightmap builder", nameof(lightmapBuilders)); } var lightmapBuilder = lightmapBuilders[lightmapBuilders.Count - 1]; var firstVertex = vertices.Count; var firstIndex = indices.Count; void AddTextureData() { lightmapBuilder.AddTextureData(new SingleTextureData { Texture = textureResourceSet, FirstIndex = (uint)firstIndex, IndicesCount = (uint)(indices.Count - firstIndex) }); } foreach (var face in faces) { var smax = (face.Extents[0] / 16) + 1; var tmax = (face.Extents[1] / 16) + 1; var numLightmaps = face.Styles.Count(style => style != BSPConstants.NoLightStyle); using (var lightmapTexture = CreateLightmapTexture(bspFile, face, numLightmaps, smax, tmax)) { var lightmap = lightmapTexture ?? defaultLightmapTexture; var coordinates = lightmapBuilder.TryAllocate(lightmap); if (!coordinates.HasValue) { //Lightmap is full //Add the current vertices to the full one AddTextureData(); //New starting point firstIndex = indices.Count; //Create a new one lightmapBuilder = new LightmapBuilder(lightmapBuilder.Width, lightmapBuilder.Height); lightmapBuilders.Add(lightmapBuilder); //This can't fail without throwing an exception coordinates = lightmapBuilder.TryAllocate(lightmap); } //Create triangles out of the face foreach (var i in Enumerable.Range(vertices.Count + 1, face.Points.Count - 2)) { indices.Add((uint)vertices.Count); indices.Add((uint)i); indices.Add((uint)i + 1); } var textureInfo = face.TextureInfo; foreach (var point in face.Points) { var s = Vector3.Dot(point, textureInfo.SNormal) + textureInfo.SValue; s /= texture.Width; var t = Vector3.Dot(point, textureInfo.TNormal) + textureInfo.TValue; t /= texture.Height; var lightmapS = Vector3.Dot(point, textureInfo.SNormal) + textureInfo.SValue; lightmapS -= face.TextureMins[0]; lightmapS += coordinates.Value.X * BSPConstants.LightmapScale; lightmapS += 8; lightmapS /= lightmapBuilder.Width * BSPConstants.LightmapScale; //lightmapS /= numLightmaps != 0 ? numLightmaps : 1; //Rescale X so it covers one lightmap in the texture var lightmapT = Vector3.Dot(point, textureInfo.TNormal) + textureInfo.TValue; lightmapT -= face.TextureMins[1]; lightmapT += coordinates.Value.Y * BSPConstants.LightmapScale; lightmapT += 8; lightmapT /= lightmapBuilder.Height * BSPConstants.LightmapScale; vertices.Add(new BSPSurfaceData { WorldTexture = new WorldTextureCoordinate { Vertex = point, Texture = new Vector2(s, t) }, Lightmap = new Vector2(lightmapS, lightmapT), LightmapXOffset = smax / (float)lightmapBuilder.Width, Style0 = face.Styles[0], Style1 = face.Styles[1], Style2 = face.Styles[2], Style3 = face.Styles[3] }); } } } AddTextureData(); }