Jump to content

[1.1.2] Kerbin-Side (v1.1.0) & Supplements


AlphaAsh

Recommended Posts

KSP 1.0.5 has some changed and new functions that have impacted the collision meshes of third party statics. I've not been able to identify exactly how or what and cannot even begin to diagnose the problem, let alone fix it.

Kerbal Konstructs and KerbinSide is not compatible with KSP 1.0.5. That's all she wrote for this mod.
Link to comment
Share on other sites

[quote name='AlphaAsh']Unless KSP 1.1 miraculously becomes compatible, yes that's it, no more updates.[/QUOTE]

I don't know the first thing about modding, but if someone who wasn't you managed to find the problem and create a fix, would you continue this?
Link to comment
Share on other sites

[quote name='TaintedLion']I don't know the first thing about modding, but if someone who wasn't you managed to find the problem and create a fix, would you continue this?[/QUOTE]

Yes.

However, I'm fairly certain the issues are a result of changes to collider handling in KSP. That's some serious hard-code and considering the hacky nature of Kerbal Konstructs, I don't see a work-around. All I've been doing since 1.0.5 is try to fix the problems. It's not fun. I'm tired, fed up and Fallout 4 is calling me.

[COLOR="silver"][SIZE=1]- - - Updated - - -[/SIZE][/COLOR]

I would like to add that I really do appreciate all the support I've had from the players of this mod. Even when I'm a grumpy old fart with a tendency to cuss out idiots.

A particularly huge shout out to the mystery donater who just donated with a thank you. That is rare. I have something in my eye now.
Link to comment
Share on other sites

[quote name='AlphaAsh']Unless KSP 1.1 miraculously becomes compatible, yes that's it, no more updates.[/QUOTE]

Ironically (and sadly) the last version where memory issues made this mod hard to fit, is the first where it won't run.

I understand fully the situation and know it's worse for you than it is for me. I'm just saddened by it.
Link to comment
Share on other sites

[quote name='AlphaAsh']Yes.

However, I'm fairly certain the issues are a result of changes to collider handling in KSP. That's some serious hard-code and considering the hacky nature of Kerbal Konstructs, I don't see a work-around. All I've been doing since 1.0.5 is try to fix the problems. It's not fun. I'm tired, fed up and Fallout 4 is calling me.[/QUOTE]

*Hugs* Aww. I'm sorry to hear that. I was going to take a look to see if I could find the problem but I can;t even find the source. In the meantime take this donation in the hopes you come back to it in a few months.
Link to comment
Share on other sites

That sucks a lot. This mod has become a bedrock for many, and I was looking forward to it on my new rig.

Thinking pragmatically, we know the colliders are busted. What still does work? Can we repurpose any of it, or break it up? Could we convert statics to parts?
Link to comment
Share on other sites

[quote name='AlphaAsh']However, I'm fairly certain the issues are a result of changes to collider handling in KSP. That's some serious hard-code and considering the hacky nature of Kerbal Konstructs, I don't see a work-around. All I've been doing since 1.0.5 is try to fix the problems. It's not fun. I'm tired, fed up and Fallout 4 is calling me.[/quote]
Yes, apparently the mu loader is now forcing all MeshColliders to convex which causes Unity to recompute them if they're not already (hamfisted change for Unity 5.1 maybe? I read something about concave collider support being removed). This is causing the odd problems. Unfortunately, there isn't any direct way to prevent the loader from ignoring the flag inside the mu format that I can find.

One solution is to hijack the load process, parse the mu for the correct value and set it back before the collider is recomputed... and yes it's exactly as ugly as it sounds. Here's my prototype which appears to work. Note that you must rename all affected mu's to muks, so for example ksairstripv24.mu becomes ksairstripv24.muks. They'll look the same as any regular loaded mu so you shouldn't need to change any configs.

