mirror of
https://git.do.srb2.org/STJr/UltimateZoneBuilder.git
synced 2025-01-31 04:40:55 +00:00
Added VOXELDEF support.
Visual mode: added "Match Brightness" action. DECORATE parser: actor names, which contain "+" and "-" symbols, are now parsed correctly. Fixed an error in lump ranges creation logic. Cosmetic fixes in various warning/error messages. Updated documentation.
This commit is contained in:
parent
46feeb422f
commit
47a84ad20f
23 changed files with 1043 additions and 165 deletions
|
@ -566,6 +566,12 @@
|
|||
<td><div align="center">Ctrl</div></td>
|
||||
<td>Lowers the targeted or selected floors/ceilings to match the height of adjacent sector. Hold Ctrl to lower to lowest surface in selection. Also drops selected things to floor.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Match Brightness</td>
|
||||
<td><div align="center">Ctrl+M</div></td>
|
||||
<td><div align="center"></div></td>
|
||||
<td>Makes the brightness of selected surfaces the same as the brightness of highlighted surface (UDMF only).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Move Backward</td>
|
||||
<td><div align="center">D</div></td>
|
||||
|
|
|
@ -864,6 +864,7 @@
|
|||
<Compile Include="ZDoom\StateGoto.cs" />
|
||||
<Compile Include="ZDoom\TexturesParser.cs" />
|
||||
<Compile Include="ZDoom\TextureStructure.cs" />
|
||||
<Compile Include="ZDoom\VoxeldefParser.cs" />
|
||||
<Compile Include="ZDoom\ZDTextParser.cs" />
|
||||
<Compile Include="ZDoom\DecorateParser.cs" />
|
||||
<Compile Include="ZDoom\StateStructure.cs" />
|
||||
|
|
|
@ -91,13 +91,8 @@ namespace CodeImp.DoomBuilder.Config
|
|||
public bool AbsoluteZ { get { return absolutez; } }
|
||||
public SizeF SpriteScale { get { return spritescale; } }
|
||||
|
||||
//mxd. need this to add model overrides for things defined in configs.
|
||||
public string ClassName {
|
||||
get {
|
||||
if (actor != null)
|
||||
return actor.ClassName;
|
||||
return classname;
|
||||
} }
|
||||
//mxd. Need this to add model overrides for things defined in configs.
|
||||
public string ClassName { get { return (actor != null ? actor.ClassName : classname); } }
|
||||
|
||||
#endregion
|
||||
|
||||
|
|
|
@ -313,16 +313,14 @@ namespace CodeImp.DoomBuilder.Data
|
|||
LoadInternalSprites();
|
||||
|
||||
//mxd
|
||||
General.MainWindow.DisplayStatus(StatusType.Busy, "Parsing MAPINFO...");
|
||||
loadMapInfo();
|
||||
ModelReader.Init();
|
||||
loadVoxels();
|
||||
Dictionary<string, int> actorsByClass = createActorsByClassList();
|
||||
General.MainWindow.DisplayStatus(StatusType.Busy, "Parsing model definitions...");
|
||||
loadModeldefs(actorsByClass);
|
||||
General.MainWindow.DisplayStatus(StatusType.Busy, "Parsing GLDEFS...");
|
||||
loadGldefs(actorsByClass);
|
||||
actorsByClass = null; //don't need them any more
|
||||
General.MainWindow.DisplayReady();
|
||||
//don't need them any more
|
||||
actorsByClass = null;
|
||||
|
||||
// Process colormaps (we just put them in as textures)
|
||||
foreach(KeyValuePair<long, ImageData> t in colormapsonly)
|
||||
|
@ -1055,37 +1053,35 @@ namespace CodeImp.DoomBuilder.Data
|
|||
foreach(ThingTypeInfo ti in General.Map.Data.ThingTypes)
|
||||
{
|
||||
// Valid sprite name?
|
||||
if((ti.Sprite.Length > 0) && (ti.Sprite.Length < 9))
|
||||
if(ti.Sprite.Length == 0 || ti.Sprite.Length > 8) continue; //mxd
|
||||
|
||||
ImageData image = null;
|
||||
|
||||
// Sprite not in our collection yet?
|
||||
if(!sprites.ContainsKey(ti.SpriteLongName))
|
||||
{
|
||||
ImageData image = null;
|
||||
|
||||
// Sprite not in our collection yet?
|
||||
if(!sprites.ContainsKey(ti.SpriteLongName))
|
||||
// Find sprite data
|
||||
Stream spritedata = GetSpriteData(ti.Sprite);
|
||||
if(spritedata != null)
|
||||
{
|
||||
// Find sprite data
|
||||
Stream spritedata = GetSpriteData(ti.Sprite);
|
||||
if(spritedata != null)
|
||||
{
|
||||
// Make new sprite image
|
||||
image = new SpriteImage(ti.Sprite);
|
||||
|
||||
// Add to collection
|
||||
sprites.Add(ti.SpriteLongName, image);
|
||||
}
|
||||
else
|
||||
{ //mxd
|
||||
General.ErrorLogger.Add(ErrorType.Error, "Missing sprite lump '" + ti.Sprite + "'. Forgot to include required resources?");
|
||||
}
|
||||
// Make new sprite image
|
||||
image = new SpriteImage(ti.Sprite);
|
||||
|
||||
// Add to collection
|
||||
sprites.Add(ti.SpriteLongName, image);
|
||||
}
|
||||
else
|
||||
{ //mxd
|
||||
General.ErrorLogger.Add(ErrorType.Error, "Missing sprite lump '" + ti.Sprite + "'. Forgot to include required resources?");
|
||||
}
|
||||
else
|
||||
{
|
||||
image = sprites[ti.SpriteLongName];
|
||||
}
|
||||
|
||||
// Add to preview manager
|
||||
if(image != null)
|
||||
previews.AddImage(image);
|
||||
}
|
||||
else
|
||||
{
|
||||
image = sprites[ti.SpriteLongName];
|
||||
}
|
||||
|
||||
// Add to preview manager
|
||||
if(image != null) previews.AddImage(image);
|
||||
}
|
||||
|
||||
// Output info
|
||||
|
@ -1388,7 +1384,7 @@ namespace CodeImp.DoomBuilder.Data
|
|||
|
||||
#endregion
|
||||
|
||||
#region ================== mxd. Modeldef, Gldefs, Mapinfo
|
||||
#region ================== mxd. Modeldef, Voxeldef, Gldefs, Mapinfo
|
||||
|
||||
//mxd. This creates <Actor Class, Thing.Type> dictionary. Should be called after all DECORATE actors are parsed
|
||||
private Dictionary<string, int> createActorsByClassList() {
|
||||
|
@ -1468,7 +1464,7 @@ namespace CodeImp.DoomBuilder.Data
|
|||
if (actorsByClass == null || actorsByClass.Count == 0) return;
|
||||
|
||||
Dictionary<string, ModelData> modelDefEntriesByName = new Dictionary<string, ModelData>();
|
||||
ModeldefParser mdeParser = new ModeldefParser();
|
||||
ModeldefParser parser = new ModeldefParser();
|
||||
|
||||
foreach (DataReader dr in containers) {
|
||||
currentreader = dr;
|
||||
|
@ -1476,8 +1472,8 @@ namespace CodeImp.DoomBuilder.Data
|
|||
Dictionary<string, Stream> streams = dr.GetModeldefData();
|
||||
foreach (KeyValuePair<string, Stream> group in streams) {
|
||||
// Parse the data
|
||||
if (mdeParser.Parse(group.Value, currentreader.Location.location + "\\" + group.Key)) {
|
||||
foreach (KeyValuePair<string, ModelData> g in mdeParser.ModelDefEntries) {
|
||||
if(parser.Parse(group.Value, currentreader.Location.location + "\\" + group.Key)) {
|
||||
foreach(KeyValuePair<string, ModelData> g in parser.Entries) {
|
||||
modelDefEntriesByName.Add(g.Key, g.Value);
|
||||
}
|
||||
}
|
||||
|
@ -1496,6 +1492,83 @@ namespace CodeImp.DoomBuilder.Data
|
|||
foreach(Thing t in General.Map.Map.Things) t.UpdateModelStatus();
|
||||
}
|
||||
|
||||
//mxd
|
||||
private void loadVoxels() {
|
||||
//Get names of all voxel models, which can be used "as is"
|
||||
Dictionary<string, bool> voxelNames = new Dictionary<string, bool>();
|
||||
|
||||
foreach(DataReader dr in containers) {
|
||||
currentreader = dr;
|
||||
|
||||
string[] result = dr.GetVoxelNames();
|
||||
if(result == null) continue;
|
||||
|
||||
foreach(string s in result) {
|
||||
if(!voxelNames.ContainsKey(s)) voxelNames.Add(s, false);
|
||||
}
|
||||
}
|
||||
|
||||
Dictionary<string, List<int>> sprites = new Dictionary<string, List<int>>();
|
||||
|
||||
// Go for all things
|
||||
foreach(ThingTypeInfo ti in thingtypes.Values) {
|
||||
// Valid sprite name?
|
||||
string sprite = string.Empty;
|
||||
|
||||
if(ti.Sprite.Length == 0 || ti.Sprite.Length > 8) {
|
||||
if(ti.Actor == null) continue;
|
||||
sprite = ti.Actor.FindSuitableVoxel(voxelNames);
|
||||
} else {
|
||||
sprite = ti.Sprite;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(sprite)) continue;
|
||||
if(!sprites.ContainsKey(sprite)) sprites.Add(sprite, new List<int>());
|
||||
sprites[sprite].Add(ti.Index);
|
||||
}
|
||||
|
||||
VoxeldefParser parser = new VoxeldefParser();
|
||||
Dictionary<string, bool> processed = new Dictionary<string,bool>();
|
||||
|
||||
//parse VOXLEDEF
|
||||
foreach(DataReader dr in containers) {
|
||||
currentreader = dr;
|
||||
|
||||
KeyValuePair<string, Stream> group = dr.GetVoxeldefData();
|
||||
if(group.Value != null && parser.Parse(group.Value, group.Key)) {
|
||||
foreach(KeyValuePair<string, ModelData> entry in parser.Entries){
|
||||
foreach(KeyValuePair<string, List<int>> sc in sprites) {
|
||||
if (sc.Key.Contains(entry.Key)) {
|
||||
foreach(int id in sc.Value) {
|
||||
modeldefEntries[id] = entry.Value;
|
||||
}
|
||||
processed.Add(entry.Key, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentreader = null;
|
||||
|
||||
//get voxel models
|
||||
foreach(KeyValuePair<string, bool> group in voxelNames) {
|
||||
if(processed.ContainsKey(group.Key)) continue;
|
||||
foreach (KeyValuePair<string, List<int>> sc in sprites) {
|
||||
if(sc.Key.Contains(group.Key)) {
|
||||
//it's a model without a definition, and it corresponds to a sprite we can display, so let's add it
|
||||
ModelData data = new ModelData();
|
||||
data.IsVoxel = true;
|
||||
data.ModelNames.Add(group.Key);
|
||||
|
||||
foreach(int id in sprites[sc.Key]) {
|
||||
modeldefEntries[id] = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//mxd. This parses gldefs. Should be called after all DECORATE actors are parsed and actorsByClass dictionary created
|
||||
private void loadGldefs(Dictionary<string, int> actorsByClass) {
|
||||
//if no actors defined in DECORATE or game config...
|
||||
|
|
|
@ -162,6 +162,18 @@ namespace CodeImp.DoomBuilder.Data
|
|||
public virtual Dictionary<string, Stream> GetGldefsData(GameType gameType) { return new Dictionary<string, Stream>(); }
|
||||
public virtual Dictionary<string, Stream> GetGldefsData(string location) { return new Dictionary<string, Stream>(); }
|
||||
|
||||
//mxd. When implemented, this returns the list of voxel model names
|
||||
public virtual string[] GetVoxelNames() { return null; }
|
||||
|
||||
//mxd. When implemented, this checks if the given voxel lump exists
|
||||
public virtual bool GetVoxelExists(string name) { return false; }
|
||||
|
||||
//mxd. When implemented, this returns the voxel lump
|
||||
public virtual Stream GetVoxelData(string name) { return null; }
|
||||
|
||||
//mxd
|
||||
public virtual KeyValuePair<string, Stream> GetVoxeldefData() { return new KeyValuePair<string,Stream>(); }
|
||||
|
||||
//mxd
|
||||
internal virtual MemoryStream LoadFile(string name) { return null; }
|
||||
//mxd
|
||||
|
|
|
@ -232,7 +232,51 @@ namespace CodeImp.DoomBuilder.Data
|
|||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region ================== Voxels (mxd)
|
||||
|
||||
//mxd. This finds and returns a voxel stream
|
||||
public override Stream GetVoxelData(string name) {
|
||||
// Error when suspended
|
||||
if(issuspended) throw new Exception("Data reader is suspended");
|
||||
|
||||
try {
|
||||
// Find in voxels directory
|
||||
string path = Path.Combine(VOXELS_DIR, Path.GetDirectoryName(name));
|
||||
string filename = FindFirstFile(path, Path.GetFileName(name), true);
|
||||
if((filename != null) && FileExists(filename)) {
|
||||
return LoadFile(filename);
|
||||
}
|
||||
} catch(Exception e) {
|
||||
General.ErrorLogger.Add(ErrorType.Error, e.GetType().Name + " while loading voxel '" + name + "' from directory: " + e.Message);
|
||||
}
|
||||
|
||||
// Nothing found
|
||||
return null;
|
||||
}
|
||||
|
||||
//mxd. This checks if the given sprite exists
|
||||
public override bool GetVoxelExists(string name) {
|
||||
// Error when suspended
|
||||
if(issuspended) throw new Exception("Data reader is suspended");
|
||||
|
||||
// Find in voxels directory
|
||||
try {
|
||||
string path = Path.Combine(VOXELS_DIR, Path.GetDirectoryName(name));
|
||||
string filename = FindFirstFile(path, Path.GetFileName(name), true);
|
||||
if((filename != null) && FileExists(filename)) {
|
||||
return true;
|
||||
}
|
||||
} catch(Exception e) {
|
||||
General.ErrorLogger.Add(ErrorType.Error, e.GetType().Name + " while checking voxel '" + name + "' existance in directory: " + e.Message);
|
||||
}
|
||||
|
||||
// Nothing found
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Methods
|
||||
|
||||
// Return a short name for this data location
|
||||
|
|
|
@ -238,6 +238,44 @@ namespace CodeImp.DoomBuilder.Data
|
|||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Voxels (mxd)
|
||||
|
||||
//mxd. This finds and returns a voxel stream
|
||||
public override Stream GetVoxelData(string name) {
|
||||
// Error when suspended
|
||||
if(issuspended) throw new Exception("Data reader is suspended");
|
||||
|
||||
string pfilename = name.Replace('\\', '^');
|
||||
|
||||
// Find in sprites directory
|
||||
string filename = FindFirstFile(VOXELS_DIR, pfilename, true);
|
||||
if((filename != null) && FileExists(filename)) {
|
||||
return LoadFile(filename);
|
||||
}
|
||||
|
||||
// Nothing found
|
||||
return null;
|
||||
}
|
||||
|
||||
//mxd. This checks if the given voxel exists
|
||||
public override bool GetVoxelExists(string name) {
|
||||
// Error when suspended
|
||||
if(issuspended) throw new Exception("Data reader is suspended");
|
||||
|
||||
string pfilename = name.Replace('\\', '^');
|
||||
|
||||
// Find in sprites directory
|
||||
string filename = FindFirstFile(VOXELS_DIR, pfilename, true);
|
||||
if((filename != null) && FileExists(filename)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Nothing found
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Methods
|
||||
|
|
|
@ -20,6 +20,7 @@ using System;
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using CodeImp.DoomBuilder.GZBuilder.Data;
|
||||
|
||||
#endregion
|
||||
|
@ -37,6 +38,7 @@ namespace CodeImp.DoomBuilder.Data
|
|||
protected const string SPRITES_DIR = "sprites";
|
||||
protected const string COLORMAPS_DIR = "colormaps";
|
||||
protected const string GRAPHICS_DIR = "graphics"; //mxd
|
||||
protected const string VOXELS_DIR = "voxels"; //mxd
|
||||
|
||||
#endregion
|
||||
|
||||
|
@ -461,16 +463,16 @@ namespace CodeImp.DoomBuilder.Data
|
|||
|
||||
#endregion
|
||||
|
||||
#region ================== Modeldef
|
||||
|
||||
#region ================== Modeldef (mxd)
|
||||
|
||||
//mxd
|
||||
public override Dictionary<string, Stream> GetModeldefData() {
|
||||
Dictionary<string, Stream> streams = new Dictionary<string, Stream>();
|
||||
// Error when suspended
|
||||
if (issuspended) throw new Exception("Data reader is suspended");
|
||||
|
||||
//modedef should be in root folder
|
||||
string[] allFiles = GetAllFiles("", false);
|
||||
Dictionary<string, Stream> streams = new Dictionary<string, Stream>();
|
||||
|
||||
foreach (string s in allFiles) {
|
||||
if (s.ToLowerInvariant().IndexOf("modeldef") != -1) {
|
||||
|
@ -481,10 +483,48 @@ namespace CodeImp.DoomBuilder.Data
|
|||
return streams;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Voxeldef (mxd)
|
||||
|
||||
//mxd. This returns the list of voxels, which can be used without VOXELDEF definition
|
||||
public override string[] GetVoxelNames() {
|
||||
// Error when suspended
|
||||
if(issuspended) throw new Exception("Data reader is suspended");
|
||||
|
||||
string[] files = GetAllFiles("voxels", false);
|
||||
List<string> voxels = new List<string>();
|
||||
Regex spriteName = new Regex("(?i)\\A[a-z0-9]{4}([a-z][0-9]{0,2})$");
|
||||
|
||||
for(int i = 0; i < files.Length; i++) {
|
||||
string s = Path.GetFileNameWithoutExtension(files[i]).ToUpperInvariant();
|
||||
if(spriteName.IsMatch(s)) voxels.Add(s);
|
||||
}
|
||||
|
||||
return voxels.ToArray();
|
||||
}
|
||||
|
||||
//mxd
|
||||
public override KeyValuePair<string, Stream> GetVoxeldefData() {
|
||||
// Error when suspended
|
||||
if(issuspended) throw new Exception("Data reader is suspended");
|
||||
|
||||
//voxeldef should be in root folder
|
||||
string[] files = GetAllFiles("", false);
|
||||
|
||||
foreach(string s in files) {
|
||||
if(Path.GetFileNameWithoutExtension(s).ToUpperInvariant() == "VOXELDEF") {
|
||||
return new KeyValuePair<string,Stream>(s, LoadFile(s));
|
||||
}
|
||||
}
|
||||
|
||||
return new KeyValuePair<string,Stream>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== (Z)MAPINFO
|
||||
|
||||
#region ================== (Z)MAPINFO (mxd)
|
||||
|
||||
//mxd
|
||||
public override Dictionary<string, Stream> GetMapinfoData() {
|
||||
Dictionary<string, Stream> streams = new Dictionary<string, Stream>();
|
||||
|
@ -507,14 +547,15 @@ namespace CodeImp.DoomBuilder.Data
|
|||
|
||||
#endregion
|
||||
|
||||
#region ================== GLDEFS
|
||||
#region ================== GLDEFS (mxd)
|
||||
|
||||
//mxd
|
||||
public override Dictionary<string, Stream> GetGldefsData(GameType gameType) {
|
||||
Dictionary<string, Stream> streams = new Dictionary<string, Stream>();
|
||||
// Error when suspended
|
||||
if (issuspended) throw new Exception("Data reader is suspended");
|
||||
|
||||
Dictionary<string, Stream> streams = new Dictionary<string, Stream>();
|
||||
|
||||
//at least one of gldefs should be in root folder
|
||||
string[] allFiles = GetAllFiles("", false);
|
||||
|
||||
|
@ -540,15 +581,12 @@ namespace CodeImp.DoomBuilder.Data
|
|||
|
||||
//mxd
|
||||
public override Dictionary<string, Stream> GetGldefsData(string location) {
|
||||
Dictionary<string, Stream> streams = new Dictionary<string, Stream>();
|
||||
// Error when suspended
|
||||
if (issuspended) throw new Exception("Data reader is suspended");
|
||||
|
||||
Dictionary<string, Stream> streams = new Dictionary<string, Stream>();
|
||||
Stream s = LoadFile(location);
|
||||
|
||||
if (s != null)
|
||||
streams.Add(location, s);
|
||||
|
||||
if (s != null) streams.Add(location, s);
|
||||
return streams;
|
||||
}
|
||||
|
||||
|
|
|
@ -104,7 +104,7 @@ namespace CodeImp.DoomBuilder.Data
|
|||
invertedflatranges.Add(range);
|
||||
}
|
||||
|
||||
for (int i = 0; i < flatranges.Count; i++) {
|
||||
for (int i = 1; i < flatranges.Count; i++) {
|
||||
if (flatranges[i].start == 0) continue;
|
||||
LumpRange range = new LumpRange();
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.Data
|
|||
Vector3 v7 = new Vector3(bbs.MinX, bbs.MaxY, bbs.MaxZ);
|
||||
Vector3 v8 = new Vector3(bbs.MaxX, bbs.MaxY, bbs.MaxZ);
|
||||
|
||||
return new Vector3[] { v0, v1, v2, v3, v4, v5, v6, v7, v8 };
|
||||
return new[] { v0, v1, v2, v3, v4, v5, v6, v7, v8 };
|
||||
}
|
||||
|
||||
public static Vector3[] CalculateBoundingPlane(BoundingBoxSizes bbs) {
|
||||
|
@ -45,7 +45,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.Data
|
|||
//center
|
||||
Vector3 v0 = new Vector3(bbs.MinX + (bbs.MaxX - bbs.MinX) / 2, bbs.MinY + (bbs.MaxY - bbs.MinY) / 2, bbs.MinZ + (bbs.MaxZ - bbs.MinZ) / 2);
|
||||
Vector3 v1 = new Vector3(bbs.MinX, bbs.MinY, bbs.MinZ);
|
||||
return new Vector3[] { v0, v1 };
|
||||
return new[] { v0, v1 };
|
||||
}
|
||||
|
||||
public static void UpdateBoundingBoxSizes(ref BoundingBoxSizes bbs, WorldVertex v) {
|
||||
|
|
|
@ -2,35 +2,11 @@
|
|||
using SlimDX;
|
||||
using SlimDX.Direct3D9;
|
||||
using CodeImp.DoomBuilder.GZBuilder.MD3;
|
||||
using CodeImp.DoomBuilder.Data;
|
||||
using System.IO;
|
||||
using System;
|
||||
using System.Text;
|
||||
using CodeImp.DoomBuilder.Rendering;
|
||||
using CodeImp.DoomBuilder.Geometry;
|
||||
using CodeImp.DoomBuilder.IO;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
|
||||
namespace CodeImp.DoomBuilder.GZBuilder.Data
|
||||
{
|
||||
internal sealed class ModelData
|
||||
{
|
||||
private const float VERTICAL_STRETCH = 1 / 1.2f;
|
||||
|
||||
private class MD3LoadResult
|
||||
{
|
||||
public List<string> Skins;
|
||||
public List<Mesh> Meshes;
|
||||
public string Errors;
|
||||
|
||||
public MD3LoadResult() {
|
||||
Skins = new List<string>();
|
||||
Meshes = new List<Mesh>();
|
||||
}
|
||||
}
|
||||
|
||||
internal string ClassName;
|
||||
internal List<string> ModelNames;
|
||||
internal List<string> TextureNames;
|
||||
|
||||
|
@ -45,10 +21,13 @@ namespace CodeImp.DoomBuilder.GZBuilder.Data
|
|||
internal float AngleOffset; //in radians
|
||||
internal float PitchOffset; //in radians
|
||||
internal float RollOffset; //in radians
|
||||
internal bool OverridePalette; //used for voxel models only
|
||||
internal bool IsVoxel;
|
||||
|
||||
internal ModelData() {
|
||||
ModelNames = new List<string>();
|
||||
TextureNames = new List<string>();
|
||||
Scale = new Vector3(1, 1, 1);
|
||||
}
|
||||
|
||||
internal void Dispose() {
|
||||
|
|
|
@ -72,7 +72,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom {
|
|||
SkipWhitespace(true);
|
||||
token = ReadToken();
|
||||
if (token != "{") {
|
||||
General.ErrorLogger.Add(ErrorType.Error, "Error in '" + sourcefilename + "' at line " + GetCurrentLineNumber() + ": expected '{', but got " + token);
|
||||
General.ErrorLogger.Add(ErrorType.Error, "Error in '" + sourcefilename + "' at line " + GetCurrentLineNumber() + ": expected '{', but got '" + token + "'");
|
||||
continue; //something wrong with gldefs declaration, continue to next one
|
||||
}
|
||||
|
||||
|
@ -339,7 +339,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom {
|
|||
token = ReadToken();
|
||||
|
||||
if (token != "{") {
|
||||
General.ErrorLogger.Add(ErrorType.Error, "Error in '" + sourcefilename + "' at line " + GetCurrentLineNumber() + ": expected '{', but got " + token);
|
||||
General.ErrorLogger.Add(ErrorType.Error, "Error in '" + sourcefilename + "' at line " + GetCurrentLineNumber() + ": expected '{', but got '" + token + "'");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -169,7 +169,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom {
|
|||
else
|
||||
mapInfo.OutsideFogColor = color;
|
||||
} else { //...or not
|
||||
General.ErrorLogger.Add(ErrorType.Error, "Failed to parse " + fadeType + " value from string '" + colorVal + "' in " + sourcename + "' at line " + GetCurrentLineNumber());
|
||||
General.ErrorLogger.Add(ErrorType.Error, "Failed to parse " + fadeType + " value from string '" + colorVal + "' in '" + sourcename + "' at line " + GetCurrentLineNumber());
|
||||
datastream.Seek(-token.Length - 1, SeekOrigin.Current); //step back and try parsing this token again
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -6,18 +6,18 @@ using CodeImp.DoomBuilder.GZBuilder.Data;
|
|||
namespace CodeImp.DoomBuilder.GZBuilder.GZDoom {
|
||||
|
||||
internal class ModeldefParser : ZDTextParser {
|
||||
private Dictionary<string, ModelData> modelDefEntries; //classname, entry
|
||||
internal Dictionary<string, ModelData> ModelDefEntries { get { return modelDefEntries; } }
|
||||
private Dictionary<string, ModelData> entries; //classname, entry
|
||||
internal Dictionary<string, ModelData> Entries { get { return entries; } }
|
||||
internal string Source { get { return sourcename; } }
|
||||
|
||||
internal ModeldefParser() {
|
||||
modelDefEntries = new Dictionary<string, ModelData>();
|
||||
entries = new Dictionary<string, ModelData>();
|
||||
}
|
||||
|
||||
//should be called after all decorate actors are parsed
|
||||
public override bool Parse(Stream stream, string sourcefilename) {
|
||||
base.Parse(stream, sourcefilename);
|
||||
modelDefEntries = new Dictionary<string, ModelData>();
|
||||
entries = new Dictionary<string, ModelData>();
|
||||
|
||||
// Continue until at the end of the stream
|
||||
while (SkipWhitespace(true)) {
|
||||
|
@ -29,24 +29,21 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom {
|
|||
if (token == "model") { //model structure start
|
||||
//find classname
|
||||
SkipWhitespace(true);
|
||||
string className = StripTokenQuotes(ReadToken()).ToLowerInvariant();
|
||||
|
||||
if (!string.IsNullOrEmpty(className)) {
|
||||
if(modelDefEntries.ContainsKey(className)) continue; //already got this class; continue to next one
|
||||
string className = StripTokenQuotes(ReadToken(ActorStructure.ACTOR_CLASS_SPECIAL_TOKENS)).ToLowerInvariant();
|
||||
|
||||
if(!string.IsNullOrEmpty(className) && !entries.ContainsKey(className)) {
|
||||
//now find opening brace
|
||||
SkipWhitespace(true);
|
||||
token = ReadToken();
|
||||
if (token != "{") {
|
||||
General.ErrorLogger.Add(ErrorType.Error, "Unexpected token found in "+sourcefilename+" at line "+GetCurrentLineNumber()+": expected '{', but got " + token);
|
||||
General.ErrorLogger.Add(ErrorType.Error, "Unexpected token found in '"+sourcefilename+"' at line "+GetCurrentLineNumber()+": expected '{', but got '" + token + "'");
|
||||
continue; //something wrong with modeldef declaration, continue to next one
|
||||
}
|
||||
|
||||
ModeldefStructure mds = new ModeldefStructure();
|
||||
ModelData mde = mds.Parse(this);
|
||||
if (mde != null) {
|
||||
mde.ClassName = className;
|
||||
modelDefEntries.Add(className, mde);
|
||||
entries.Add(className, mde);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,9 +71,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom {
|
|||
|
||||
}
|
||||
|
||||
if (modelDefEntries.Count > 0)
|
||||
return true;
|
||||
return false;
|
||||
return entries.Count > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom {
|
|||
}
|
||||
|
||||
if (modelIndex >= MAX_MODELS) {
|
||||
General.ErrorLogger.Add(ErrorType.Error, "Error in " + parser.Source + " at line " + parser.GetCurrentLineNumber() + ": GZDoom doesn't allow more than " + MAX_MODELS + " per MODELDEF entry!");
|
||||
General.ErrorLogger.Add(ErrorType.Error, "Error in " + parser.Source + " at line " + parser.GetCurrentLineNumber() + ": GZDoom doesn't allow more than " + MAX_MODELS + " models per MODELDEF entry!");
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -97,7 +97,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom {
|
|||
}
|
||||
|
||||
if (skinIndex >= MAX_MODELS) {
|
||||
General.ErrorLogger.Add(ErrorType.Error, "Error in " + parser.Source + " at line " + parser.GetCurrentLineNumber() + ": GZDoom doesn't allow more than " + MAX_MODELS + " per MODELDEF entry!");
|
||||
General.ErrorLogger.Add(ErrorType.Error, "Error in " + parser.Source + " at line " + parser.GetCurrentLineNumber() + ": GZDoom doesn't allow more than " + MAX_MODELS + " skins per MODELDEF entry!");
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -261,7 +261,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom {
|
|||
}
|
||||
|
||||
if (modelIndex >= MAX_MODELS) {
|
||||
General.ErrorLogger.Add(ErrorType.Error, "Error in " + parser.Source + " at line " + parser.GetCurrentLineNumber() + ": GZDoom doesn't allow more than " + MAX_MODELS + " per MODELDEF entry!");
|
||||
General.ErrorLogger.Add(ErrorType.Error, "Error in " + parser.Source + " at line " + parser.GetCurrentLineNumber() + ": GZDoom doesn't allow more than " + MAX_MODELS + " models per MODELDEF entry!");
|
||||
gotErrors = true;
|
||||
break;
|
||||
}
|
||||
|
@ -314,9 +314,9 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom {
|
|||
ModelData mde = new ModelData();
|
||||
mde.Scale = scale;
|
||||
mde.zOffset = zOffset;
|
||||
mde.AngleOffset = Angle2D.DegToRad(angleOffset);// *(float)Math.PI / 180.0f;
|
||||
mde.RollOffset = Angle2D.DegToRad(rollOffset);// *(float)Math.PI / 180.0f;
|
||||
mde.PitchOffset = Angle2D.DegToRad(pitchOffset);// *(float)Math.PI / 180.0f;
|
||||
mde.AngleOffset = Angle2D.DegToRad(angleOffset);
|
||||
mde.RollOffset = Angle2D.DegToRad(rollOffset);
|
||||
mde.PitchOffset = Angle2D.DegToRad(pitchOffset);
|
||||
|
||||
for(int i = 0; i < modelNames.Length; i++) {
|
||||
if (!string.IsNullOrEmpty(modelNames[i])) {
|
||||
|
|
|
@ -41,13 +41,9 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
|
|||
|
||||
#endregion
|
||||
|
||||
#region ================== Load
|
||||
|
||||
public static void Load(ModelData mde, List<DataReader> containers, Device device) {
|
||||
mde.Model = new GZModel();
|
||||
BoundingBoxSizes bbs = new BoundingBoxSizes();
|
||||
MD3LoadResult result = new MD3LoadResult();
|
||||
#region ================== Init
|
||||
|
||||
internal static void Init() {
|
||||
if(vertexElements == null) {
|
||||
vertexElements = new[] {
|
||||
new VertexElement(0, 0, DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.Position, 0),
|
||||
|
@ -57,6 +53,61 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
|
|||
VertexElement.VertexDeclarationEnd
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Load
|
||||
|
||||
public static void Load(ModelData mde, List<DataReader> containers, Device device) {
|
||||
if(mde.IsVoxel) {
|
||||
loadKVX(mde, containers, device);
|
||||
} else {
|
||||
loadModel(mde, containers, device);
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadKVX(ModelData mde, List<DataReader> containers, Device device) {
|
||||
mde.Model = new GZModel();
|
||||
|
||||
//prepare WhiteTexture... just in case :)
|
||||
//if(General.Map.Data.WhiteTexture.Texture == null || General.Map.Data.WhiteTexture.Texture.Disposed)
|
||||
//General.Map.Data.WhiteTexture.CreateTexture();
|
||||
|
||||
for(int i = 0; i < mde.ModelNames.Count; i++) {
|
||||
//find the model
|
||||
Stream ms;
|
||||
|
||||
foreach(DataReader dr in containers) {
|
||||
ms = dr.GetVoxelData(mde.ModelNames[i]);
|
||||
if(ms == null) continue;
|
||||
|
||||
//load kvx
|
||||
ReadKVX(mde, ms, device);
|
||||
|
||||
//add texture
|
||||
//mde.Model.Textures.Add(General.Map.Data.WhiteTexture.Texture);
|
||||
|
||||
//done
|
||||
ms.Close();
|
||||
ms.Dispose();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//clear unneeded data
|
||||
mde.TextureNames = null;
|
||||
mde.ModelNames = null;
|
||||
|
||||
if(mde.Model.Meshes == null || mde.Model.Meshes.Count == 0) {
|
||||
mde.Model = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadModel(ModelData mde, List<DataReader> containers, Device device) {
|
||||
mde.Model = new GZModel();
|
||||
BoundingBoxSizes bbs = new BoundingBoxSizes();
|
||||
MD3LoadResult result = new MD3LoadResult();
|
||||
|
||||
//load models and textures
|
||||
for(int i = 0; i < mde.ModelNames.Count; i++) {
|
||||
|
@ -196,7 +247,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
|
|||
Dictionary<string, List<WorldVertex>> vertListsPerTexture = new Dictionary<string, List<WorldVertex>>();
|
||||
Dictionary<string, List<int>> vertexOffsets = new Dictionary<string, List<int>>();
|
||||
|
||||
string error = "";
|
||||
string error;
|
||||
for (int c = 0; c < numSurfaces; c++) {
|
||||
string skin = "";
|
||||
error = ReadSurface(ref bbs, ref skin, br, polyIndecesList, vertList, mde);
|
||||
|
@ -361,18 +412,19 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
|
|||
return "";
|
||||
}
|
||||
|
||||
private static void CreateMesh(Device device, ref MD3LoadResult result, List<WorldVertex> vertList, List<int> polyIndecesList) {
|
||||
private static void CreateMesh(Device device, ref MD3LoadResult result, List<WorldVertex> verts, List<int> indices) {
|
||||
//create mesh
|
||||
Mesh mesh = new Mesh(device, polyIndecesList.Count / 3, vertList.Count, MeshFlags.Use32Bit | MeshFlags.IndexBufferManaged | MeshFlags.VertexBufferManaged, vertexElements);
|
||||
Mesh mesh = new Mesh(device, indices.Count / 3, verts.Count, MeshFlags.Use32Bit | MeshFlags.IndexBufferManaged | MeshFlags.VertexBufferManaged, vertexElements);
|
||||
|
||||
using(DataStream stream = mesh.LockVertexBuffer(LockFlags.None)) {
|
||||
stream.WriteRange(vertList.ToArray());
|
||||
stream.WriteRange(verts.ToArray());
|
||||
}
|
||||
mesh.UnlockVertexBuffer();
|
||||
|
||||
using(DataStream stream = mesh.LockIndexBuffer(LockFlags.None)) {
|
||||
stream.WriteRange(polyIndecesList.ToArray());
|
||||
stream.WriteRange(indices.ToArray());
|
||||
}
|
||||
mesh.UnlockIndexBuffer();
|
||||
|
||||
mesh.OptimizeInPlace(MeshOptimizeFlags.AttributeSort);
|
||||
|
||||
|
@ -384,7 +436,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
|
|||
|
||||
#region ================== MD2
|
||||
|
||||
private static MD3LoadResult ReadMD2Model(ref BoundingBoxSizes bbs, ModelData mde, MemoryStream s, Device D3DDevice) {
|
||||
private static MD3LoadResult ReadMD2Model(ref BoundingBoxSizes bbs, ModelData mde, MemoryStream s, Device device) {
|
||||
long start = s.Position;
|
||||
MD3LoadResult result = new MD3LoadResult();
|
||||
|
||||
|
@ -397,7 +449,6 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
|
|||
|
||||
int modelVersion = br.ReadInt32();
|
||||
if(modelVersion != 8) { //MD2 version. Must be equal to 8
|
||||
//General.ErrorLogger.Add(ErrorType.Error, "Unable to load model '" + path + "': MD2 version must be 8 but is " + modelVersion);
|
||||
result.Errors = "MD2 version must be 8 but is " + modelVersion;
|
||||
return result;
|
||||
}
|
||||
|
@ -459,7 +510,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
|
|||
s.Position += 16; //frame name
|
||||
|
||||
//rotation angles
|
||||
float angle = mde.AngleOffset - Angle2D.PIHALF;// 0.5f * (float)Math.PI; //subtract 90 degrees to get correct rotation
|
||||
float angle = mde.AngleOffset - Angle2D.PIHALF; // subtract 90 degrees to get correct rotation
|
||||
float angleOfsetCos = (float)Math.Cos(angle);
|
||||
float angleOfsetSin = (float)Math.Sin(angle);
|
||||
float pitchOfsetCos = (float)Math.Cos(-mde.PitchOffset);
|
||||
|
@ -534,7 +585,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
|
|||
}
|
||||
|
||||
//mesh
|
||||
Mesh mesh = new Mesh(D3DDevice, polyIndecesList.Count / 3, vertList.Count, MeshFlags.Use32Bit | MeshFlags.IndexBufferManaged | MeshFlags.VertexBufferManaged, vertexElements);
|
||||
Mesh mesh = new Mesh(device, polyIndecesList.Count / 3, vertList.Count, MeshFlags.Use32Bit | MeshFlags.IndexBufferManaged | MeshFlags.VertexBufferManaged, vertexElements);
|
||||
|
||||
using (DataStream stream = mesh.LockVertexBuffer(LockFlags.None)) {
|
||||
stream.WriteRange(vertList.ToArray());
|
||||
|
@ -558,9 +609,269 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
|
|||
|
||||
#endregion
|
||||
|
||||
#region ================== KVX
|
||||
|
||||
private static void ReadKVX(ModelData mde, Stream stream, Device device) {
|
||||
PixelColor[] palette = new PixelColor[256];
|
||||
List<WorldVertex> verts = new List<WorldVertex>();
|
||||
int xsize, ysize, zsize;
|
||||
Vector3D pivot;
|
||||
|
||||
using(BinaryReader reader = new BinaryReader(stream, Encoding.ASCII)) {
|
||||
reader.ReadInt32(); //numbytes, we don't use that
|
||||
xsize = reader.ReadInt32();
|
||||
ysize = reader.ReadInt32();
|
||||
zsize = reader.ReadInt32();
|
||||
|
||||
pivot = new Vector3D();
|
||||
pivot.x = reader.ReadInt32() / 256f;
|
||||
pivot.y = reader.ReadInt32() / 256f;
|
||||
pivot.z = reader.ReadInt32() / 256f;
|
||||
|
||||
//read offsets
|
||||
int[] xoffset = new int[xsize + 1]; //why is it xsize + 1, not xsize?..
|
||||
short[,] xyoffset = new short[xsize, ysize + 1]; //why is it ysize + 1, not ysize?..
|
||||
|
||||
for(int i = 0; i < xoffset.Length; i++) {
|
||||
xoffset[i] = reader.ReadInt32();
|
||||
}
|
||||
|
||||
for(int x = 0; x < xsize; x++) {
|
||||
for(int y = 0; y < ysize + 1; y++) {
|
||||
xyoffset[x, y] = reader.ReadInt16();
|
||||
}
|
||||
}
|
||||
|
||||
//read slabs
|
||||
List<int> offsets = new List<int>(xsize * ysize);
|
||||
for(int x = 0; x < xsize; x++) {
|
||||
for(int y = 0; y < ysize; y++) {
|
||||
offsets.Add(xoffset[x] + xyoffset[x, y] + 28); //for some reason offsets are counted from start of xoffset[]...
|
||||
}
|
||||
}
|
||||
|
||||
int counter = 0;
|
||||
int slabsEnd = (int)(reader.BaseStream.Length - 768);
|
||||
|
||||
//read palette
|
||||
if(!mde.OverridePalette) {
|
||||
reader.BaseStream.Position = slabsEnd;
|
||||
for(int i = 0; i < 256; i++) {
|
||||
byte r = (byte)(reader.ReadByte() * 4);
|
||||
byte g = (byte)(reader.ReadByte() * 4);
|
||||
byte b = (byte)(reader.ReadByte() * 4);
|
||||
palette[i] = new PixelColor(255, r, g, b);
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < 256; i++ ) {
|
||||
palette[i] = General.Map.Data.Palette[i];
|
||||
}
|
||||
}
|
||||
|
||||
for(int x = 0; x < xsize; x++) {
|
||||
for(int y = 0; y < ysize; y++) {
|
||||
reader.BaseStream.Position = offsets[counter];
|
||||
int next = (counter < offsets.Count - 1 ? offsets[counter + 1] : slabsEnd);
|
||||
|
||||
//read slab
|
||||
while(reader.BaseStream.Position < next) {
|
||||
int ztop = reader.ReadByte();
|
||||
int zleng = reader.ReadByte();
|
||||
if(ztop + zleng > zsize) break;
|
||||
int flags = reader.ReadByte();
|
||||
|
||||
if(zleng > 0) {
|
||||
List<int> colorIndices = new List<int>(zleng);
|
||||
for(int i = 0; i < zleng; i++) {
|
||||
colorIndices.Add(reader.ReadByte());
|
||||
}
|
||||
|
||||
if((flags & 16) != 0) {
|
||||
AddFace(verts, new Vector3D(x, y, ztop), new Vector3D(x + 1, y, ztop), new Vector3D(x, y + 1, ztop), new Vector3D(x + 1, y + 1, ztop), pivot, colorIndices[0], mde.AngleOffset, mde.Scale.X);
|
||||
}
|
||||
|
||||
int z = ztop;
|
||||
int cstart = 0;
|
||||
while(z < ztop + zleng) {
|
||||
int c = 0;
|
||||
while(z + c < ztop + zleng && colorIndices[cstart + c] == colorIndices[cstart]) c++;
|
||||
|
||||
if((flags & 1) != 0) {
|
||||
AddFace(verts, new Vector3D(x, y, z), new Vector3D(x, y + 1, z), new Vector3D(x, y, z + c), new Vector3D(x, y + 1, z + c), pivot, colorIndices[cstart], mde.AngleOffset, mde.Scale.X);
|
||||
}
|
||||
if((flags & 2) != 0) {
|
||||
AddFace(verts, new Vector3D(x + 1, y + 1, z), new Vector3D(x + 1, y, z), new Vector3D(x + 1, y + 1, z + c), new Vector3D(x + 1, y, z + c), pivot, colorIndices[cstart], mde.AngleOffset, mde.Scale.X);
|
||||
}
|
||||
if((flags & 4) != 0) {
|
||||
AddFace(verts, new Vector3D(x + 1, y, z), new Vector3D(x, y, z), new Vector3D(x + 1, y, z + c), new Vector3D(x, y, z + c), pivot, colorIndices[cstart], mde.AngleOffset, mde.Scale.X);
|
||||
}
|
||||
if((flags & 8) != 0) {
|
||||
AddFace(verts, new Vector3D(x, y + 1, z), new Vector3D(x + 1, y + 1, z), new Vector3D(x, y + 1, z + c), new Vector3D(x + 1, y + 1, z + c), pivot, colorIndices[cstart], mde.AngleOffset, mde.Scale.X);
|
||||
}
|
||||
|
||||
if(c == 0) c++;
|
||||
z += c;
|
||||
cstart += c;
|
||||
}
|
||||
|
||||
if((flags & 32) != 0) {
|
||||
z = ztop + zleng - 1;
|
||||
AddFace(verts, new Vector3D(x + 1, y, z + 1), new Vector3D(x, y, z + 1), new Vector3D(x + 1, y + 1, z + 1), new Vector3D(x, y + 1, z + 1), pivot, colorIndices[zleng - 1], mde.AngleOffset, mde.Scale.X);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//create bounding box
|
||||
BoundingBoxSizes bbs = new BoundingBoxSizes();
|
||||
bbs.MinX = (short)((xsize / 2 - pivot.x) * mde.Scale.X);
|
||||
bbs.MaxX = (short)((xsize / 2 + pivot.x) * mde.Scale.X);
|
||||
bbs.MinZ = (short)((zsize / 2 - pivot.z) * mde.Scale.X);
|
||||
bbs.MaxZ = (short)((zsize / 2 + pivot.z) * mde.Scale.X);
|
||||
bbs.MinY = (short)((ysize / 2 - pivot.y) * mde.Scale.X);
|
||||
bbs.MaxY = (short)((ysize / 2 + pivot.y) * mde.Scale.X);
|
||||
|
||||
mde.Model.BoundingBox = BoundingBoxTools.CalculateBoundingBox(bbs);
|
||||
|
||||
//create bitmap
|
||||
Bitmap bmp = createVoxelTexture(palette);
|
||||
|
||||
//create texture
|
||||
MemoryStream memstream = new MemoryStream((4096 * 4) + 4096);
|
||||
bmp.Save(memstream, ImageFormat.Bmp);
|
||||
memstream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
Texture texture = Texture.FromStream(device, memstream, (int)memstream.Length, 64, 64, 0, Usage.None, Format.Unknown, Pool.Managed, Filter.Point, Filter.Box, 0);
|
||||
memstream.Dispose();
|
||||
|
||||
//add texture
|
||||
mde.Model.Textures.Add(texture);
|
||||
|
||||
//create mesh
|
||||
int[] indices = new int[verts.Count];
|
||||
for(int i = 0; i < verts.Count; i++) {
|
||||
indices[i] = i;
|
||||
}
|
||||
|
||||
Mesh mesh = new Mesh(device, verts.Count / 3, verts.Count, MeshFlags.Use32Bit | MeshFlags.IndexBufferManaged | MeshFlags.VertexBufferManaged, vertexElements);
|
||||
|
||||
DataStream mstream = mesh.VertexBuffer.Lock(0, 0, LockFlags.None);
|
||||
mstream.WriteRange(verts.ToArray());
|
||||
mesh.VertexBuffer.Unlock();
|
||||
|
||||
mstream = mesh.IndexBuffer.Lock(0, 0, LockFlags.None);
|
||||
mstream.WriteRange(indices);
|
||||
mesh.IndexBuffer.Unlock();
|
||||
|
||||
mesh.OptimizeInPlace(MeshOptimizeFlags.AttributeSort);
|
||||
|
||||
//add mesh
|
||||
mde.Model.Meshes.Add(mesh);
|
||||
}
|
||||
|
||||
// Shameless GZDoom rip-off
|
||||
private static void AddFace(List<WorldVertex> verts, Vector3D v1, Vector3D v2, Vector3D v3, Vector3D v4, Vector3D pivot, int colorIndex, float angle, float scale) {
|
||||
float pu0 = (colorIndex % 16) / 16f;
|
||||
float pu1 = pu0 + 0.0001f;
|
||||
float pv0 = (colorIndex / 16) / 16f;
|
||||
float pv1 = pv0 + 0.0001f;
|
||||
|
||||
WorldVertex wv1 = new WorldVertex();
|
||||
wv1.x = v1.x - pivot.x;
|
||||
wv1.y = -v1.y + pivot.y;
|
||||
wv1.z = -v1.z + pivot.z;
|
||||
wv1.u = pu0;
|
||||
wv1.v = pv0;
|
||||
wv1 = TransformVertex(wv1, angle, scale);
|
||||
verts.Add(wv1);
|
||||
|
||||
WorldVertex wv2 = new WorldVertex();
|
||||
wv2.x = v2.x - pivot.x;
|
||||
wv2.y = -v2.y + pivot.y;
|
||||
wv2.z = -v2.z + pivot.z;
|
||||
wv2.u = pu1;
|
||||
wv2.v = pv1;
|
||||
wv2 = TransformVertex(wv2, angle, scale);
|
||||
verts.Add(wv2);
|
||||
|
||||
WorldVertex wv4 = new WorldVertex();
|
||||
wv4.x = v4.x - pivot.x;
|
||||
wv4.y = -v4.y + pivot.y;
|
||||
wv4.z = -v4.z + pivot.z;
|
||||
wv4.u = pu0;
|
||||
wv4.v = pv0;
|
||||
wv4 = TransformVertex(wv4, angle, scale);
|
||||
verts.Add(wv4);
|
||||
|
||||
WorldVertex wv3 = new WorldVertex();
|
||||
wv3.x = v3.x - pivot.x;
|
||||
wv3.y = -v3.y + pivot.y;
|
||||
wv3.z = -v3.z + pivot.z;
|
||||
wv3.u = pu1;
|
||||
wv3.v = pv1;
|
||||
wv3 = TransformVertex(wv3, angle, scale);
|
||||
verts.Add(wv3);
|
||||
|
||||
verts.Add(wv1);
|
||||
verts.Add(wv4);
|
||||
}
|
||||
|
||||
private static WorldVertex TransformVertex(WorldVertex v, float angle, float scale) {
|
||||
if (angle != 0) {
|
||||
float angleOfsetCos = (float) Math.Cos(angle);
|
||||
float angleOfsetSin = (float) Math.Sin(angle);
|
||||
|
||||
float rx1 = angleOfsetCos * v.x - angleOfsetSin * v.y;
|
||||
float ry1 = angleOfsetSin * v.x + angleOfsetCos * v.y;
|
||||
v.y = ry1;
|
||||
v.x = rx1;
|
||||
}
|
||||
|
||||
if (scale != 1.0f) {
|
||||
v.x *= scale;
|
||||
v.y *= scale;
|
||||
v.z *= scale;
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
private unsafe static Bitmap createVoxelTexture(PixelColor[] palette) {
|
||||
Bitmap bmp = new Bitmap(16, 16);
|
||||
BitmapData bmpdata = bmp.LockBits(new Rectangle(0, 0, 16, 16), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
|
||||
|
||||
if(bmpdata != null) {
|
||||
PixelColor* pixels = (PixelColor*)(bmpdata.Scan0.ToPointer());
|
||||
const int numpixels = 256;
|
||||
int i = 255;
|
||||
|
||||
for(PixelColor* cp = pixels + numpixels - 1; cp >= pixels; cp--, i--) {
|
||||
cp->r = palette[i].r;
|
||||
cp->g = palette[i].g;
|
||||
cp->b = palette[i].b;
|
||||
cp->a = palette[i].a;
|
||||
}
|
||||
bmp.UnlockBits(bmpdata);
|
||||
}
|
||||
|
||||
//scale bitmap, so colors stay (almost) the same when bilinear filtering is enabled
|
||||
Bitmap scaled = new Bitmap(64, 64);
|
||||
using(Graphics gs = Graphics.FromImage(scaled)) {
|
||||
gs.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
|
||||
gs.DrawImage(bmp, new Rectangle(0, 0, 64, 64), new Rectangle(0, 0, 16, 16), GraphicsUnit.Pixel);
|
||||
}
|
||||
|
||||
return scaled;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Utility
|
||||
|
||||
//util
|
||||
private static MemoryStream LoadFile(List<DataReader> containers, string path, bool isModel) {
|
||||
foreach(DataReader dr in containers) {
|
||||
if(isModel && dr is WADReader) continue; //models cannot be stored in WADs
|
||||
|
|
|
@ -375,15 +375,15 @@ namespace CodeImp.DoomBuilder.VisualModes
|
|||
n = 0;
|
||||
lightRenderStyle = DynamicLightRenderStyle.NORMAL;
|
||||
//lightColor.Alpha used in shader to perform some calculations based on light type
|
||||
lightColor = new Color4((float)lightRenderStyle / 100.0f, (float)thing.Args[0] / scaled_intensity, (float)thing.Args[1] / scaled_intensity, (float)thing.Args[2] / scaled_intensity);
|
||||
lightColor = new Color4((float)lightRenderStyle / 100.0f, thing.Args[0] / scaled_intensity, thing.Args[1] / scaled_intensity, thing.Args[2] / scaled_intensity);
|
||||
} else if (light_id < GZBuilder.GZGeneral.GZ_LIGHT_TYPES[1]) {
|
||||
n = 10;
|
||||
lightRenderStyle = DynamicLightRenderStyle.ADDITIVE;
|
||||
lightColor = new Color4((float)lightRenderStyle / 100.0f, (float)thing.Args[0] / scaled_intensity, (float)thing.Args[1] / scaled_intensity, (float)thing.Args[2] / scaled_intensity);
|
||||
lightColor = new Color4((float)lightRenderStyle / 100.0f, thing.Args[0] / scaled_intensity, thing.Args[1] / scaled_intensity, thing.Args[2] / scaled_intensity);
|
||||
} else {
|
||||
n = 20;
|
||||
lightRenderStyle = DynamicLightRenderStyle.NEGATIVE;
|
||||
lightColor = new Color4((float)lightRenderStyle / 100.0f, (float)thing.Args[0] / scaled_intensity, (float)thing.Args[1] / scaled_intensity, (float)thing.Args[2] / scaled_intensity);
|
||||
lightColor = new Color4((float)lightRenderStyle / 100.0f, thing.Args[0] / scaled_intensity, thing.Args[1] / scaled_intensity, thing.Args[2] / scaled_intensity);
|
||||
}
|
||||
lightType = (DynamicLightType)(thing.Type - 9800 - n);
|
||||
|
||||
|
@ -391,20 +391,20 @@ namespace CodeImp.DoomBuilder.VisualModes
|
|||
int scaler = 1;
|
||||
if (thing.Sector != null)
|
||||
scaler = thing.Sector.Brightness / 4;
|
||||
lightPrimaryRadius = (float)(thing.Args[3] * scaler) * General.Settings.GZDynamicLightRadius;
|
||||
lightPrimaryRadius = (thing.Args[3] * scaler) * General.Settings.GZDynamicLightRadius;
|
||||
} else {
|
||||
lightPrimaryRadius = (float)(thing.Args[3] * 2) * General.Settings.GZDynamicLightRadius; //works... that.. way in GZDoom
|
||||
lightPrimaryRadius = (thing.Args[3] * 2) * General.Settings.GZDynamicLightRadius; //works... that.. way in GZDoom
|
||||
if (lightType > 0)
|
||||
lightSecondaryRadius = (float)(thing.Args[4] * 2) * General.Settings.GZDynamicLightRadius;
|
||||
lightSecondaryRadius = (thing.Args[4] * 2) * General.Settings.GZDynamicLightRadius;
|
||||
}
|
||||
} else { //it's one of vavoom lights
|
||||
lightRenderStyle = DynamicLightRenderStyle.VAVOOM;
|
||||
lightType = (DynamicLightType)thing.Type;
|
||||
if (lightType == DynamicLightType.VAVOOM_COLORED)
|
||||
lightColor = new Color4((float)lightRenderStyle / 100.0f, (float)thing.Args[1] / scaled_intensity, (float)thing.Args[2] / scaled_intensity, (float)thing.Args[3] / scaled_intensity);
|
||||
lightColor = new Color4((float)lightRenderStyle / 100.0f, thing.Args[1] / scaled_intensity, thing.Args[2] / scaled_intensity, thing.Args[3] / scaled_intensity);
|
||||
else
|
||||
lightColor = new Color4((float)lightRenderStyle / 100.0f, General.Settings.GZDynamicLightIntensity, General.Settings.GZDynamicLightIntensity, General.Settings.GZDynamicLightIntensity);
|
||||
lightPrimaryRadius = (float)(thing.Args[0] * 8) * General.Settings.GZDynamicLightRadius;
|
||||
lightPrimaryRadius = (thing.Args[0] * 8) * General.Settings.GZDynamicLightRadius;
|
||||
}
|
||||
UpdateLightRadius();
|
||||
}
|
||||
|
@ -429,7 +429,6 @@ namespace CodeImp.DoomBuilder.VisualModes
|
|||
}
|
||||
|
||||
lightInterval = light.Interval;
|
||||
|
||||
updateLightRadius(lightInterval);
|
||||
}
|
||||
|
||||
|
@ -474,7 +473,7 @@ namespace CodeImp.DoomBuilder.VisualModes
|
|||
} else if (lightType == DynamicLightType.RANDOM) {
|
||||
float delta = (float)Math.Sin(time / (interval * 9.0f)); //just playing by the eye here...
|
||||
if (Math.Sign(delta) != Math.Sign(lightDelta))
|
||||
lightRadius = rMin + (float)(new Random().Next(0, (int)(diff * 10))) / 10.0f;
|
||||
lightRadius = rMin + (new Random().Next(0, (int)(diff * 10))) / 10.0f;
|
||||
lightDelta = delta;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,8 +28,9 @@ namespace CodeImp.DoomBuilder.ZDoom
|
|||
{
|
||||
#region ================== Constants
|
||||
|
||||
private readonly string[] SPRITE_POSTFIXES = new string[] {"2C8", "2D8", "2A8", "2B8", "1C1", "1D1", "1A1", "1B1", "A2", "A1", "A0", "2", "1", "0" };
|
||||
|
||||
private readonly string[] SPRITE_POSTFIXES = new[] {"2C8", "2D8", "2A8", "2B8", "1C1", "1D1", "1A1", "1B1", "A2", "A1", "A0", "2", "1", "0" };
|
||||
internal const string ACTOR_CLASS_SPECIAL_TOKENS = ":{}\n;,"; //mxd
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Variables
|
||||
|
@ -91,7 +92,8 @@ namespace CodeImp.DoomBuilder.ZDoom
|
|||
|
||||
// First next token is the class name
|
||||
parser.SkipWhitespace(true);
|
||||
classname = parser.StripTokenQuotes(parser.ReadToken());
|
||||
classname = parser.StripTokenQuotes(parser.ReadToken(ACTOR_CLASS_SPECIAL_TOKENS));
|
||||
|
||||
if(string.IsNullOrEmpty(classname))
|
||||
{
|
||||
parser.ReportError("Expected actor class name");
|
||||
|
@ -549,7 +551,7 @@ namespace CodeImp.DoomBuilder.ZDoom
|
|||
/// </summary>
|
||||
public string FindSuitableSprite()
|
||||
{
|
||||
string result = "";
|
||||
string result = string.Empty;
|
||||
|
||||
// Sprite forced?
|
||||
if(HasPropertyWithValue("$sprite"))
|
||||
|
@ -622,6 +624,69 @@ namespace CodeImp.DoomBuilder.ZDoom
|
|||
}
|
||||
|
||||
// No sprite found
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
//mxd.
|
||||
///TODO: rewrite this
|
||||
public string FindSuitableVoxel(Dictionary<string, bool> voxels) {
|
||||
string result = string.Empty;
|
||||
|
||||
// Try the idle state
|
||||
if(HasState("idle")) {
|
||||
StateStructure s = GetState("idle");
|
||||
string spritename = s.GetSprite(0);
|
||||
if(!string.IsNullOrEmpty(spritename))
|
||||
result = spritename;
|
||||
}
|
||||
|
||||
// Try the see state
|
||||
if(string.IsNullOrEmpty(result) && HasState("see")) {
|
||||
StateStructure s = GetState("see");
|
||||
string spritename = s.GetSprite(0);
|
||||
if(!string.IsNullOrEmpty(spritename))
|
||||
result = spritename;
|
||||
}
|
||||
|
||||
// Try the inactive state
|
||||
if(string.IsNullOrEmpty(result) && HasState("inactive")) {
|
||||
StateStructure s = GetState("inactive");
|
||||
string spritename = s.GetSprite(0);
|
||||
if(!string.IsNullOrEmpty(spritename))
|
||||
result = spritename;
|
||||
}
|
||||
|
||||
// Try the spawn state
|
||||
if(string.IsNullOrEmpty(result) && HasState("spawn")) {
|
||||
StateStructure s = GetState("spawn");
|
||||
string spritename = s.GetSprite(0);
|
||||
if(!string.IsNullOrEmpty(spritename))
|
||||
result = spritename;
|
||||
}
|
||||
|
||||
// Still no sprite found? then just pick the first we can find
|
||||
if(string.IsNullOrEmpty(result)) {
|
||||
Dictionary<string, StateStructure> list = GetAllStates();
|
||||
foreach(StateStructure s in list.Values) {
|
||||
string spritename = s.GetSprite(0);
|
||||
if(!string.IsNullOrEmpty(spritename)) {
|
||||
result = spritename;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!string.IsNullOrEmpty(result)) {
|
||||
if (voxels.ContainsKey(result)) return result;
|
||||
|
||||
// The sprite name may be incomplete. Find an existing sprite with direction.
|
||||
foreach(string postfix in SPRITE_POSTFIXES) {
|
||||
if(voxels.ContainsKey(result + postfix)) return result + postfix;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// No voxel found
|
||||
return "";
|
||||
}
|
||||
|
||||
|
|
133
Source/Core/ZDoom/VoxeldefParser.cs
Normal file
133
Source/Core/ZDoom/VoxeldefParser.cs
Normal file
|
@ -0,0 +1,133 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using CodeImp.DoomBuilder.Geometry;
|
||||
using CodeImp.DoomBuilder.GZBuilder.Data;
|
||||
using SlimDX;
|
||||
|
||||
namespace CodeImp.DoomBuilder.ZDoom
|
||||
{
|
||||
public sealed class VoxeldefParser : ZDTextParser
|
||||
{
|
||||
private Dictionary<string, ModelData> entries; //sprite name, entry
|
||||
internal Dictionary<string, ModelData> Entries { get { return entries; } }
|
||||
|
||||
public override bool Parse(Stream stream, string sourcefilename) {
|
||||
base.Parse(stream, sourcefilename);
|
||||
entries = new Dictionary<string, ModelData>();
|
||||
string prevToken = string.Empty;
|
||||
|
||||
List<string> spriteNames = new List<string>();
|
||||
string modelName = string.Empty;
|
||||
|
||||
// Continue until at the end of the stream
|
||||
while(SkipWhitespace(true)) {
|
||||
string token = ReadToken();
|
||||
|
||||
if(token != null) {
|
||||
token = StripTokenQuotes(token).ToLowerInvariant();
|
||||
|
||||
if(token == ",") { //previous token was a sprite name
|
||||
if(!string.IsNullOrEmpty(prevToken)) {
|
||||
//if(prevToken.Length > 4) prevToken = prevToken.Substring(0, 4);
|
||||
if(!spriteNames.Contains(prevToken)) spriteNames.Add(prevToken);
|
||||
}
|
||||
prevToken = token.ToUpperInvariant();
|
||||
|
||||
} else if(token == "=") { //next token should be a voxel model name
|
||||
if(!string.IsNullOrEmpty(prevToken)) {
|
||||
//if(prevToken.Length > 4) prevToken = prevToken.Substring(0, 4);
|
||||
if(!spriteNames.Contains(prevToken)) spriteNames.Add(prevToken);
|
||||
}
|
||||
|
||||
SkipWhitespace(true);
|
||||
token = ReadToken();
|
||||
|
||||
if(string.IsNullOrEmpty(token)) {
|
||||
General.ErrorLogger.Add(ErrorType.Error, "Unable to get voxel model name from '" + sourcefilename + "', line " + GetCurrentLineNumber());
|
||||
spriteNames.Clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
modelName = StripTokenQuotes(token).ToLowerInvariant();
|
||||
|
||||
//read the settings
|
||||
} else if(token == "{") {
|
||||
ModelData data = new ModelData();
|
||||
data.IsVoxel = true;
|
||||
|
||||
while(SkipWhitespace(true)) {
|
||||
token = ReadToken();
|
||||
|
||||
if(!string.IsNullOrEmpty(token)) {
|
||||
token = StripTokenQuotes(token).ToLowerInvariant();
|
||||
|
||||
if(token == "}") { //store data
|
||||
if(!string.IsNullOrEmpty(modelName) && spriteNames.Count > 0) {
|
||||
data.ModelNames.Add(modelName);
|
||||
|
||||
foreach(string s in spriteNames){
|
||||
if(entries.ContainsKey(s)) { //TODO: is this a proper behaviour?
|
||||
entries[s] = data;
|
||||
} else {
|
||||
entries.Add(s, data);
|
||||
}
|
||||
}
|
||||
|
||||
//reset local data
|
||||
modelName = string.Empty;
|
||||
prevToken = string.Empty;
|
||||
spriteNames.Clear();
|
||||
}
|
||||
|
||||
break;
|
||||
} else if(token == "overridepalette") {
|
||||
data.OverridePalette = true;
|
||||
|
||||
} else if(token == "angleoffset") {
|
||||
SkipWhitespace(true);
|
||||
|
||||
token = StripTokenQuotes(ReadToken());
|
||||
if(token != "=") {
|
||||
General.ErrorLogger.Add(ErrorType.Error, "Error in " + sourcefilename + " at line " + GetCurrentLineNumber() + ": expected '=', but got '" + token + "'");
|
||||
break;
|
||||
}
|
||||
|
||||
float angleOffset = 0; //90?
|
||||
token = StripTokenQuotes(ReadToken());
|
||||
if(!ReadSignedFloat(token, ref angleOffset)) {
|
||||
// Not numeric!
|
||||
General.ErrorLogger.Add(ErrorType.Error, "Error in " + sourcefilename + " at line " + GetCurrentLineNumber() + ": expected AngleOffset value, but got '" + token + "'");
|
||||
}
|
||||
data.AngleOffset = Angle2D.DegToRad(angleOffset);
|
||||
|
||||
} else if(token == "scale") {
|
||||
SkipWhitespace(true);
|
||||
|
||||
token = StripTokenQuotes(ReadToken());
|
||||
if(token != "=") {
|
||||
General.ErrorLogger.Add(ErrorType.Error, "Error in " + sourcefilename + " at line " + GetCurrentLineNumber() + ": expected '=', but got '" + token + "'");
|
||||
break;
|
||||
}
|
||||
|
||||
float scale = 1.0f;
|
||||
token = StripTokenQuotes(ReadToken());
|
||||
if(!ReadSignedFloat(token, ref scale)) {
|
||||
// Not numeric!
|
||||
General.ErrorLogger.Add(ErrorType.Error, "Error in " + sourcefilename + " at line " + GetCurrentLineNumber() + ": expected Scale value, but got '" + token + "'");
|
||||
}
|
||||
|
||||
data.Scale = new Vector3(scale, scale, scale);
|
||||
}
|
||||
prevToken = token.ToUpperInvariant();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
prevToken = token.ToUpperInvariant();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return entries.Count > 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -98,10 +98,8 @@ namespace CodeImp.DoomBuilder.ZDoom
|
|||
// This returns true if the given character is a special token
|
||||
protected internal bool IsSpecialToken(string s)
|
||||
{
|
||||
if(s.Length > 0)
|
||||
return (specialtokens.IndexOf(s[0]) > -1);
|
||||
else
|
||||
return false;
|
||||
if(s.Length > 0) return (specialtokens.IndexOf(s[0]) > -1);
|
||||
return false;
|
||||
}
|
||||
|
||||
// This removes beginning and ending quotes from a token
|
||||
|
@ -184,12 +182,12 @@ namespace CodeImp.DoomBuilder.ZDoom
|
|||
// Returns null when the end of the stream has been reached
|
||||
protected internal string ReadToken()
|
||||
{
|
||||
string token = "";
|
||||
bool quotedstring = false;
|
||||
|
||||
// Return null when the end of the stream has been reached
|
||||
if(datastream.Position == datastream.Length) return null;
|
||||
|
||||
string token = "";
|
||||
bool quotedstring = false;
|
||||
|
||||
// Start reading
|
||||
char c = (char)datareader.ReadByte();
|
||||
while(!IsWhitespace(c) || quotedstring || IsSpecialToken(c))
|
||||
|
@ -262,6 +260,73 @@ namespace CodeImp.DoomBuilder.ZDoom
|
|||
return token;
|
||||
}
|
||||
|
||||
// This reads a token (all sequential non-whitespace characters or a single character) using custom set of special tokens
|
||||
// Returns null when the end of the stream has been reached (mxd)
|
||||
protected internal string ReadToken(string specialTokens) {
|
||||
// Return null when the end of the stream has been reached
|
||||
if(datastream.Position == datastream.Length) return null;
|
||||
|
||||
string token = "";
|
||||
bool quotedstring = false;
|
||||
|
||||
// Start reading
|
||||
char c = (char)datareader.ReadByte();
|
||||
while(!IsWhitespace(c) || quotedstring || specialTokens.IndexOf(c) != -1) {
|
||||
// Special token?
|
||||
if(!quotedstring && specialTokens.IndexOf(c) != -1) {
|
||||
// Not reading a token yet?
|
||||
if(token.Length == 0) {
|
||||
// This is our whole token
|
||||
token += c;
|
||||
break;
|
||||
}
|
||||
|
||||
// This is a new token and shouldn't be read now
|
||||
// Go one character back so we can read this token again
|
||||
datastream.Seek(-1, SeekOrigin.Current);
|
||||
break;
|
||||
} else {
|
||||
// Quote?
|
||||
if(c == '"') {
|
||||
// Quote to end the string?
|
||||
if(quotedstring) quotedstring = false;
|
||||
|
||||
// First character is a quote?
|
||||
if(token.Length == 0) quotedstring = true;
|
||||
|
||||
token += c;
|
||||
}
|
||||
// Potential comment?
|
||||
else if((c == '/') && !quotedstring) {
|
||||
// Check the next byte
|
||||
if(datastream.Position == datastream.Length) return token;
|
||||
char c2 = (char)datareader.ReadByte();
|
||||
if((c2 == '/') || (c2 == '*')) {
|
||||
// This is a comment start, so the token ends here
|
||||
// Go two characters back so we can read this comment again
|
||||
datastream.Seek(-2, SeekOrigin.Current);
|
||||
break;
|
||||
} else {
|
||||
// Not a comment
|
||||
// Go one character back so we can read this char again
|
||||
datastream.Seek(-1, SeekOrigin.Current);
|
||||
token += c;
|
||||
}
|
||||
} else {
|
||||
token += c;
|
||||
}
|
||||
}
|
||||
|
||||
// Next character
|
||||
if(datastream.Position < datastream.Length)
|
||||
c = (char)datareader.Read();
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
// This reads the rest of the line
|
||||
// Returns null when the end of the stream has been reached
|
||||
protected internal string ReadLine()
|
||||
|
|
|
@ -1097,6 +1097,18 @@ lookthroughthing
|
|||
default = 89; //Y
|
||||
}
|
||||
|
||||
//mxd
|
||||
matchbrightness
|
||||
{
|
||||
title = "Match Brightness";
|
||||
category = "visual";
|
||||
description = "Makes the brightness of selected surfaces the same as the brightness of highlighted surface (UDMF only).";
|
||||
allowkeys = true;
|
||||
allowmouse = false;
|
||||
allowscroll = false;
|
||||
default = 131149; //Ctrl-M
|
||||
}
|
||||
|
||||
//mxd. moved from GZDoomEditing plugin
|
||||
gzdbvisualmode
|
||||
{
|
||||
|
|
|
@ -1295,14 +1295,10 @@ namespace CodeImp.DoomBuilder.BuilderModes
|
|||
// Reset changed flags
|
||||
foreach(KeyValuePair<Sector, VisualSector> vs in allsectors) {
|
||||
BaseVisualSector bvs = (vs.Value as BaseVisualSector);
|
||||
foreach(VisualFloor vf in bvs.ExtraFloors)
|
||||
vf.Changed = false;
|
||||
foreach(VisualCeiling vc in bvs.ExtraCeilings)
|
||||
vc.Changed = false;
|
||||
foreach(VisualFloor vf in bvs.ExtraBackFloors)
|
||||
vf.Changed = false;
|
||||
foreach(VisualCeiling vc in bvs.ExtraBackCeilings)
|
||||
vc.Changed = false;
|
||||
foreach(VisualFloor vf in bvs.ExtraFloors) vf.Changed = false;
|
||||
foreach(VisualCeiling vc in bvs.ExtraCeilings) vc.Changed = false;
|
||||
foreach(VisualFloor vf in bvs.ExtraBackFloors) vf.Changed = false;
|
||||
foreach(VisualCeiling vc in bvs.ExtraBackCeilings) vc.Changed = false;
|
||||
bvs.Floor.Changed = false;
|
||||
bvs.Ceiling.Changed = false;
|
||||
}
|
||||
|
@ -2109,6 +2105,122 @@ namespace CodeImp.DoomBuilder.BuilderModes
|
|||
PostAction();
|
||||
}
|
||||
|
||||
//mxd
|
||||
[BeginAction("matchbrightness")]
|
||||
public void MatchBrightness() {
|
||||
//check input
|
||||
if (!General.Map.UDMF) {
|
||||
General.Interface.DisplayStatus(StatusType.Warning, "'Match Brightness' action works only in UDMF map format!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedobjects.Count == 0) {
|
||||
General.Interface.DisplayStatus(StatusType.Warning, "'Match Brightness' action requires a selection!");
|
||||
return;
|
||||
}
|
||||
|
||||
IVisualEventReceiver highlighted = (target.picked as IVisualEventReceiver);
|
||||
|
||||
if(highlighted is BaseVisualThing) {
|
||||
General.Interface.DisplayStatus(StatusType.Warning, "Highlight a surface, to which you want to match the brightness.");
|
||||
return;
|
||||
}
|
||||
|
||||
//get target brightness
|
||||
int targetBrightness = int.MinValue;
|
||||
if(highlighted is VisualFloor) {
|
||||
VisualFloor v = highlighted as VisualFloor;
|
||||
targetBrightness = v.Level.sector.Fields.GetValue("lightfloor", 0);
|
||||
if (!v.Level.sector.Fields.GetValue("lightfloorabsolute", false)) {
|
||||
targetBrightness += v.Level.sector.Brightness;
|
||||
}
|
||||
} else if(highlighted is VisualCeiling) {
|
||||
VisualCeiling v = highlighted as VisualCeiling;
|
||||
targetBrightness = v.Level.sector.Fields.GetValue("lightceiling", 0);
|
||||
if(!v.Level.sector.Fields.GetValue("lightceilingabsolute", false)) {
|
||||
targetBrightness += v.Level.sector.Brightness;
|
||||
}
|
||||
} else if(highlighted is VisualUpper || highlighted is VisualMiddleSingle || highlighted is VisualMiddleDouble || highlighted is VisualLower) {
|
||||
BaseVisualGeometrySidedef v = highlighted as BaseVisualGeometrySidedef;
|
||||
targetBrightness = v.Sidedef.Fields.GetValue("light", 0);
|
||||
if(!v.Sidedef.Fields.GetValue("lightabsolute", false)) {
|
||||
targetBrightness += v.Sidedef.Sector.Brightness;
|
||||
}
|
||||
} else if(highlighted is VisualMiddle3D) {
|
||||
VisualMiddle3D v = highlighted as VisualMiddle3D;
|
||||
Sidedef sd = v.GetControlLinedef().Front;
|
||||
|
||||
if (sd == null) {
|
||||
General.Interface.DisplayStatus(StatusType.Warning, "Highlight a surface, to which you want to match the brightness.");
|
||||
return;
|
||||
}
|
||||
targetBrightness = sd.Fields.GetValue("light", 0);
|
||||
if(!sd.Fields.GetValue("lightabsolute", false)) {
|
||||
targetBrightness += sd.Sector.Brightness;
|
||||
}
|
||||
|
||||
} else {
|
||||
General.Interface.DisplayStatus(StatusType.Warning, "Highlight a surface, to which you want to match the brightness.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetBrightness == int.MinValue) {
|
||||
General.Interface.DisplayStatus(StatusType.Warning, "Highlight a surface, to which you want to match the brightness.");
|
||||
return;
|
||||
}
|
||||
|
||||
//make undo
|
||||
CreateUndo("Match Brightness");
|
||||
targetBrightness = General.Clamp(targetBrightness, 0, 255);
|
||||
|
||||
//apply new brightness
|
||||
foreach (IVisualEventReceiver obj in selectedobjects) {
|
||||
if(obj == highlighted) continue;
|
||||
|
||||
if(obj is VisualFloor) {
|
||||
VisualFloor v = obj as VisualFloor;
|
||||
v.Level.sector.Fields.BeforeFieldsChange();
|
||||
v.Sector.Changed = true;
|
||||
|
||||
if (v.Level.sector.Fields.GetValue("lightfloorabsolute", false)) {
|
||||
v.Level.sector.Fields["lightfloor"] = new UniValue(UniversalType.Integer, targetBrightness);
|
||||
} else {
|
||||
v.Level.sector.Fields["lightfloor"] = new UniValue(UniversalType.Integer, targetBrightness - v.Level.sector.Brightness);
|
||||
}
|
||||
|
||||
v.Sector.UpdateSectorGeometry(false);
|
||||
|
||||
} else if(obj is VisualCeiling) {
|
||||
VisualCeiling v = obj as VisualCeiling;
|
||||
v.Level.sector.Fields.BeforeFieldsChange();
|
||||
v.Sector.Changed = true;
|
||||
|
||||
if(v.Level.sector.Fields.GetValue("lightceilingabsolute", false)) {
|
||||
v.Level.sector.Fields["lightceiling"] = new UniValue(UniversalType.Integer, targetBrightness);
|
||||
} else {
|
||||
v.Level.sector.Fields["lightceiling"] = new UniValue(UniversalType.Integer, targetBrightness - v.Level.sector.Brightness);
|
||||
}
|
||||
|
||||
v.Sector.UpdateSectorGeometry(false);
|
||||
|
||||
} else if(obj is VisualUpper || obj is VisualMiddleSingle || obj is VisualMiddleDouble || obj is VisualLower) {
|
||||
BaseVisualGeometrySidedef v = obj as BaseVisualGeometrySidedef;
|
||||
v.Sidedef.Fields.BeforeFieldsChange();
|
||||
v.Sector.Changed = true;
|
||||
|
||||
if (v.Sidedef.Fields.GetValue("lightabsolute", false)) {
|
||||
v.Sidedef.Fields["light"] = new UniValue(UniversalType.Integer, targetBrightness);
|
||||
} else {
|
||||
v.Sidedef.Fields["light"] = new UniValue(UniversalType.Integer, targetBrightness - v.Sidedef.Sector.Brightness);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//done
|
||||
General.Interface.DisplayStatus(StatusType.Action, "Matched brightness for " + selectedobjects.Count + " surfaces.");
|
||||
Interface_OnSectorEditFormValuesChanged(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
[BeginAction("showvisualthings")]
|
||||
public void ShowVisualThings()
|
||||
{
|
||||
|
|
|
@ -94,7 +94,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
|
|||
// This builds the thing geometry. Returns false when nothing was created.
|
||||
public virtual bool Setup()
|
||||
{
|
||||
PixelColor sectorcolor = new PixelColor(255, 255, 255, 255);
|
||||
int sectorcolor = new PixelColor(255, 255, 255, 255).ToInt();
|
||||
|
||||
//mxd. Check thing size
|
||||
float infoRadius, infoHeight;
|
||||
|
@ -122,7 +122,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
|
|||
// Use sector brightness for color shading
|
||||
PixelColor areabrightness = PixelColor.FromInt(mode.CalculateBrightness(level.brightnessbelow));
|
||||
PixelColor areacolor = PixelColor.Modulate(level.colorbelow, areabrightness);
|
||||
sectorcolor = areacolor.WithAlpha(255);
|
||||
sectorcolor = areacolor.WithAlpha(255).ToInt();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -157,19 +157,19 @@ namespace CodeImp.DoomBuilder.BuilderModes
|
|||
|
||||
if(sizeless) { //mxd
|
||||
float hh = height / 2;
|
||||
verts[0] = new WorldVertex(-radius + offsetx, 0.0f, offsety - hh, sectorcolor.ToInt(), 0.0f, 1.0f);
|
||||
verts[1] = new WorldVertex(-radius + offsetx, 0.0f, hh + offsety, sectorcolor.ToInt(), 0.0f, 0.0f);
|
||||
verts[2] = new WorldVertex(+radius + offsetx, 0.0f, hh + offsety, sectorcolor.ToInt(), 1.0f, 0.0f);
|
||||
verts[0] = new WorldVertex(-radius + offsetx, 0.0f, offsety - hh, sectorcolor, 0.0f, 1.0f);
|
||||
verts[1] = new WorldVertex(-radius + offsetx, 0.0f, hh + offsety, sectorcolor, 0.0f, 0.0f);
|
||||
verts[2] = new WorldVertex(+radius + offsetx, 0.0f, hh + offsety, sectorcolor, 1.0f, 0.0f);
|
||||
verts[3] = verts[0];
|
||||
verts[4] = verts[2];
|
||||
verts[5] = new WorldVertex(+radius + offsetx, 0.0f, offsety - hh, sectorcolor.ToInt(), 1.0f, 1.0f);
|
||||
verts[5] = new WorldVertex(+radius + offsetx, 0.0f, offsety - hh, sectorcolor, 1.0f, 1.0f);
|
||||
} else {
|
||||
verts[0] = new WorldVertex(-radius + offsetx, 0.0f, offsety, sectorcolor.ToInt(), 0.0f, 1.0f);
|
||||
verts[1] = new WorldVertex(-radius + offsetx, 0.0f, height + offsety, sectorcolor.ToInt(), 0.0f, 0.0f);
|
||||
verts[2] = new WorldVertex(+radius + offsetx, 0.0f, height + offsety, sectorcolor.ToInt(), 1.0f, 0.0f);
|
||||
verts[0] = new WorldVertex(-radius + offsetx, 0.0f, offsety, sectorcolor, 0.0f, 1.0f);
|
||||
verts[1] = new WorldVertex(-radius + offsetx, 0.0f, height + offsety, sectorcolor, 0.0f, 0.0f);
|
||||
verts[2] = new WorldVertex(+radius + offsetx, 0.0f, height + offsety, sectorcolor, 1.0f, 0.0f);
|
||||
verts[3] = verts[0];
|
||||
verts[4] = verts[2];
|
||||
verts[5] = new WorldVertex(+radius + offsetx, 0.0f, offsety, sectorcolor.ToInt(), 1.0f, 1.0f);
|
||||
verts[5] = new WorldVertex(+radius + offsetx, 0.0f, offsety, sectorcolor, 1.0f, 1.0f);
|
||||
}
|
||||
SetVertices(verts);
|
||||
}
|
||||
|
@ -183,12 +183,12 @@ namespace CodeImp.DoomBuilder.BuilderModes
|
|||
|
||||
// Make vertices
|
||||
WorldVertex[] verts = new WorldVertex[6];
|
||||
verts[0] = new WorldVertex(-radius, 0.0f, 0.0f, sectorcolor.ToInt(), 0.0f, 1.0f);
|
||||
verts[1] = new WorldVertex(-radius, 0.0f, height, sectorcolor.ToInt(), 0.0f, 0.0f);
|
||||
verts[2] = new WorldVertex(+radius, 0.0f, height, sectorcolor.ToInt(), 1.0f, 0.0f);
|
||||
verts[0] = new WorldVertex(-radius, 0.0f, 0.0f, sectorcolor, 0.0f, 1.0f);
|
||||
verts[1] = new WorldVertex(-radius, 0.0f, height, sectorcolor, 0.0f, 0.0f);
|
||||
verts[2] = new WorldVertex(+radius, 0.0f, height, sectorcolor, 1.0f, 0.0f);
|
||||
verts[3] = verts[0];
|
||||
verts[4] = verts[2];
|
||||
verts[5] = new WorldVertex(+radius, 0.0f, 0.0f, sectorcolor.ToInt(), 1.0f, 1.0f);
|
||||
verts[5] = new WorldVertex(+radius, 0.0f, 0.0f, sectorcolor, 1.0f, 1.0f);
|
||||
SetVertices(verts);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue