mirror of
https://mirror.ghproxy.com/https://github.com/dexyfex/CodeWalker
synced 2026-05-15 01:15:11 +08:00
Merge remote changes with local
This commit is contained in:
@@ -7,6 +7,8 @@ using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using CodeWalker.Core.Utils;
|
||||
using CodeWalker.World;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
@@ -925,6 +927,7 @@ namespace CodeWalker.GameFiles
|
||||
GrassInstanceBatches = batches.ToArray();
|
||||
|
||||
HasChanged = true;
|
||||
UpdateGrassPhysDict(true);
|
||||
}
|
||||
|
||||
public bool RemoveGrassBatch(YmapGrassInstanceBatch batch)
|
||||
@@ -949,6 +952,10 @@ namespace CodeWalker.GameFiles
|
||||
}
|
||||
}
|
||||
|
||||
if (batches.Count <= 0)
|
||||
{
|
||||
UpdateGrassPhysDict(false);
|
||||
}
|
||||
|
||||
GrassInstanceBatches = batches.ToArray();
|
||||
|
||||
@@ -1137,6 +1144,27 @@ namespace CodeWalker.GameFiles
|
||||
}
|
||||
|
||||
|
||||
private void UpdateGrassPhysDict(bool add)
|
||||
{
|
||||
var physDict = physicsDictionaries?.ToList() ?? new List<MetaHash>();
|
||||
var vproc1 = JenkHash.GenHash("v_proc1");
|
||||
var vproc2 = JenkHash.GenHash("v_proc2"); // I think you need vproc2 as well.
|
||||
var change = false;
|
||||
if (!physDict.Contains(vproc1))
|
||||
{
|
||||
change = true;
|
||||
if (add) physDict.Add(vproc1);
|
||||
else physDict.Remove(vproc1);
|
||||
}
|
||||
if (!physDict.Contains(vproc2))
|
||||
{
|
||||
change = true;
|
||||
if (add) physDict.Add(vproc2);
|
||||
else physDict.Remove(vproc2);
|
||||
}
|
||||
if (change) physicsDictionaries = physDict.ToArray();
|
||||
}
|
||||
|
||||
|
||||
private static uint SetBit(uint value, int bit)
|
||||
{
|
||||
@@ -1493,6 +1521,8 @@ namespace CodeWalker.GameFiles
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||
public class YmapGrassInstanceBatch
|
||||
{
|
||||
private const float BatchVertMultiplier = 0.00001525878f;
|
||||
|
||||
public Archetype Archetype { get; set; } //cached by GameFileCache on loading...
|
||||
public rage__fwGrassInstanceListDef Batch { get; set; }
|
||||
public rage__fwGrassInstanceListDef__InstanceData[] Instances { get; set; }
|
||||
@@ -1504,10 +1534,478 @@ namespace CodeWalker.GameFiles
|
||||
public float Distance; //used for rendering
|
||||
public YmapFile Ymap { get; set; }
|
||||
|
||||
private List<BoundingBox> grassBounds; // for brush
|
||||
public bool BrushEnabled; // for brush
|
||||
public float BrushRadius = 5f; // for brush
|
||||
public bool HasChanged; // for brush and renderer
|
||||
|
||||
// TODO: Make configurable.
|
||||
const float BoundingSize = 0.3F;
|
||||
static readonly Vector3 GrassMinMax = Vector3.One * BoundingSize;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Batch.ToString();
|
||||
}
|
||||
|
||||
public void UpdateInstanceCount()
|
||||
{
|
||||
var b = Batch;
|
||||
var ins = b.InstanceList;
|
||||
ins.Count1 = (ushort)Instances.Length;
|
||||
b.InstanceList = ins;
|
||||
Batch = b;
|
||||
}
|
||||
|
||||
public bool IsPointBlockedByInstance(Vector3 point)
|
||||
{
|
||||
return grassBounds.Any(bb => bb.Contains(point) == ContainmentType.Contains);
|
||||
}
|
||||
|
||||
private void ReInitializeBoundingCache()
|
||||
{
|
||||
// cache is already initialized correctly.
|
||||
if (grassBounds != null && (grassBounds.Count == Instances.Length))
|
||||
return;
|
||||
|
||||
// Clear the current bounding cache.
|
||||
if (grassBounds == null)
|
||||
grassBounds = new List<BoundingBox>();
|
||||
else grassBounds?.Clear();
|
||||
|
||||
foreach (var inst in Instances)
|
||||
{
|
||||
// create bounding box for this instance.
|
||||
var worldPos = GetGrassWorldPos(inst.Position, new BoundingBox(AABBMin, AABBMax));
|
||||
var bb = new BoundingBox(worldPos - GrassMinMax, worldPos + GrassMinMax);
|
||||
grassBounds.Add(bb);
|
||||
}
|
||||
}
|
||||
|
||||
public bool EraseInstancesAtMouse(
|
||||
YmapGrassInstanceBatch batch,
|
||||
SpaceRayIntersectResult mouseRay,
|
||||
float radius)
|
||||
{
|
||||
rage__spdAABB batchAABB = batch.Batch.BatchAABB;
|
||||
var oldInstanceBounds = new BoundingBox
|
||||
(
|
||||
batchAABB.min.XYZ(),
|
||||
batchAABB.max.XYZ()
|
||||
);
|
||||
var deleteSphere = new BoundingSphere(mouseRay.Position, radius);
|
||||
|
||||
// check each instance to see if it's in the delete sphere
|
||||
// thankfully we've just avoided an O(n^2) op using this bounds stuff (doesn't mean it's super fast though,
|
||||
// but it's not super slow either, even at like 50,000 instances)
|
||||
var insList = new List<rage__fwGrassInstanceListDef__InstanceData>();
|
||||
foreach (var instance in batch.Instances)
|
||||
{
|
||||
// get the world pos
|
||||
var worldPos = GetGrassWorldPos(instance.Position, oldInstanceBounds);
|
||||
|
||||
// create a boundary around the instance.
|
||||
var instanceBounds = new BoundingBox(worldPos - GrassMinMax, worldPos + GrassMinMax);
|
||||
|
||||
// check if the sphere contains the boundary.
|
||||
var bb = new BoundingBox(instanceBounds.Minimum, instanceBounds.Maximum);
|
||||
var ct = deleteSphere.Contains(ref bb);
|
||||
if (ct == ContainmentType.Contains || ct == ContainmentType.Intersects)
|
||||
{
|
||||
//delInstances.Add(instance); // Add a copy of this instance
|
||||
continue;
|
||||
}
|
||||
insList.Add(instance);
|
||||
}
|
||||
if (insList.Count == Instances.Length)
|
||||
return false;
|
||||
|
||||
var newBounds = GetNewGrassBounds(insList, oldInstanceBounds);
|
||||
// recalc instances
|
||||
var b = RecalcBatch(newBounds, batch);
|
||||
batch.Batch = b;
|
||||
insList = RecalculateInstances(insList, oldInstanceBounds, newBounds);
|
||||
batch.Instances = insList.ToArray();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void CreateInstancesAtMouse(
|
||||
YmapGrassInstanceBatch batch,
|
||||
SpaceRayIntersectResult mouseRay,
|
||||
float radius,
|
||||
int amount,
|
||||
Func<Vector3, SpaceRayIntersectResult> spawnRayFunc,
|
||||
Color color,
|
||||
int ao,
|
||||
int scale,
|
||||
Vector3 pad,
|
||||
bool randomScale)
|
||||
{
|
||||
|
||||
ReInitializeBoundingCache();
|
||||
var spawnPosition = mouseRay.Position;
|
||||
var positions = new List<Vector3>();
|
||||
var normals = new List<Vector3>();
|
||||
|
||||
// Get rand positions.
|
||||
GetSpawns(spawnPosition, spawnRayFunc, positions, normals, radius, amount);
|
||||
if (positions.Count <= 0) return;
|
||||
|
||||
// get the instance list
|
||||
var instances =
|
||||
batch.Instances?.ToList() ?? new List<rage__fwGrassInstanceListDef__InstanceData>();
|
||||
var batchAABB = batch.Batch.BatchAABB;
|
||||
|
||||
// make sure to store the old instance bounds for the original
|
||||
// grass instances
|
||||
var oldInstanceBounds = new BoundingBox(batchAABB.min.XYZ(), batchAABB.max.XYZ());
|
||||
|
||||
if (positions.Count <= 0)
|
||||
return;
|
||||
|
||||
// Begin the spawn bounds.
|
||||
var grassBound = new BoundingBox(positions[0] - GrassMinMax, positions[0] + GrassMinMax);
|
||||
grassBound = EncapsulatePositions(positions, grassBound);
|
||||
|
||||
// Calculate the new spawn bounds.
|
||||
var newInstanceBounds = new BoundingBox(oldInstanceBounds.Minimum, oldInstanceBounds.Maximum);
|
||||
newInstanceBounds = instances.Count > 0
|
||||
? newInstanceBounds.Encapsulate(grassBound)
|
||||
: new BoundingBox(grassBound.Minimum, grassBound.Maximum);
|
||||
|
||||
// now we need to recalculate the position of each instance
|
||||
instances = RecalculateInstances(instances, oldInstanceBounds, newInstanceBounds);
|
||||
|
||||
// Add new instances at each spawn position with
|
||||
// the parameters in the brush.
|
||||
SpawnInstances(positions, normals, instances, newInstanceBounds, color, ao, scale, pad, randomScale);
|
||||
|
||||
// then recalc the bounds of the grass batch
|
||||
var b = RecalcBatch(newInstanceBounds, batch);
|
||||
|
||||
// plug our values back in and refresh the ymap.
|
||||
batch.Batch = b;
|
||||
|
||||
// Give back the new intsances
|
||||
batch.Instances = instances.ToArray();
|
||||
grassBounds.Clear();
|
||||
}
|
||||
|
||||
// bhv approach recommended by dexy.
|
||||
public YmapGrassInstanceBatch[] OptimizeInstances(YmapGrassInstanceBatch batch, float minRadius)
|
||||
{
|
||||
// this function will return an array of grass instance batches
|
||||
// that are split up into sectors (groups) with a specific size.
|
||||
// say for instance we have 30,000 instances spread across a large
|
||||
// distance. We will split those instances into a grid-like group
|
||||
// and return the groups as an array of batches.
|
||||
var oldInstanceBounds = new BoundingBox(batch.Batch.BatchAABB.min.XYZ(), batch.Batch.BatchAABB.max.XYZ());
|
||||
|
||||
if (oldInstanceBounds.Radius() < minRadius)
|
||||
{
|
||||
return new [] { batch };
|
||||
}
|
||||
|
||||
// Get our optimized grassInstances
|
||||
var split = SplitGrassRecursive(batch.Instances.ToList(), oldInstanceBounds, minRadius);
|
||||
|
||||
// Initiate a new batch list.
|
||||
var newBatches = new List<YmapGrassInstanceBatch>();
|
||||
|
||||
foreach (var grassList in split)
|
||||
{
|
||||
// Create a new batch
|
||||
var newBatch = new YmapGrassInstanceBatch
|
||||
{
|
||||
Archetype = batch.Archetype,
|
||||
Ymap = batch.Ymap
|
||||
};
|
||||
|
||||
// Get the boundary of the grassInstances
|
||||
var newInstanceBounds = GetNewGrassBounds(grassList, oldInstanceBounds);
|
||||
|
||||
// Recalculate the batch boundaries.
|
||||
var b = RecalcBatch(newInstanceBounds, newBatch);
|
||||
newBatch.Batch = b;
|
||||
|
||||
var ins = RecalculateInstances(grassList, oldInstanceBounds, newInstanceBounds);
|
||||
newBatch.Instances = ins.ToArray();
|
||||
newBatches.Add(newBatch);
|
||||
}
|
||||
|
||||
return newBatches.ToArray();
|
||||
}
|
||||
|
||||
private List<List<rage__fwGrassInstanceListDef__InstanceData>> SplitGrassRecursive(
|
||||
IReadOnlyList<rage__fwGrassInstanceListDef__InstanceData> grassInstances,
|
||||
BoundingBox batchAABB,
|
||||
float minRadius = 15F
|
||||
)
|
||||
{
|
||||
var ret = new List<List<rage__fwGrassInstanceListDef__InstanceData>>();
|
||||
var oldPoints = SplitGrass(grassInstances, batchAABB);
|
||||
while (true)
|
||||
{
|
||||
var stop = true;
|
||||
var newPoints = new List<List<rage__fwGrassInstanceListDef__InstanceData>>();
|
||||
foreach (var mb in oldPoints)
|
||||
{
|
||||
// for some reason we got a null group?
|
||||
if (mb == null)
|
||||
continue;
|
||||
|
||||
// Get the bounds of the grassInstances list
|
||||
var radius = GetNewGrassBounds(mb, batchAABB).Radius();
|
||||
|
||||
// check if the radius of the grassInstances
|
||||
if (radius <= minRadius)
|
||||
{
|
||||
// this point list is within the minimum
|
||||
// radius.
|
||||
ret.Add(mb);
|
||||
continue; // we don't need to continue.
|
||||
}
|
||||
|
||||
// since we're here let's keep going
|
||||
stop = false;
|
||||
|
||||
// split the grassInstances again
|
||||
var s = SplitGrass(mb, batchAABB);
|
||||
|
||||
// add it into the new grassInstances list.
|
||||
newPoints.AddRange(s);
|
||||
}
|
||||
|
||||
// set the old grassInstances to the new grassInstances.
|
||||
oldPoints = newPoints.ToArray();
|
||||
|
||||
// if we're done, and all grassInstances are within the desired size
|
||||
// then end the loop.
|
||||
if (stop) break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private List<rage__fwGrassInstanceListDef__InstanceData>[] SplitGrass(
|
||||
IReadOnlyList<rage__fwGrassInstanceListDef__InstanceData> points,
|
||||
BoundingBox batchAABB)
|
||||
{
|
||||
var pointGroup = new List<rage__fwGrassInstanceListDef__InstanceData>[2];
|
||||
|
||||
// Calculate the bounds of these grassInstances.
|
||||
var m = GetNewGrassBounds(points, batchAABB);
|
||||
|
||||
// Get the center and size
|
||||
var mm = new Vector3
|
||||
{
|
||||
X = Math.Abs(m.Minimum.X - m.Maximum.X),
|
||||
Y = Math.Abs(m.Minimum.Y - m.Maximum.Y),
|
||||
Z = Math.Abs(m.Minimum.Z - m.Maximum.Z)
|
||||
};
|
||||
|
||||
// x is the greatest axis...
|
||||
if (mm.X > mm.Y && mm.X > mm.Z)
|
||||
{
|
||||
// Calculate both boundaries.
|
||||
var lhs = new BoundingBox(m.Minimum, m.Maximum - new Vector3(mm.X * 0.5F, 0, 0));
|
||||
var rhs = new BoundingBox(m.Minimum + new Vector3(mm.X * 0.5F, 0, 0), m.Maximum);
|
||||
|
||||
// Set the grassInstances accordingly.
|
||||
pointGroup[0] = points
|
||||
.Where(p => lhs.Contains(GetGrassWorldPos(p.Position, batchAABB)) == ContainmentType.Contains).ToList();
|
||||
pointGroup[1] = points
|
||||
.Where(p => rhs.Contains(GetGrassWorldPos(p.Position, batchAABB)) == ContainmentType.Contains).ToList();
|
||||
}
|
||||
// y is the greatest axis...
|
||||
else if (mm.Y > mm.X && mm.Y > mm.Z)
|
||||
{
|
||||
// Calculate both boundaries.
|
||||
var lhs = new BoundingBox(m.Minimum, m.Maximum - new Vector3(0, mm.Y * 0.5F, 0));
|
||||
var rhs = new BoundingBox(m.Minimum + new Vector3(0, mm.Y * 0.5F, 0), m.Maximum);
|
||||
|
||||
// Set the grassInstances accordingly.
|
||||
pointGroup[0] = points
|
||||
.Where(p => lhs.Contains(GetGrassWorldPos(p.Position, batchAABB)) == ContainmentType.Contains).ToList();
|
||||
pointGroup[1] = points
|
||||
.Where(p => rhs.Contains(GetGrassWorldPos(p.Position, batchAABB)) == ContainmentType.Contains).ToList();
|
||||
}
|
||||
// z is the greatest axis...
|
||||
else if (mm.Z > mm.X && mm.Z > mm.Y)
|
||||
{
|
||||
// Calculate both boundaries.
|
||||
var lhs = new BoundingBox(m.Minimum, m.Maximum - new Vector3(0, 0, mm.Z * 0.5F));
|
||||
var rhs = new BoundingBox(m.Minimum + new Vector3(0, 0, mm.Z * 0.5F), m.Maximum);
|
||||
|
||||
// Set the grassInstances accordingly.
|
||||
pointGroup[0] = points
|
||||
.Where(p => lhs.Contains(GetGrassWorldPos(p.Position, batchAABB)) == ContainmentType.Contains).ToList();
|
||||
pointGroup[1] = points
|
||||
.Where(p => rhs.Contains(GetGrassWorldPos(p.Position, batchAABB)) == ContainmentType.Contains).ToList();
|
||||
}
|
||||
return pointGroup;
|
||||
}
|
||||
|
||||
private static BoundingBox GetNewGrassBounds(IReadOnlyList<rage__fwGrassInstanceListDef__InstanceData> newGrass, BoundingBox oldAABB)
|
||||
{
|
||||
var grassPositions = newGrass.Select(x => GetGrassWorldPos(x.Position, oldAABB)).ToArray();
|
||||
return BoundingBox.FromPoints(grassPositions).Expand(1f);
|
||||
}
|
||||
|
||||
private void SpawnInstances(
|
||||
IReadOnlyList<Vector3> positions,
|
||||
IReadOnlyList<Vector3> normals,
|
||||
ICollection<rage__fwGrassInstanceListDef__InstanceData> instanceList,
|
||||
BoundingBox instanceBounds,
|
||||
Color color,
|
||||
int ao,
|
||||
int scale,
|
||||
Vector3 pad,
|
||||
bool randomScale)
|
||||
{
|
||||
for (var i = 0; i < positions.Count; i++)
|
||||
{
|
||||
var pos = positions[i];
|
||||
// create the new instance.
|
||||
var newInstance = CreateNewInstance(normals[i], color, ao, scale, pad, randomScale);
|
||||
|
||||
// get the grass position of the new instance and add it to the
|
||||
// instance list
|
||||
var grassPosition = GetGrassPos(pos, instanceBounds);
|
||||
newInstance.Position = grassPosition;
|
||||
instanceList.Add(newInstance);
|
||||
}
|
||||
}
|
||||
|
||||
private rage__fwGrassInstanceListDef__InstanceData CreateNewInstance(Vector3 normal, Color color, int ao, int scale, Vector3 pad,
|
||||
bool randomScale = false)
|
||||
{
|
||||
//Vector3 pad = FloatUtil.ParseVector3String(PadTextBox.Text);
|
||||
//int scale = (int)ScaleNumericUpDown.Value;
|
||||
var rand = new Random();
|
||||
if (randomScale)
|
||||
scale = rand.Next(scale / 2, scale);
|
||||
var newInstance = new rage__fwGrassInstanceListDef__InstanceData
|
||||
{
|
||||
Ao = (byte)ao,
|
||||
Scale = (byte)scale,
|
||||
Color = new ArrayOfBytes3 { b0 = color.R, b1 = color.G, b2 = color.B },
|
||||
Pad = new ArrayOfBytes3 { b0 = (byte)pad.X, b1 = (byte)pad.Y, b2 = (byte)pad.Z },
|
||||
NormalX = (byte)((normal.X + 1) * 0.5F * 255F),
|
||||
NormalY = (byte)((normal.Y + 1) * 0.5F * 255F)
|
||||
};
|
||||
return newInstance;
|
||||
}
|
||||
|
||||
private rage__fwGrassInstanceListDef RecalcBatch(BoundingBox newInstanceBounds, YmapGrassInstanceBatch batch)
|
||||
{
|
||||
batch.AABBMax = newInstanceBounds.Maximum;
|
||||
batch.AABBMin = newInstanceBounds.Minimum;
|
||||
batch.Position = newInstanceBounds.Center();
|
||||
batch.Radius = newInstanceBounds.Radius();
|
||||
var b = batch.Batch;
|
||||
b.BatchAABB = new rage__spdAABB
|
||||
{
|
||||
min =
|
||||
new Vector4(newInstanceBounds.Minimum,
|
||||
0), // Let's pass the new stuff into the batchabb as well just because.
|
||||
max = new Vector4(newInstanceBounds.Maximum, 0)
|
||||
};
|
||||
return b;
|
||||
}
|
||||
|
||||
private void GetSpawns(
|
||||
Vector3 origin, Func<Vector3,
|
||||
SpaceRayIntersectResult> spawnRayFunc,
|
||||
ICollection<Vector3> positions,
|
||||
ICollection<Vector3> normals,
|
||||
float radius,
|
||||
int resolution = 28)
|
||||
{
|
||||
var rand = new Random();
|
||||
for (var i = 0; i < resolution; i++)
|
||||
{
|
||||
var randX = (float)rand.NextDouble(-radius, radius);
|
||||
var randY = (float)rand.NextDouble(-radius, radius);
|
||||
if (Math.Abs(randX) > 0 && Math.Abs(randY) > 0)
|
||||
{
|
||||
randX *= .7071f;
|
||||
randY *= .7071f;
|
||||
}
|
||||
var posOffset = origin + new Vector3(randX, randY, 2f);
|
||||
var spaceRay = spawnRayFunc.Invoke(posOffset);
|
||||
if (!spaceRay.Hit) continue;
|
||||
// not truly O(n^2) but may be slow...
|
||||
// actually just did some testing, not slow at all.
|
||||
if (IsPointBlockedByInstance(spaceRay.Position)) continue;
|
||||
normals.Add(spaceRay.Normal);
|
||||
positions.Add(spaceRay.Position);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<rage__fwGrassInstanceListDef__InstanceData> RecalculateInstances(
|
||||
List<rage__fwGrassInstanceListDef__InstanceData> instances,
|
||||
BoundingBox oldInstanceBounds,
|
||||
BoundingBox newInstanceBounds)
|
||||
{
|
||||
var refreshList = new List<rage__fwGrassInstanceListDef__InstanceData>();
|
||||
foreach (var inst in instances)
|
||||
{
|
||||
// Copy instance
|
||||
var copy =
|
||||
new rage__fwGrassInstanceListDef__InstanceData
|
||||
{
|
||||
Position = inst.Position,
|
||||
Ao = inst.Ao,
|
||||
Color = inst.Color,
|
||||
NormalX = inst.NormalX,
|
||||
NormalY = inst.NormalY,
|
||||
Pad = inst.Pad,
|
||||
Scale = inst.Scale
|
||||
};
|
||||
// get the position from where we would be in the old bounds, and move it to
|
||||
// the position it needs to be in the new bounds.
|
||||
var oldPos = GetGrassWorldPos(copy.Position, oldInstanceBounds);
|
||||
//var oldPos = oldInstanceBounds.min + oldInstanceBounds.Size * (grassPos * BatchVertMultiplier);
|
||||
copy.Position = GetGrassPos(oldPos, newInstanceBounds);
|
||||
refreshList.Add(copy);
|
||||
}
|
||||
instances = refreshList.ToList();
|
||||
return instances;
|
||||
}
|
||||
|
||||
private static BoundingBox EncapsulatePositions(IEnumerable<Vector3> positions, BoundingBox bounds)
|
||||
{
|
||||
foreach (var pos in positions)
|
||||
{
|
||||
var posBounds = new BoundingBox(pos - (GrassMinMax + 0.1f), pos + (GrassMinMax + 0.1f));
|
||||
bounds = bounds.Encapsulate(posBounds);
|
||||
}
|
||||
return bounds;
|
||||
}
|
||||
|
||||
private static ArrayOfUshorts3 GetGrassPos(Vector3 worldPos, BoundingBox batchAABB)
|
||||
{
|
||||
var offset = worldPos - batchAABB.Minimum;
|
||||
var size = batchAABB.Size();
|
||||
var percentage =
|
||||
new Vector3(
|
||||
offset.X / size.X,
|
||||
offset.Y / size.Y,
|
||||
offset.Z / size.Z
|
||||
);
|
||||
var instancePos = percentage / BatchVertMultiplier;
|
||||
return new ArrayOfUshorts3
|
||||
{
|
||||
u0 = (ushort)instancePos.X,
|
||||
u1 = (ushort)instancePos.Y,
|
||||
u2 = (ushort)instancePos.Z
|
||||
};
|
||||
}
|
||||
|
||||
private static Vector3 GetGrassWorldPos(ArrayOfUshorts3 grassPos, BoundingBox batchAABB)
|
||||
{
|
||||
return batchAABB.Minimum + batchAABB.Size() * (grassPos.XYZ() * BatchVertMultiplier);
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||
@@ -1548,7 +2046,6 @@ namespace CodeWalker.GameFiles
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||
public class YmapTimeCycleModifier
|
||||
{
|
||||
|
||||
@@ -241,7 +241,121 @@ namespace CodeWalker.GameFiles
|
||||
|
||||
}
|
||||
|
||||
public void AddArchetype(Archetype arch)
|
||||
{
|
||||
List<Archetype> allArchs = new List<Archetype>();
|
||||
|
||||
if (AllArchetypes != null)
|
||||
allArchs.AddRange(AllArchetypes);
|
||||
|
||||
allArchs.Add(arch);
|
||||
|
||||
AllArchetypes = allArchs.ToArray();
|
||||
}
|
||||
|
||||
public void RemoveArchetype(Archetype arch)
|
||||
{
|
||||
List<Archetype> allArchs = new List<Archetype>();
|
||||
|
||||
if (AllArchetypes != null)
|
||||
allArchs.AddRange(AllArchetypes);
|
||||
|
||||
if (allArchs.Contains(arch))
|
||||
allArchs.Remove(arch);
|
||||
|
||||
AllArchetypes = allArchs.ToArray();
|
||||
}
|
||||
|
||||
public byte[] Save()
|
||||
{
|
||||
MetaBuilder mb = new MetaBuilder();
|
||||
|
||||
var mdb = mb.EnsureBlock(MetaName.CMapTypes);
|
||||
|
||||
CMapTypes mapTypes = CMapTypes;
|
||||
|
||||
if((AllArchetypes != null) && (AllArchetypes.Length > 0))
|
||||
{
|
||||
MetaPOINTER[] archPtrs = new MetaPOINTER[AllArchetypes.Length];
|
||||
|
||||
for(int i=0; i<AllArchetypes.Length; i++)
|
||||
{
|
||||
var arch = AllArchetypes[i];
|
||||
arch._BaseArchetypeDef.extensions = mb.AddWrapperArrayPtr(arch.Extensions);
|
||||
}
|
||||
|
||||
for (int i = 0; i < archPtrs.Length; i++)
|
||||
{
|
||||
var arch = AllArchetypes[i];
|
||||
|
||||
if (arch is MloArchetype)
|
||||
{
|
||||
var mloArch = arch as MloArchetype;
|
||||
|
||||
mloArch._BaseMloArchetypeDef._MloArchetypeDef.entities = mb.AddWrapperArrayPtr(mloArch.entities);
|
||||
mloArch._BaseMloArchetypeDef._MloArchetypeDef.rooms = mb.AddWrapperArray(mloArch.rooms);
|
||||
mloArch._BaseMloArchetypeDef._MloArchetypeDef.portals = mb.AddWrapperArray(mloArch.portals);
|
||||
mloArch._BaseMloArchetypeDef._MloArchetypeDef.entitySets = mb.AddWrapperArray(mloArch.entitySets);
|
||||
mloArch._BaseMloArchetypeDef._MloArchetypeDef.timeCycleModifiers = mb.AddItemArrayPtr(MetaName.CTimeCycleModifier, mloArch.timeCycleModifiers);
|
||||
|
||||
archPtrs[i] = mb.AddItemPtr(MetaName.CMloArchetypeDef, mloArch.BaseMloArchetypeDef);
|
||||
|
||||
}
|
||||
else if (arch is TimeArchetype)
|
||||
{
|
||||
var timeArch = arch as TimeArchetype;
|
||||
archPtrs[i] = mb.AddItemPtr(MetaName.CTimeArchetypeDef, timeArch.TimeArchetypeDef);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
archPtrs[i] = mb.AddItemPtr(MetaName.CBaseArchetypeDef, arch.BaseArchetypeDef);
|
||||
}
|
||||
}
|
||||
|
||||
mapTypes.archetypes = mb.AddPointerArray(archPtrs);
|
||||
}
|
||||
|
||||
if((CompositeEntityTypes != null) && (CompositeEntityTypes.Length > 0))
|
||||
{
|
||||
MetaPOINTER[] cetPtrs = new MetaPOINTER[CompositeEntityTypes.Length] ;
|
||||
|
||||
for (int i = 0; i < cetPtrs.Length; i++)
|
||||
{
|
||||
var cet = CompositeEntityTypes[i];
|
||||
cetPtrs[i] = mb.AddItemPtr(MetaName.CCompositeEntityType, cet);
|
||||
}
|
||||
|
||||
mapTypes.compositeEntityTypes = mb.AddItemArrayPtr(MetaName.CCompositeEntityType, cetPtrs);
|
||||
}
|
||||
|
||||
mb.AddItem(MetaName.CMapTypes, mapTypes);
|
||||
|
||||
mb.AddStructureInfo(MetaName.CMapTypes);
|
||||
mb.AddStructureInfo(MetaName.CBaseArchetypeDef);
|
||||
mb.AddStructureInfo(MetaName.CMloArchetypeDef);
|
||||
mb.AddStructureInfo(MetaName.CTimeArchetypeDef);
|
||||
mb.AddStructureInfo(MetaName.CMloRoomDef);
|
||||
mb.AddStructureInfo(MetaName.CMloPortalDef);
|
||||
mb.AddStructureInfo(MetaName.CMloEntitySet);
|
||||
mb.AddStructureInfo(MetaName.CCompositeEntityType);
|
||||
|
||||
mb.AddEnumInfo((MetaName)1991964615);
|
||||
mb.AddEnumInfo((MetaName)1294270217);
|
||||
mb.AddEnumInfo((MetaName)1264241711);
|
||||
mb.AddEnumInfo((MetaName)648413703);
|
||||
mb.AddEnumInfo((MetaName)3573596290);
|
||||
mb.AddEnumInfo((MetaName)700327466);
|
||||
mb.AddEnumInfo((MetaName)193194928);
|
||||
mb.AddEnumInfo((MetaName)2266515059);
|
||||
|
||||
Meta = mb.GetMeta();
|
||||
|
||||
byte[] data = ResourceBuilder.Build(Meta, 2); //ymap is version 2...
|
||||
|
||||
return data;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -128,6 +128,9 @@ namespace CodeWalker.GameFiles
|
||||
{
|
||||
public override MetaName Type => MetaName.CMloArchetypeDef;
|
||||
|
||||
public CMloArchetypeDef _BaseMloArchetypeDef;
|
||||
public CMloArchetypeDef BaseMloArchetypeDef { get { return _BaseMloArchetypeDef; } set { _BaseMloArchetypeDef = value; } }
|
||||
|
||||
public CMloArchetypeDefData _MloArchetypeDef;
|
||||
public CMloArchetypeDefData MloArchetypeDef { get { return _MloArchetypeDef; } set { _MloArchetypeDef = value; } }
|
||||
|
||||
@@ -141,6 +144,7 @@ namespace CodeWalker.GameFiles
|
||||
{
|
||||
Ytyp = ytyp;
|
||||
InitVars(ref arch._BaseArchetypeDef);
|
||||
BaseMloArchetypeDef = arch;
|
||||
MloArchetypeDef = arch.MloArchetypeDef;
|
||||
}
|
||||
|
||||
|
||||
@@ -935,6 +935,10 @@ namespace CodeWalker.GameFiles
|
||||
[TC(typeof(EXP))] public struct ArrayOfUshorts3 //array of 3 ushorts
|
||||
{
|
||||
public ushort u0, u1, u2;
|
||||
public Vector3 XYZ()
|
||||
{
|
||||
return new Vector3(u0, u1, u2);
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
return u0.ToString() + ", " + u1.ToString() + ", " + u2.ToString();
|
||||
|
||||
@@ -1443,6 +1443,13 @@ namespace CodeWalker.GameFiles
|
||||
return Materials[type.Index];
|
||||
}
|
||||
|
||||
public static BoundsMaterialData GetMaterial(byte index)
|
||||
{
|
||||
if (Materials == null) return null;
|
||||
if ((int)index >= Materials.Count) return null;
|
||||
return Materials[index];
|
||||
}
|
||||
|
||||
public static string GetMaterialName(BoundsMaterialType type)
|
||||
{
|
||||
var m = GetMaterial(type);
|
||||
|
||||
@@ -718,16 +718,16 @@ namespace CodeWalker.GameFiles
|
||||
//public float TranslationX { get; set; }
|
||||
//public float TranslationY { get; set; }
|
||||
//public float TranslationZ { get; set; }
|
||||
public uint Unknown_1Ch { get; set; } // 0x00000000
|
||||
public float Unknown_20h { get; set; } // 1.0
|
||||
public float Unknown_24h { get; set; } // 1.0
|
||||
public float Unknown_28h { get; set; } // 1.0
|
||||
public float Unknown_2Ch { get; set; } // 1.0
|
||||
public ushort Unknown_30h { get; set; } //limb end index? IK chain?
|
||||
public uint Unknown_1Ch { get; set; } // 0x00000000 RHW?
|
||||
public float ScaleX { get; set; } // 1.0
|
||||
public float ScaleY { get; set; } // 1.0
|
||||
public float ScaleZ { get; set; } // 1.0
|
||||
public float Unknown_2Ch { get; set; } // 1.0 RHW?
|
||||
public ushort NextSiblingIndex { get; set; } //limb end index? IK chain?
|
||||
public short ParentIndex { get; set; }
|
||||
public uint Unknown_34h { get; set; } // 0x00000000
|
||||
public ulong NamePointer { get; set; }
|
||||
public ushort Unknown_40h { get; set; }
|
||||
public ushort Flags { get; set; }
|
||||
public ushort Unknown_42h { get; set; }
|
||||
public ushort Id { get; set; }
|
||||
public ushort Unknown_46h { get; set; }
|
||||
@@ -756,15 +756,15 @@ namespace CodeWalker.GameFiles
|
||||
//this.TranslationY = reader.ReadSingle();
|
||||
//this.TranslationZ = reader.ReadSingle();
|
||||
this.Unknown_1Ch = reader.ReadUInt32();
|
||||
this.Unknown_20h = reader.ReadSingle();
|
||||
this.Unknown_24h = reader.ReadSingle();
|
||||
this.Unknown_28h = reader.ReadSingle();
|
||||
this.ScaleX = reader.ReadSingle();
|
||||
this.ScaleY = reader.ReadSingle();
|
||||
this.ScaleZ = reader.ReadSingle();
|
||||
this.Unknown_2Ch = reader.ReadSingle();
|
||||
this.Unknown_30h = reader.ReadUInt16();
|
||||
this.NextSiblingIndex = reader.ReadUInt16();
|
||||
this.ParentIndex = reader.ReadInt16();
|
||||
this.Unknown_34h = reader.ReadUInt32();
|
||||
this.NamePointer = reader.ReadUInt64();
|
||||
this.Unknown_40h = reader.ReadUInt16();
|
||||
this.Flags = reader.ReadUInt16();
|
||||
this.Unknown_42h = reader.ReadUInt16();
|
||||
this.Id = reader.ReadUInt16();
|
||||
this.Unknown_46h = reader.ReadUInt16();
|
||||
@@ -796,15 +796,15 @@ namespace CodeWalker.GameFiles
|
||||
//writer.Write(this.TranslationY);
|
||||
//writer.Write(this.TranslationZ);
|
||||
writer.Write(this.Unknown_1Ch);
|
||||
writer.Write(this.Unknown_20h);
|
||||
writer.Write(this.Unknown_24h);
|
||||
writer.Write(this.Unknown_28h);
|
||||
writer.Write(this.ScaleX);
|
||||
writer.Write(this.ScaleY);
|
||||
writer.Write(this.ScaleZ);
|
||||
writer.Write(this.Unknown_2Ch);
|
||||
writer.Write(this.Unknown_30h);
|
||||
writer.Write(this.NextSiblingIndex);
|
||||
writer.Write(this.ParentIndex);
|
||||
writer.Write(this.Unknown_34h);
|
||||
writer.Write(this.NamePointer);
|
||||
writer.Write(this.Unknown_40h);
|
||||
writer.Write(this.Flags);
|
||||
writer.Write(this.Unknown_42h);
|
||||
writer.Write(this.Id);
|
||||
writer.Write(this.Unknown_46h);
|
||||
@@ -968,12 +968,8 @@ namespace CodeWalker.GameFiles
|
||||
public float Unknown_50h { get; set; } // -pi
|
||||
public float Unknown_54h { get; set; } // pi
|
||||
public float Unknown_58h { get; set; } // 1.0
|
||||
public float Unknown_5Ch { get; set; }
|
||||
public float Unknown_60h { get; set; }
|
||||
public float Unknown_64h { get; set; }
|
||||
public float Unknown_68h { get; set; }
|
||||
public float Unknown_6Ch { get; set; }
|
||||
public float Unknown_70h { get; set; }
|
||||
public Vector3 Min { get; set; }
|
||||
public Vector3 Max { get; set; }
|
||||
public float Unknown_74h { get; set; } // pi
|
||||
public float Unknown_78h { get; set; } // -pi
|
||||
public float Unknown_7Ch { get; set; } // pi
|
||||
|
||||
Reference in New Issue
Block a user