[spoiler=MuksLoader][code][DatabaseLoaderAttrib(new [] { "muks" })]
public class MuksLoader : DatabaseLoaderModel_MU
{
public override IEnumerator Load(UrlDir.UrlFile urlFile, FileInfo file)
{
Debug.Log("MuksLoader loading: " + urlFile.url);

yield return GameDatabase.Instance.StartCoroutine(base.Load(urlFile, file));

if (obj == null)
yield break;

using (var fs = new FileStream(urlFile.fullPath, FileMode.Open))
using (var mr = new MuksReader(fs))
{
var topNode = mr.LoadNodeTree();
var meshColliders = obj.GetComponentsInChildren<MeshCollider>(true);
var convexEntries = CreateEntries(topNode);

if (meshColliders.Length != convexEntries.Sum(kvp => kvp.Value.Count))
Debug.LogWarning("Number of MeshCollider convex nodes does not match number of MeshColliders!"); // whoops there's a bug!

foreach (var mc in meshColliders)
{
var url = HierarchyUtil.CompileID(mc.transform, mc.transform.root.name);

Queue<bool> convexFlagQueue;

if (convexEntries.TryGetValue(url, out convexFlagQueue))
mc.convex = convexFlagQueue.Dequeue();
else Debug.LogWarning("Failed to get convex flag for " + url);

}

if (convexEntries.Sum(kvp => kvp.Value.Count) > 0)
Debug.LogWarning("Unused convex entries for " + urlFile.url);
}

yield return 0;
}


/// This is probably a pretty crap way of doing this but because there can be multiple GameObjects with the
/// same url and a MeshCollider component, we won't be able to tell their convex flags apart if they differ...
/// I'm assuming we'll get the same MeshCollider order out as was created, so we'll just keep a list of what
/// was used in the order it was used in and remove entries as they're used
private static Dictionary<string, Queue<bool>> CreateEntries(
MuksReader.Node tree,
Dictionary<string, Queue<bool>> dictionary = null)
{
if (dictionary == null)
dictionary = new Dictionary<string, Queue<bool>>();

if (tree.HasMeshCollider)
AddToDictionary(dictionary, CreateNodeUrl(tree), tree.IsConvex);

foreach (var ch in tree.Children)
CreateEntries(ch, dictionary);

return dictionary;
}


private static void AddToDictionary(Dictionary<string, Queue<bool>> dictionary, string url, bool value)
{
if (dictionary == null) throw new ArgumentNullException("dictionary");
if (url == null) throw new ArgumentNullException("url");


Queue<bool> entries;
url = url.Replace(" ", string.Empty);

if (!dictionary.TryGetValue(url, out entries))
{
entries = new Queue<bool>();
dictionary.Add(url, entries);
}

entries.Enqueue(value);
}


private static string CreateNodeUrl(MuksReader.Node from)
{
if (@from == null) throw new ArgumentNullException("from");

if (@from.Parent == null)
return @from.Name;

return CreateNodeUrl(@from.Parent) + "/" + @from.Name;
}
}[/code][/spoiler]
[spoiler=MuksReader][code]public class MuksReader : BinaryReader
{
private enum DataBlockType
{
Node = 0,
Finished = 1,
Animation = 2,
MeshCollider = 3,
SphereCollider = 4,
CapsuleCollider = 5,
BoxCollider = 6,
MeshFilter = 7,
MeshRenderer = 8,
SkinnedMeshRenderer = 9,
MaterialSet = 10,
TextureSet = 12,
Light = 23,
TagAndLayer = 24,
MeshColliderTrigger = 25,
SphereColliderTrigger = 26,
CapsuleColliderTrigger = 27,
BoxColliderTrigger = 28,
WheelColliderTrigger = 29,
Camera = 30,
Particles = 31,
}


public class Node
{
public string Name = string.Empty;
public List<Node> Children = new List<Node>();
public bool HasMeshCollider = false;
public bool IsConvex = true;
public Node Parent;
}

struct MuFlags
{
public int FlagRelatedToMeshRendererAndTextures;
public string UnknownString;

}


public class MaterialTextureCache
{
private readonly Dictionary<int, List<string>> _textures = new Dictionary<int, List<string>>();

public void Add(int textureIndex, string textureName)
{
if (textureIndex < 0) return;

Insert(textureIndex, textureName);
}


private void Insert(int idx, string textureName)
{
List<string> list;

if (!_textures.TryGetValue(idx, out list))
{
list = new List<string>();
_textures.Add(idx, list);
}

if (!list.Contains(textureName))
list.Add(textureName);
}


public int GetMaterialCount()
{
return _textures.Keys.Count();
}
}


private const string MainTex = "_MainTex";
private const string BumpMapTex = "_BumpMap";
private const string EmissiveTex = "_Emissive";




public MuksReader(Stream input) : base(input)
{
}

public MuksReader(Stream input, Encoding encoding) : base(input, encoding)
{
}


public Node LoadNodeTree()
{
var fileType = ReadInt32();
var somehowMeshRendererRelated = ReadInt32();
var unknown2 = ReadString();

return ReadNode(null, new MaterialTextureCache(), new MuFlags { FlagRelatedToMeshRendererAndTextures = somehowMeshRendererRelated, UnknownString = unknown2 });
}


private Node ReadNode(Node parent, MaterialTextureCache cache, MuFlags flags)
{
var nodeName = ReadString();
var node = new Node { Name = nodeName };

if (parent != null)
parent.Children.Add(node);

node.Parent = parent;

DiscardVector3();
DiscardQuaternion();
DiscardVector3();

while (PeekChar() != -1)
{
var nextBlockType = (DataBlockType)ReadInt32();

switch (nextBlockType)
{
case DataBlockType.Node:
ReadNode(node, cache, flags);
break;

case DataBlockType.Finished:
return node;

case DataBlockType.MeshCollider:
ReadMeshCollider(node);
break;

case DataBlockType.MeshColliderTrigger:
ReadMeshColliderTrigger(node);
break;

default:
IgnoreBlock(nextBlockType, cache, flags);
break;
}

}

return node;
}


private void ReadMeshCollider(Node current)
{
if (current.HasMeshCollider)
throw new Exception("This node already has a MeshCollider assigned so something has broken!");

current.HasMeshCollider = true;
current.IsConvex = ReadBoolean();

DiscardMesh();
}


private void ReadMeshColliderTrigger(Node current)
{
if (current.HasMeshCollider)
throw new Exception("This node already has a MeshCollider assigned so something has broken!");

current.HasMeshCollider = true;
var trigger = ReadBoolean();
current.IsConvex = ReadBoolean();

DiscardMesh();
}


private void IgnoreBlock(DataBlockType type, MaterialTextureCache cache, MuFlags flags)
{
switch (type)
{
case DataBlockType.Animation:
DiscardAnimation();
break;

case DataBlockType.SphereCollider:
DiscardSphereCollider();
break;

case DataBlockType.CapsuleCollider:
DiscardCapsuleCollider();
break;

case DataBlockType.BoxCollider:
DiscardBoxCollider();
break;

case DataBlockType.MeshFilter:
DiscardMeshFilter();
break;

case DataBlockType.MeshRenderer:
DiscardMeshRenderer(flags.FlagRelatedToMeshRendererAndTextures);
break;

case DataBlockType.SkinnedMeshRenderer:
DiscardSkinnedMeshRenderer();
break;

case DataBlockType.MaterialSet:
DiscardMaterialSet(flags.FlagRelatedToMeshRendererAndTextures, cache);
break;

case DataBlockType.TextureSet:
DiscardTextureSet(cache);
break;

case DataBlockType.Light:
DiscardLight(flags.FlagRelatedToMeshRendererAndTextures);
break;

case DataBlockType.TagAndLayer:
var tag = ReadString();
var layer = ReadInt32();
break;

case DataBlockType.SphereColliderTrigger:
DiscardSphereColliderTrigger();
break;

case DataBlockType.CapsuleColliderTrigger:
DiscardCapsuleColliderTrigger();
break;

case DataBlockType.BoxColliderTrigger:
DiscardBoxColliderTrigger();
break;

case DataBlockType.WheelColliderTrigger:
DiscardWheelColliderTrigger();
break;

case DataBlockType.Camera:
DiscardCamera();
break;

case DataBlockType.Particles:
DiscardParticles();
break;

default:
throw new NotImplementedException("Unrecognized data block type: " + type);
}
}


private void DiscardVector2()
{
ReadSingle();
ReadSingle();
}


private void DiscardVector3()
{
ReadSingle();
ReadSingle();
ReadSingle();
}


private void DiscardVector4()
{
DiscardQuaternion();
}


private void DiscardBounds()
{
DiscardVector3();
DiscardVector3();
}


private void DiscardQuaternion()
{
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
}


private void DiscardMatrix()
{
for (var i = 0; i < 16; ++i)
ReadSingle();
}


private void DiscardColor()
{
DiscardVector4();
}


private void DiscardAnimation()
{
var numClips = ReadInt32();

for (var clipIndex = 0; clipIndex < numClips; ++clipIndex)
{
ReadString();
DiscardBounds();
ReadInt32();

var unknownNum = ReadInt32();

for (var j = 0; j < unknownNum; ++j)
{
ReadString();
ReadString();
ReadInt32();

ReadInt32();
ReadInt32();
var keyframeCount = ReadInt32();

for (var i = 0; i < keyframeCount; i++)
{
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadInt32();
}
}
}

ReadString();
ReadBoolean();
}


private void DiscardMesh()
{
var entryType = (EntryType)ReadInt32();
if (entryType != EntryType.MeshStart)
throw new Exception("Malformed mesh?");

var numVertices = ReadInt32();
var unknown = ReadInt32();

while ((entryType = (EntryType)ReadInt32()) != EntryType.MeshEnd)
{
switch (entryType)
{
case EntryType.MeshVerts:
case EntryType.MeshNormals:
for (var i = 0; i < numVertices; ++i)
DiscardVector3();
break;

case EntryType.MeshUV:
case EntryType.MeshUV2:
for (var i = 0; i < numVertices; ++i)
DiscardVector2();
break;

case EntryType.MeshTangents:
for (var i = 0; i < numVertices; ++i)
DiscardVector4();
break;

case EntryType.MeshTriangles:
var numTriangles = ReadInt32();
for (var i = 0; i < numTriangles; ++i)
ReadInt32();
break;

case EntryType.MeshBoneWeights:
ReadInt32();
ReadSingle();
ReadInt32();
ReadSingle();
ReadInt32();
ReadSingle();
ReadInt32();
ReadSingle();
break;


case EntryType.MeshBindPoses:
var poseCount = ReadInt32();

for (var i = 0; i < poseCount; ++i)
DiscardMatrix();

break;

}
}
}


private void DiscardSphereCollider()
{
ReadSingle();
DiscardVector3();
}


private void DiscardCapsuleCollider()
{
ReadSingle();
ReadInt32();
DiscardVector3();
}


private void DiscardBoxCollider()
{
DiscardVector3();
DiscardVector3();
}


private void DiscardMeshFilter()
{
DiscardMesh();
}


private void DiscardMeshRenderer(int muFlag)
{
if (muFlag >= 1)
{
ReadBoolean();
ReadBoolean();
}

var num = ReadInt32();

for (var i = 0; i < num; ++i)
ReadInt32();
}


private void DiscardSkinnedMeshRenderer()
{
var num = ReadInt32();

for (var i = 0; i < num; ++i)
ReadInt32();

DiscardVector3();
DiscardVector3();

ReadInt32();
ReadBoolean();

var numStrings = ReadInt32();

for (var i = 0; i < numStrings; ++i)
ReadString();

DiscardMesh();
}


private void DiscardMaterial(MaterialTextureCache cache)
{
ReadString();

var shader = ReadInt32();

switch (shader)
{
case 1: // KSP/Diffuse
DiscardMaterialTexture(MainTex, cache);
break;

case 2: // "KSP/Specular"
DiscardMaterialTexture(MainTex, cache);
DiscardColor();
ReadSingle();
break;

case 3: // "KSP/Bumped"
DiscardMaterialTexture(MainTex, cache);
DiscardMaterialTexture(BumpMapTex, cache);
break;

case 4: // "KSP/Bumped Specular"
DiscardMaterialTexture(MainTex, cache);
DiscardMaterialTexture(BumpMapTex, cache);
DiscardColor();
ReadSingle();
break;

case 5: // "KSP/Emissive/Diffuse"
DiscardMaterialTexture(MainTex, cache);
DiscardMaterialTexture(EmissiveTex, cache);
DiscardColor();
break;

case 6: // "KSP/Emissive/Specular"
DiscardMaterialTexture(MainTex, cache);
DiscardColor();
ReadSingle();
DiscardMaterialTexture(EmissiveTex, cache);
DiscardColor();
break;

case 7: // "KSP/Emissive/Bumped Specular"
DiscardMaterialTexture(MainTex, cache);
DiscardMaterialTexture(BumpMapTex, cache);
DiscardColor();
ReadSingle();
DiscardMaterialTexture(EmissiveTex, cache);
DiscardColor();
break;

case 8: // "KSP/Alpha/Cutoff"
DiscardMaterialTexture(MainTex, cache);
ReadSingle();
break;

case 9: // "KSP/Alpha/Cutoff Bumped"
DiscardMaterialTexture(MainTex, cache);
DiscardMaterialTexture(BumpMapTex, cache);
ReadSingle();
break;

case 10: // "KSP/Alpha/Translucent"
DiscardMaterialTexture(MainTex, cache);
break;

case 11: // "KSP/Alpha/Translucent Specular"
DiscardMaterialTexture(MainTex, cache);
ReadSingle();
DiscardColor();
ReadSingle();
break;

case 12: // "KSP/Alpha/Unlit Transparent"
DiscardMaterialTexture(MainTex, cache);
DiscardColor();
break;

case 13: // "KSP/Unlit"
DiscardMaterialTexture(MainTex, cache);
DiscardColor();
break;

case 14: // "KSP/Particles/Alpha Blended"
DiscardMaterialTexture(MainTex, cache);
DiscardColor();
ReadSingle();
break;

case 15: // "KSP/Particles/Additive"
DiscardMaterialTexture(MainTex, cache);
DiscardColor();
ReadSingle();
break;

default:
throw new NotImplementedException("Unrecognized value: " + shader);
}
}


private void DiscardMaterialTexture(string textureName, MaterialTextureCache cache)
{
var texIndex = ReadInt32();

cache.Add(texIndex, textureName);

DiscardVector2();
DiscardVector2();
}


private void DiscardMaterial4(MaterialTextureCache cache)
{
ReadString();
ReadString();
var propertyCount = ReadInt32();

for (int i = 0; i < propertyCount; ++i)
{
var textureName = ReadString();
var propertyType = ReadInt32();

switch (propertyType)
{
case 0:
DiscardColor();
break;

case 1:
DiscardVector4();
break;

case 2:
ReadSingle();
break;

case 3:
ReadSingle();
break;

case 4:
DiscardMaterialTexture(textureName, cache);
break;

default:
throw new NotImplementedException("Unrecognized value for property type: " + propertyType);
}
}
}


private void DiscardMaterialSet(int muFlag, MaterialTextureCache cache)
{
var numTextures = ReadInt32();

for (var i = 0; i < numTextures; ++i)
if (muFlag >= 4)
DiscardMaterial4(cache);
else
DiscardMaterial(cache);
}


private void DiscardTextureSet(MaterialTextureCache cache)
{
var numTextures = ReadInt32();

if (numTextures != cache.GetMaterialCount())
{
// stock mu loader errors out here if these don't match
return;
}


for (var i = 0; i < numTextures; i++)
{
ReadString();
ReadInt32();
}
}


private void DiscardLight(int muFlag)
{
ReadInt32();
ReadSingle();
ReadSingle();
DiscardColor();
ReadInt32();

if (muFlag > 1)
ReadSingle();
}


private void DiscardSphereColliderTrigger()
{
ReadBoolean();
ReadSingle();
DiscardVector3();
}


private void DiscardCapsuleColliderTrigger()
{
ReadBoolean();
ReadSingle();
ReadSingle();
ReadInt32();
DiscardVector3();
}


private void DiscardBoxColliderTrigger()
{
ReadBoolean();
DiscardVector3();
DiscardVector3();
}


private void DiscardWheelColliderTrigger()
{
ReadSingle();
ReadSingle();
ReadSingle();
DiscardVector3();

ReadSingle();
ReadSingle();
ReadSingle();

ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();

ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
}


private void DiscardCamera()
{
ReadInt32();
DiscardColor();
ReadInt32();
ReadBoolean();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
}


private void DiscardParticles()
{
ReadBoolean();
ReadInt32();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
DiscardColor();
ReadBoolean();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadInt32();
ReadInt32();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadBoolean();
ReadBoolean();

for (int i = 0; i < 5; i++)
DiscardColor();

ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadBoolean();
ReadBoolean();
ReadSingle();
ReadSingle();
ReadSingle();
ReadInt32();
ReadInt32();
ReadInt32();
ReadInt32();
ReadInt32();
}
}[/code][/spoiler] Edited by xEvilReeperx
Link to comment
Share on other sites

[quote name='xEvilReeperx']Yes, apparently the mu loader is now forcing all MeshColliders to convex which causes Unity to recompute them if they're not already (hamfisted change for Unity 5.1 maybe? I read something about concave collider support being removed). This is causing the odd problems. Unfortunately, there isn't any direct way to prevent the loader from ignoring the flag inside the mu format that I can find.

One solution is to hijack the load process, parse the mu for the correct value and set it back before the collider is recomputed... and yes it's exactly as ugly as it sounds. Here's my prototype which appears to work. Note that you must rename all affected mu's to muks, so for example ksairstripv24.mu becomes ksairstripv24.muks. They'll look the same as any regular loaded mu so you shouldn't need to change any configs.

[spoiler=MuksLoader][code][DatabaseLoaderAttrib(new [] { "muks" })]
public class MuksLoader : DatabaseLoaderModel_MU
{
public override IEnumerator Load(UrlDir.UrlFile urlFile, FileInfo file)
{
Debug.Log("MuksLoader loading: " + urlFile.url);

yield return GameDatabase.Instance.StartCoroutine(base.Load(urlFile, file));

if (obj == null)
yield break;

using (var fs = new FileStream(urlFile.fullPath, FileMode.Open))
using (var mr = new MuksReader(fs))
{
var topNode = mr.LoadNodeTree();
var meshColliders = obj.GetComponentsInChildren<MeshCollider>(true);
var convexEntries = CreateEntries(topNode);

if (meshColliders.Length != convexEntries.Sum(kvp => kvp.Value.Count))
Debug.LogWarning("Number of MeshCollider convex nodes does not match number of MeshColliders!"); // whoops there's a bug!

foreach (var mc in meshColliders)
{
var url = HierarchyUtil.CompileID(mc.transform, mc.transform.root.name);

Queue<bool> convexFlagQueue;

if (convexEntries.TryGetValue(url, out convexFlagQueue))
mc.convex = convexFlagQueue.Dequeue();
else Debug.LogWarning("Failed to get convex flag for " + url);

}

if (convexEntries.Sum(kvp => kvp.Value.Count) > 0)
Debug.LogWarning("Unused convex entries for " + urlFile.url);
}

yield return 0;
}


/// This is probably a pretty crap way of doing this but because there can be multiple GameObjects with the
/// same url and a MeshCollider component, we won't be able to tell their convex flags apart if they differ...
/// I'm assuming we'll get the same MeshCollider order out as was created, so we'll just keep a list of what
/// was used in the order it was used in and remove entries as they're used
private static Dictionary<string, Queue<bool>> CreateEntries(
MuksReader.Node tree,
Dictionary<string, Queue<bool>> dictionary = null)
{
if (dictionary == null)
dictionary = new Dictionary<string, Queue<bool>>();

if (tree.HasMeshCollider)
AddToDictionary(dictionary, CreateNodeUrl(tree), tree.IsConvex);

foreach (var ch in tree.Children)
CreateEntries(ch, dictionary);

return dictionary;
}


private static void AddToDictionary(Dictionary<string, Queue<bool>> dictionary, string url, bool value)
{
if (dictionary == null) throw new ArgumentNullException("dictionary");
if (url == null) throw new ArgumentNullException("url");


Queue<bool> entries;
url = url.Replace(" ", string.Empty);

if (!dictionary.TryGetValue(url, out entries))
{
entries = new Queue<bool>();
dictionary.Add(url, entries);
}

entries.Enqueue(value);
}


private static string CreateNodeUrl(MuksReader.Node from)
{
if (@from == null) throw new ArgumentNullException("from");

if (@from.Parent == null)
return @from.Name;

return CreateNodeUrl(@from.Parent) + "/" + @from.Name;
}
}[/code][/spoiler]
[spoiler=MuksReader][code]public class MuksReader : BinaryReader
{
private enum DataBlockType
{
Node = 0,
Finished = 1,
Animation = 2,
MeshCollider = 3,
SphereCollider = 4,
CapsuleCollider = 5,
BoxCollider = 6,
MeshFilter = 7,
MeshRenderer = 8,
SkinnedMeshRenderer = 9,
MaterialSet = 10,
TextureSet = 12,
Light = 23,
TagAndLayer = 24,
MeshColliderTrigger = 25,
SphereColliderTrigger = 26,
CapsuleColliderTrigger = 27,
BoxColliderTrigger = 28,
WheelColliderTrigger = 29,
Camera = 30,
Particles = 31,
}


public class Node
{
public string Name = string.Empty;
public List<Node> Children = new List<Node>();
public bool HasMeshCollider = false;
public bool IsConvex = true;
public Node Parent;
}

struct MuFlags
{
public int FlagRelatedToMeshRendererAndTextures;
public string UnknownString;

}


public class MaterialTextureCache
{
private readonly Dictionary<int, List<string>> _textures = new Dictionary<int, List<string>>();

public void Add(int textureIndex, string textureName)
{
if (textureIndex < 0) return;

Insert(textureIndex, textureName);
}


private void Insert(int idx, string textureName)
{
List<string> list;

if (!_textures.TryGetValue(idx, out list))
{
list = new List<string>();
_textures.Add(idx, list);
}

if (!list.Contains(textureName))
list.Add(textureName);
}


public int GetMaterialCount()
{
return _textures.Keys.Count();
}
}


private const string MainTex = "_MainTex";
private const string BumpMapTex = "_BumpMap";
private const string EmissiveTex = "_Emissive";




public MuksReader(Stream input) : base(input)
{
}

public MuksReader(Stream input, Encoding encoding) : base(input, encoding)
{
}


public Node LoadNodeTree()
{
var fileType = ReadInt32();
var somehowMeshRendererRelated = ReadInt32();
var unknown2 = ReadString();

return ReadNode(null, new MaterialTextureCache(), new MuFlags { FlagRelatedToMeshRendererAndTextures = somehowMeshRendererRelated, UnknownString = unknown2 });
}


private Node ReadNode(Node parent, MaterialTextureCache cache, MuFlags flags)
{
var nodeName = ReadString();
var node = new Node { Name = nodeName };

if (parent != null)
parent.Children.Add(node);

node.Parent = parent;

DiscardVector3();
DiscardQuaternion();
DiscardVector3();

while (PeekChar() != -1)
{
var nextBlockType = (DataBlockType)ReadInt32();

switch (nextBlockType)
{
case DataBlockType.Node:
ReadNode(node, cache, flags);
break;

case DataBlockType.Finished:
return node;

case DataBlockType.MeshCollider:
ReadMeshCollider(node);
break;

case DataBlockType.MeshColliderTrigger:
ReadMeshColliderTrigger(node);
break;

default:
IgnoreBlock(nextBlockType, cache, flags);
break;
}

}

return node;
}


private void ReadMeshCollider(Node current)
{
if (current.HasMeshCollider)
throw new Exception("This node already has a MeshCollider assigned so something has broken!");

current.HasMeshCollider = true;
current.IsConvex = ReadBoolean();

DiscardMesh();
}


private void ReadMeshColliderTrigger(Node current)
{
if (current.HasMeshCollider)
throw new Exception("This node already has a MeshCollider assigned so something has broken!");

current.HasMeshCollider = true;
var trigger = ReadBoolean();
current.IsConvex = ReadBoolean();

DiscardMesh();
}


private void IgnoreBlock(DataBlockType type, MaterialTextureCache cache, MuFlags flags)
{
switch (type)
{
case DataBlockType.Animation:
DiscardAnimation();
break;

case DataBlockType.SphereCollider:
DiscardSphereCollider();
break;

case DataBlockType.CapsuleCollider:
DiscardCapsuleCollider();
break;

case DataBlockType.BoxCollider:
DiscardBoxCollider();
break;

case DataBlockType.MeshFilter:
DiscardMeshFilter();
break;

case DataBlockType.MeshRenderer:
DiscardMeshRenderer(flags.FlagRelatedToMeshRendererAndTextures);
break;

case DataBlockType.SkinnedMeshRenderer:
DiscardSkinnedMeshRenderer();
break;

case DataBlockType.MaterialSet:
DiscardMaterialSet(flags.FlagRelatedToMeshRendererAndTextures, cache);
break;

case DataBlockType.TextureSet:
DiscardTextureSet(cache);
break;

case DataBlockType.Light:
DiscardLight(flags.FlagRelatedToMeshRendererAndTextures);
break;

case DataBlockType.TagAndLayer:
var tag = ReadString();
var layer = ReadInt32();
break;

case DataBlockType.SphereColliderTrigger:
DiscardSphereColliderTrigger();
break;

case DataBlockType.CapsuleColliderTrigger:
DiscardCapsuleColliderTrigger();
break;

case DataBlockType.BoxColliderTrigger:
DiscardBoxColliderTrigger();
break;

case DataBlockType.WheelColliderTrigger:
DiscardWheelColliderTrigger();
break;

case DataBlockType.Camera:
DiscardCamera();
break;

case DataBlockType.Particles:
DiscardParticles();
break;

default:
throw new NotImplementedException("Unrecognized data block type: " + type);
}
}


private void DiscardVector2()
{
ReadSingle();
ReadSingle();
}


private void DiscardVector3()
{
ReadSingle();
ReadSingle();
ReadSingle();
}


private void DiscardVector4()
{
DiscardQuaternion();
}


private void DiscardBounds()
{
DiscardVector3();
DiscardVector3();
}


private void DiscardQuaternion()
{
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
}


private void DiscardMatrix()
{
for (var i = 0; i < 16; ++i)
ReadSingle();
}


private void DiscardColor()
{
DiscardVector4();
}


private void DiscardAnimation()
{
var numClips = ReadInt32();

for (var clipIndex = 0; clipIndex < numClips; ++clipIndex)
{
ReadString();
DiscardBounds();
ReadInt32();

var unknownNum = ReadInt32();

for (var j = 0; j < unknownNum; ++j)
{
ReadString();
ReadString();
ReadInt32();

ReadInt32();
ReadInt32();
var keyframeCount = ReadInt32();

for (var i = 0; i < keyframeCount; i++)
{
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadInt32();
}
}
}

ReadString();
ReadBoolean();
}


private void DiscardMesh()
{
var entryType = (EntryType)ReadInt32();
if (entryType != EntryType.MeshStart)
throw new Exception("Malformed mesh?");

var numVertices = ReadInt32();
var unknown = ReadInt32();

while ((entryType = (EntryType)ReadInt32()) != EntryType.MeshEnd)
{
switch (entryType)
{
case EntryType.MeshVerts:
case EntryType.MeshNormals:
for (var i = 0; i < numVertices; ++i)
DiscardVector3();
break;

case EntryType.MeshUV:
case EntryType.MeshUV2:
for (var i = 0; i < numVertices; ++i)
DiscardVector2();
break;

case EntryType.MeshTangents:
for (var i = 0; i < numVertices; ++i)
DiscardVector4();
break;

case EntryType.MeshTriangles:
var numTriangles = ReadInt32();
for (var i = 0; i < numTriangles; ++i)
ReadInt32();
break;

case EntryType.MeshBoneWeights:
ReadInt32();
ReadSingle();
ReadInt32();
ReadSingle();
ReadInt32();
ReadSingle();
ReadInt32();
ReadSingle();
break;


case EntryType.MeshBindPoses:
var poseCount = ReadInt32();

for (var i = 0; i < poseCount; ++i)
DiscardMatrix();

break;

}
}
}


private void DiscardSphereCollider()
{
ReadSingle();
DiscardVector3();
}


private void DiscardCapsuleCollider()
{
ReadSingle();
ReadInt32();
DiscardVector3();
}


private void DiscardBoxCollider()
{
DiscardVector3();
DiscardVector3();
}


private void DiscardMeshFilter()
{
DiscardMesh();
}


private void DiscardMeshRenderer(int muFlag)
{
if (muFlag >= 1)
{
ReadBoolean();
ReadBoolean();
}

var num = ReadInt32();

for (var i = 0; i < num; ++i)
ReadInt32();
}


private void DiscardSkinnedMeshRenderer()
{
var num = ReadInt32();

for (var i = 0; i < num; ++i)
ReadInt32();

DiscardVector3();
DiscardVector3();

ReadInt32();
ReadBoolean();

var numStrings = ReadInt32();

for (var i = 0; i < numStrings; ++i)
ReadString();

DiscardMesh();
}


private void DiscardMaterial(MaterialTextureCache cache)
{
ReadString();

var shader = ReadInt32();

switch (shader)
{
case 1: // KSP/Diffuse
DiscardMaterialTexture(MainTex, cache);
break;

case 2: // "KSP/Specular"
DiscardMaterialTexture(MainTex, cache);
DiscardColor();
ReadSingle();
break;

case 3: // "KSP/Bumped"
DiscardMaterialTexture(MainTex, cache);
DiscardMaterialTexture(BumpMapTex, cache);
break;

case 4: // "KSP/Bumped Specular"
DiscardMaterialTexture(MainTex, cache);
DiscardMaterialTexture(BumpMapTex, cache);
DiscardColor();
ReadSingle();
break;

case 5: // "KSP/Emissive/Diffuse"
DiscardMaterialTexture(MainTex, cache);
DiscardMaterialTexture(EmissiveTex, cache);
DiscardColor();
break;

case 6: // "KSP/Emissive/Specular"
DiscardMaterialTexture(MainTex, cache);
DiscardColor();
ReadSingle();
DiscardMaterialTexture(EmissiveTex, cache);
DiscardColor();
break;

case 7: // "KSP/Emissive/Bumped Specular"
DiscardMaterialTexture(MainTex, cache);
DiscardMaterialTexture(BumpMapTex, cache);
DiscardColor();
ReadSingle();
DiscardMaterialTexture(EmissiveTex, cache);
DiscardColor();
break;

case 8: // "KSP/Alpha/Cutoff"
DiscardMaterialTexture(MainTex, cache);
ReadSingle();
break;

case 9: // "KSP/Alpha/Cutoff Bumped"
DiscardMaterialTexture(MainTex, cache);
DiscardMaterialTexture(BumpMapTex, cache);
ReadSingle();
break;

case 10: // "KSP/Alpha/Translucent"
DiscardMaterialTexture(MainTex, cache);
break;

case 11: // "KSP/Alpha/Translucent Specular"
DiscardMaterialTexture(MainTex, cache);
ReadSingle();
DiscardColor();
ReadSingle();
break;

case 12: // "KSP/Alpha/Unlit Transparent"
DiscardMaterialTexture(MainTex, cache);
DiscardColor();
break;

case 13: // "KSP/Unlit"
DiscardMaterialTexture(MainTex, cache);
DiscardColor();
break;

case 14: // "KSP/Particles/Alpha Blended"
DiscardMaterialTexture(MainTex, cache);
DiscardColor();
ReadSingle();
break;

case 15: // "KSP/Particles/Additive"
DiscardMaterialTexture(MainTex, cache);
DiscardColor();
ReadSingle();
break;

default:
throw new NotImplementedException("Unrecognized value: " + shader);
}
}


private void DiscardMaterialTexture(string textureName, MaterialTextureCache cache)
{
var texIndex = ReadInt32();

cache.Add(texIndex, textureName);

DiscardVector2();
DiscardVector2();
}


private void DiscardMaterial4(MaterialTextureCache cache)
{
ReadString();
ReadString();
var propertyCount = ReadInt32();

for (int i = 0; i < propertyCount; ++i)
{
var textureName = ReadString();
var propertyType = ReadInt32();

switch (propertyType)
{
case 0:
DiscardColor();
break;

case 1:
DiscardVector4();
break;

case 2:
ReadSingle();
break;

case 3:
ReadSingle();
break;

case 4:
DiscardMaterialTexture(textureName, cache);
break;

default:
throw new NotImplementedException("Unrecognized value for property type: " + propertyType);
}
}
}


private void DiscardMaterialSet(int muFlag, MaterialTextureCache cache)
{
var numTextures = ReadInt32();

for (var i = 0; i < numTextures; ++i)
if (muFlag >= 4)
DiscardMaterial4(cache);
else
DiscardMaterial(cache);
}


private void DiscardTextureSet(MaterialTextureCache cache)
{
var numTextures = ReadInt32();

if (numTextures != cache.GetMaterialCount())
{
// stock mu loader errors out here if these don't match
return;
}


for (var i = 0; i < numTextures; i++)
{
ReadString();
ReadInt32();
}
}


private void DiscardLight(int muFlag)
{
ReadInt32();
ReadSingle();
ReadSingle();
DiscardColor();
ReadInt32();

if (muFlag > 1)
ReadSingle();
}


private void DiscardSphereColliderTrigger()
{
ReadBoolean();
ReadSingle();
DiscardVector3();
}


private void DiscardCapsuleColliderTrigger()
{
ReadBoolean();
ReadSingle();
ReadSingle();
ReadInt32();
DiscardVector3();
}


private void DiscardBoxColliderTrigger()
{
ReadBoolean();
DiscardVector3();
DiscardVector3();
}


private void DiscardWheelColliderTrigger()
{
ReadSingle();
ReadSingle();
ReadSingle();
DiscardVector3();

ReadSingle();
ReadSingle();
ReadSingle();

ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();

ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
}


private void DiscardCamera()
{
ReadInt32();
DiscardColor();
ReadInt32();
ReadBoolean();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
}


private void DiscardParticles()
{
ReadBoolean();
ReadInt32();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
DiscardColor();
ReadBoolean();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadInt32();
ReadInt32();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadBoolean();
ReadBoolean();

for (int i = 0; i < 5; i++)
DiscardColor();

ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadSingle();
ReadBoolean();
ReadBoolean();
ReadSingle();
ReadSingle();
ReadSingle();
ReadInt32();
ReadInt32();
ReadInt32();
ReadInt32();
ReadInt32();
}
}[/code][/spoiler][/QUOTE]

Oh ugh. That's kludgy.
The easier fix would be for the devs to have their new code ignore anything flagged as Static = True in a config. If that shows up in 1.1 then I'll be very happy.
Whilst your solution is appreciated, I agree, it's ugly. Too ugly though.

[COLOR="silver"][SIZE=1]- - - Updated - - -[/SIZE][/COLOR]

[quote name='ModZero']On the other hand, I'd kinda expect Squad to [I]eventually[/I] support what this mod does. Probably not anytime soon, but it's a Historical Necessityâ„¢.[/QUOTE]

I hope so too. However, considering the unmodability of the KSC, I doubt it'll be functionality that is exposed any more than it already is.

[COLOR="silver"][SIZE=1]- - - Updated - - -[/SIZE][/COLOR]

[quote name='colmo']That sucks a lot. This mod has become a bedrock for many, and I was looking forward to it on my new rig.

Thinking pragmatically, we know the colliders are busted. What still does work? Can we repurpose any of it, or break it up? Could we convert statics to parts?[/QUOTE]

Theoretically I could go over every asset and make sure all colliders are convex. However, large static models tend to need far more complex colliders than parts, making concave colliders something of a necessity. They've never caused problems on statics before, which is one of the reasons this change is so frustrating. It just did not consider the impact on statics. IE My modding efforts have been ignored. Again.

[COLOR="silver"][SIZE=1]- - - Updated - - -[/SIZE][/COLOR]

[quote name='CattyNebulart']*Hugs* Aww. I'm sorry to hear that. I was going to take a look to see if I could find the problem but I can;t even find the source. In the meantime take this donation in the hopes you come back to it in a few months.[/QUOTE]

Thank you. I will get the latest KK source up on GitHub this week, so you're welcome to have a poke at the code.
Link to comment
Share on other sites

[quote name='AlphaAsh']Oh ugh. That's kludgy.
The easier fix would be for the devs to have their new code ignore anything flagged as Static = True in a config. If that shows up in 1.1 then I'll be very happy.[/quote]
Honestly, that is just as kludgy. The complete model information would then be in potentially two different files. Might be time to write your own loader. 99.9% of the stuff loaded by the Mu loader will be attached to rigidbodies, and concave colliders on dynamic bodies aren't allowed in 5.1. That's probably the root cause of this change
Link to comment
Share on other sites

[quote name='blowfish']I'm not trying to excuse Squad's doing this without telling anyone, but aren't there justifiable performance concerns for why all colliders in KSP should be convex, even if it requires more colliders?[/QUOTE]

Nope. It's an archaic tech solution for a second-rate game engine. It's not Squad, it's Unity.
Link to comment
Share on other sites

Man, this is quite the disappointment. I've loved KK/Kerbin-side ever since it came out. It's now the major reason I'm not going to ever delete my 1.0.4 install. While I hope for (but don't expect) a fix in 1.1, I also want to wish you all the best in your future gaming and modding endeavors! Thank you so much for all your work over the years, AlphaAsh!
Link to comment
Share on other sites

[quote name='AlphaAsh']I'm tired, fed up and Fallout 4 is calling me.[/QUOTE]

I'm sorry to hear that, it does sound like you need a break. A fair warning though. Fallout 4 is ridden with bugs, even by Bethsoft's release standards (their recent status update doesn't really do it justice). I help maintain a bug tracker and I don't really see them fix even a quarter of this stuff with the amount of patchwork they usually do. Not even before the year is out, just ever. Unless you don't mind jumping from the frying pan into the fire, I'd wait a couple months for the mod community to sort half of it out.
Link to comment
Share on other sites

[quote name='Tricky14']I'm sorry to hear that, it does sound like you need a break. A fair warning though. Fallout 4 is ridden with bugs, even by Bethsoft's release standards (their recent status update doesn't really do it justice). I help maintain a bug tracker and I don't really see them fix even a quarter of this stuff with the amount of patchwork they usually do. Not even before the year is out, just ever. Unless you don't mind jumping from the frying pan into the fire, I'd wait a couple months for the mod community to sort half of it out.[/QUOTE]

The bugs don't really bother me in FO4 so far but then I've only played it for a couple of dozen hours. I'll probably worry more about Bethesda's shoddy support ethic when I start modding it.
Link to comment
Share on other sites

[quote name='theonegalen']I also want to wish you all the best in your future gaming and modding endeavors! Thank you so much for all your work over the years, AlphaAsh![/QUOTE]

Thanks. Don't worry though, I'm not done with KSP. I'm just done with trying to fix KK for now. It stopped being fun.

[COLOR=silver][SIZE=1]- - - Updated - - -[/SIZE][/COLOR]

[quote name='comham']wow, thanks squad, you spipped up again and ruined the only mod that made kerbin feel like more than a spherical heightmap[/QUOTE]

If you're happy to work within the new constraints on colliders, theoretically KK should still work fine with static content produced. Going over every asset in KerbinSide is just not something I'm willing to do and the reality is a lot of the assets need complex, concave colliders due to their complexity and size. Runways are a good example - you can't get away with a single convex collider because the length and width of such a collider ends up with mystery tears as far as KSP's physics is concerned.
Link to comment
Share on other sites

so lemme get this straight, as im activly making a kk mod, they should work fine aslong as all the coliders are convex? so splitting the model into lots f small sections and making each a convex collider should work?

[COLOR="silver"][SIZE=1]- - - Updated - - -[/SIZE][/COLOR]

im not kidding but should we all see if we can contnct squad about this and see if theyere is anythig they could do officially...

[COLOR="silver"][SIZE=1]- - - Updated - - -[/SIZE][/COLOR]

[quote name='CattyNebulart']There is also a bug that slows everything to a crawl with the error message "The hull has more than 255 polygons. This is invalid."

[URL]http://i.imgur.com/TsaWfbu.jpg[/URL]

I think it is the desert base as I took off from the old ksc.[/QUOTE]
this is because it is trying to apply a mesh collider to parts with more than the 255 poly limit it apperes
Link to comment
Share on other sites

Guest
This topic is now closed to further replies.
×
×
  • Create New...