mirror of
https://mirror.ghproxy.com/https://github.com/dexyfex/CodeWalker
synced 2024-11-23 07:22:52 +08:00
Merge pull request #28 from sollaholla/master
added grass painting (second revision)
This commit is contained in:
commit
f520ca4991
@ -114,6 +114,7 @@
|
|||||||
<DesignTime>True</DesignTime>
|
<DesignTime>True</DesignTime>
|
||||||
<DependentUpon>Resources.resx</DependentUpon>
|
<DependentUpon>Resources.resx</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Utils\BoundingBoxes.cs" />
|
||||||
<Compile Include="Utils\Cache.cs" />
|
<Compile Include="Utils\Cache.cs" />
|
||||||
<Compile Include="Utils\Matrices.cs" />
|
<Compile Include="Utils\Matrices.cs" />
|
||||||
<Compile Include="Utils\Quaternions.cs" />
|
<Compile Include="Utils\Quaternions.cs" />
|
||||||
|
@ -7,6 +7,8 @@ using System.Linq;
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CodeWalker.Core.Utils;
|
||||||
|
using CodeWalker.World;
|
||||||
|
|
||||||
namespace CodeWalker.GameFiles
|
namespace CodeWalker.GameFiles
|
||||||
{
|
{
|
||||||
@ -925,6 +927,7 @@ namespace CodeWalker.GameFiles
|
|||||||
GrassInstanceBatches = batches.ToArray();
|
GrassInstanceBatches = batches.ToArray();
|
||||||
|
|
||||||
HasChanged = true;
|
HasChanged = true;
|
||||||
|
UpdateGrassPhysDict(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool RemoveGrassBatch(YmapGrassInstanceBatch batch)
|
public bool RemoveGrassBatch(YmapGrassInstanceBatch batch)
|
||||||
@ -949,6 +952,10 @@ namespace CodeWalker.GameFiles
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (batches.Count <= 0)
|
||||||
|
{
|
||||||
|
UpdateGrassPhysDict(false);
|
||||||
|
}
|
||||||
|
|
||||||
GrassInstanceBatches = batches.ToArray();
|
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)
|
private static uint SetBit(uint value, int bit)
|
||||||
{
|
{
|
||||||
@ -1493,6 +1521,8 @@ namespace CodeWalker.GameFiles
|
|||||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||||
public class YmapGrassInstanceBatch
|
public class YmapGrassInstanceBatch
|
||||||
{
|
{
|
||||||
|
private const float BatchVertMultiplier = 0.00001525878f;
|
||||||
|
|
||||||
public Archetype Archetype { get; set; } //cached by GameFileCache on loading...
|
public Archetype Archetype { get; set; } //cached by GameFileCache on loading...
|
||||||
public rage__fwGrassInstanceListDef Batch { get; set; }
|
public rage__fwGrassInstanceListDef Batch { get; set; }
|
||||||
public rage__fwGrassInstanceListDef__InstanceData[] Instances { get; set; }
|
public rage__fwGrassInstanceListDef__InstanceData[] Instances { get; set; }
|
||||||
@ -1504,10 +1534,478 @@ namespace CodeWalker.GameFiles
|
|||||||
public float Distance; //used for rendering
|
public float Distance; //used for rendering
|
||||||
public YmapFile Ymap { get; set; }
|
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()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return Batch.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))]
|
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||||
@ -1548,7 +2046,6 @@ namespace CodeWalker.GameFiles
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||||
public class YmapTimeCycleModifier
|
public class YmapTimeCycleModifier
|
||||||
{
|
{
|
||||||
|
@ -935,6 +935,10 @@ namespace CodeWalker.GameFiles
|
|||||||
[TC(typeof(EXP))] public struct ArrayOfUshorts3 //array of 3 ushorts
|
[TC(typeof(EXP))] public struct ArrayOfUshorts3 //array of 3 ushorts
|
||||||
{
|
{
|
||||||
public ushort u0, u1, u2;
|
public ushort u0, u1, u2;
|
||||||
|
public Vector3 XYZ()
|
||||||
|
{
|
||||||
|
return new Vector3(u0, u1, u2);
|
||||||
|
}
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return u0.ToString() + ", " + u1.ToString() + ", " + u2.ToString();
|
return u0.ToString() + ", " + u1.ToString() + ", " + u2.ToString();
|
||||||
|
@ -1443,6 +1443,13 @@ namespace CodeWalker.GameFiles
|
|||||||
return Materials[type.Index];
|
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)
|
public static string GetMaterialName(BoundsMaterialType type)
|
||||||
{
|
{
|
||||||
var m = GetMaterial(type);
|
var m = GetMaterial(type);
|
||||||
|
39
CodeWalker.Core/Utils/BoundingBoxes.cs
Normal file
39
CodeWalker.Core/Utils/BoundingBoxes.cs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
using System;
|
||||||
|
using SharpDX;
|
||||||
|
|
||||||
|
namespace CodeWalker.Core.Utils
|
||||||
|
{
|
||||||
|
public static class BoundingBoxExtensions
|
||||||
|
{
|
||||||
|
public static Vector3 Size(this BoundingBox bounds)
|
||||||
|
{
|
||||||
|
return new Vector3(
|
||||||
|
Math.Abs(bounds.Maximum.X - bounds.Minimum.X),
|
||||||
|
Math.Abs(bounds.Maximum.Y - bounds.Minimum.Y),
|
||||||
|
Math.Abs(bounds.Maximum.Z - bounds.Minimum.Z));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Vector3 Center(this BoundingBox bounds)
|
||||||
|
{
|
||||||
|
return (bounds.Minimum + bounds.Maximum) * 0.5F;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BoundingBox Encapsulate(this BoundingBox box, BoundingBox bounds)
|
||||||
|
{
|
||||||
|
box.Minimum = Vector3.Min(box.Minimum, bounds.Minimum);
|
||||||
|
box.Maximum = Vector3.Max(box.Maximum, bounds.Maximum);
|
||||||
|
return box;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float Radius(this BoundingBox box)
|
||||||
|
{
|
||||||
|
var extents = (box.Maximum - box.Minimum) * 0.5F;
|
||||||
|
return extents.Length();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BoundingBox Expand(this BoundingBox b, float amount)
|
||||||
|
{
|
||||||
|
return new BoundingBox(b.Minimum - Vector3.One * amount, b.Maximum + Vector3.One * amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,18 +4,17 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CodeWalker.GameFiles;
|
||||||
|
|
||||||
namespace CodeWalker
|
namespace CodeWalker
|
||||||
{
|
{
|
||||||
public static class Vectors
|
public static class Vectors
|
||||||
{
|
{
|
||||||
|
|
||||||
public static Vector3 XYZ(this Vector4 v)
|
public static Vector3 XYZ(this Vector4 v)
|
||||||
{
|
{
|
||||||
return new Vector3(v.X, v.Y, v.Z);
|
return new Vector3(v.X, v.Y, v.Z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static Vector3 Round(this Vector3 v)
|
public static Vector3 Round(this Vector3 v)
|
||||||
{
|
{
|
||||||
return new Vector3((float)Math.Round(v.X), (float)Math.Round(v.Y), (float)Math.Round(v.Z));
|
return new Vector3((float)Math.Round(v.X), (float)Math.Round(v.Y), (float)Math.Round(v.Z));
|
||||||
@ -25,7 +24,6 @@ namespace CodeWalker
|
|||||||
{
|
{
|
||||||
return new Vector4((float)Math.Floor(v.X), (float)Math.Floor(v.Y), (float)Math.Floor(v.Z), (float)Math.Floor(v.W));
|
return new Vector4((float)Math.Floor(v.X), (float)Math.Floor(v.Y), (float)Math.Floor(v.Z), (float)Math.Floor(v.W));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -138,6 +138,18 @@ namespace CodeWalker.Project.Panels
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (ymap.GrassInstanceBatches != null)
|
||||||
|
{
|
||||||
|
foreach (var batch in ymap.GrassInstanceBatches)
|
||||||
|
{
|
||||||
|
var ytyp = batch.Archetype?.Ytyp;
|
||||||
|
var ytypname = getYtypName(ytyp);
|
||||||
|
if (ytyp != null)
|
||||||
|
{
|
||||||
|
mapdeps[ytypname] = ytyp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sb.AppendLine(" <Item>");
|
sb.AppendLine(" <Item>");
|
||||||
sb.AppendLine(" <imapName>" + ymapname + "</imapName>");
|
sb.AppendLine(" <imapName>" + ymapname + "</imapName>");
|
||||||
|
670
Project/Panels/EditYmapGrassPanel.Designer.cs
generated
670
Project/Panels/EditYmapGrassPanel.Designer.cs
generated
@ -28,28 +28,642 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void InitializeComponent()
|
private void InitializeComponent()
|
||||||
{
|
{
|
||||||
|
this.components = new System.ComponentModel.Container();
|
||||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(EditYmapGrassPanel));
|
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(EditYmapGrassPanel));
|
||||||
|
this.tabControl1 = new System.Windows.Forms.TabControl();
|
||||||
|
this.GrassBatchTab = new System.Windows.Forms.TabPage();
|
||||||
|
this.groupBox1 = new System.Windows.Forms.GroupBox();
|
||||||
|
this.label12 = new System.Windows.Forms.Label();
|
||||||
|
this.label11 = new System.Windows.Forms.Label();
|
||||||
|
this.OrientToTerrainNumericUpDown = new System.Windows.Forms.NumericUpDown();
|
||||||
|
this.ScaleRangeTextBox = new System.Windows.Forms.TextBox();
|
||||||
|
this.label10 = new System.Windows.Forms.Label();
|
||||||
|
this.LodFadeRangeNumericUpDown = new System.Windows.Forms.NumericUpDown();
|
||||||
|
this.label9 = new System.Windows.Forms.Label();
|
||||||
|
this.LodFadeStartDistanceNumericUpDown = new System.Windows.Forms.NumericUpDown();
|
||||||
|
this.label6 = new System.Windows.Forms.Label();
|
||||||
|
this.LodDistNumericUpDown = new System.Windows.Forms.NumericUpDown();
|
||||||
|
this.BrushTab = new System.Windows.Forms.TabPage();
|
||||||
|
this.groupBox2 = new System.Windows.Forms.GroupBox();
|
||||||
|
this.GrassColorLabel = new System.Windows.Forms.Label();
|
||||||
|
this.label15 = new System.Windows.Forms.Label();
|
||||||
|
this.label4 = new System.Windows.Forms.Label();
|
||||||
|
this.AoNumericUpDown = new System.Windows.Forms.NumericUpDown();
|
||||||
|
this.PadTextBox = new System.Windows.Forms.TextBox();
|
||||||
|
this.ScaleNumericUpDown = new System.Windows.Forms.NumericUpDown();
|
||||||
|
this.label2 = new System.Windows.Forms.Label();
|
||||||
|
this.label3 = new System.Windows.Forms.Label();
|
||||||
|
this.RandomizeScaleCheckBox = new System.Windows.Forms.CheckBox();
|
||||||
|
this.brushSettingsGroupBox = new System.Windows.Forms.GroupBox();
|
||||||
|
this.DensityNumericUpDown = new System.Windows.Forms.NumericUpDown();
|
||||||
|
this.RadiusNumericUpDown = new System.Windows.Forms.NumericUpDown();
|
||||||
|
this.label5 = new System.Windows.Forms.Label();
|
||||||
|
this.radiusLabel = new System.Windows.Forms.Label();
|
||||||
|
this.OptimizeBatchButtonTooltip = new System.Windows.Forms.ToolTip(this.components);
|
||||||
|
this.OptimizeBatchButton = new System.Windows.Forms.Button();
|
||||||
|
this.ExtentsMinTextBox = new System.Windows.Forms.TextBox();
|
||||||
|
this.label14 = new System.Windows.Forms.Label();
|
||||||
|
this.ExtentsMaxTextBox = new System.Windows.Forms.TextBox();
|
||||||
|
this.label13 = new System.Windows.Forms.Label();
|
||||||
|
this.GrassDeleteButton = new System.Windows.Forms.Button();
|
||||||
|
this.GrassAddToProjectButton = new System.Windows.Forms.Button();
|
||||||
|
this.HashLabel = new System.Windows.Forms.Label();
|
||||||
|
this.ArchetypeNameTextBox = new System.Windows.Forms.TextBox();
|
||||||
|
this.label7 = new System.Windows.Forms.Label();
|
||||||
|
this.PositionTextBox = new System.Windows.Forms.TextBox();
|
||||||
this.label1 = new System.Windows.Forms.Label();
|
this.label1 = new System.Windows.Forms.Label();
|
||||||
|
this.GrassGoToButton = new System.Windows.Forms.Button();
|
||||||
|
this.label17 = new System.Windows.Forms.Label();
|
||||||
|
this.OptmizationThresholdNumericUpDown = new System.Windows.Forms.NumericUpDown();
|
||||||
|
this.BrushModeCheckBox = new System.Windows.Forms.CheckBox();
|
||||||
|
this.label8 = new System.Windows.Forms.Label();
|
||||||
|
this.tabControl1.SuspendLayout();
|
||||||
|
this.GrassBatchTab.SuspendLayout();
|
||||||
|
this.groupBox1.SuspendLayout();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.OrientToTerrainNumericUpDown)).BeginInit();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.LodFadeRangeNumericUpDown)).BeginInit();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.LodFadeStartDistanceNumericUpDown)).BeginInit();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.LodDistNumericUpDown)).BeginInit();
|
||||||
|
this.BrushTab.SuspendLayout();
|
||||||
|
this.groupBox2.SuspendLayout();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.AoNumericUpDown)).BeginInit();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.ScaleNumericUpDown)).BeginInit();
|
||||||
|
this.brushSettingsGroupBox.SuspendLayout();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.DensityNumericUpDown)).BeginInit();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.RadiusNumericUpDown)).BeginInit();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.OptmizationThresholdNumericUpDown)).BeginInit();
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
//
|
//
|
||||||
|
// tabControl1
|
||||||
|
//
|
||||||
|
this.tabControl1.Controls.Add(this.GrassBatchTab);
|
||||||
|
this.tabControl1.Controls.Add(this.BrushTab);
|
||||||
|
this.tabControl1.Location = new System.Drawing.Point(12, 65);
|
||||||
|
this.tabControl1.Name = "tabControl1";
|
||||||
|
this.tabControl1.SelectedIndex = 0;
|
||||||
|
this.tabControl1.Size = new System.Drawing.Size(486, 181);
|
||||||
|
this.tabControl1.TabIndex = 37;
|
||||||
|
//
|
||||||
|
// GrassBatchTab
|
||||||
|
//
|
||||||
|
this.GrassBatchTab.Controls.Add(this.groupBox1);
|
||||||
|
this.GrassBatchTab.Location = new System.Drawing.Point(4, 22);
|
||||||
|
this.GrassBatchTab.Name = "GrassBatchTab";
|
||||||
|
this.GrassBatchTab.Padding = new System.Windows.Forms.Padding(3);
|
||||||
|
this.GrassBatchTab.Size = new System.Drawing.Size(478, 155);
|
||||||
|
this.GrassBatchTab.TabIndex = 0;
|
||||||
|
this.GrassBatchTab.Text = "Grass Batch";
|
||||||
|
this.GrassBatchTab.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
|
// groupBox1
|
||||||
|
//
|
||||||
|
this.groupBox1.Controls.Add(this.label12);
|
||||||
|
this.groupBox1.Controls.Add(this.label11);
|
||||||
|
this.groupBox1.Controls.Add(this.OrientToTerrainNumericUpDown);
|
||||||
|
this.groupBox1.Controls.Add(this.ScaleRangeTextBox);
|
||||||
|
this.groupBox1.Controls.Add(this.label10);
|
||||||
|
this.groupBox1.Controls.Add(this.LodFadeRangeNumericUpDown);
|
||||||
|
this.groupBox1.Controls.Add(this.label9);
|
||||||
|
this.groupBox1.Controls.Add(this.LodFadeStartDistanceNumericUpDown);
|
||||||
|
this.groupBox1.Controls.Add(this.label6);
|
||||||
|
this.groupBox1.Controls.Add(this.LodDistNumericUpDown);
|
||||||
|
this.groupBox1.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
||||||
|
this.groupBox1.Location = new System.Drawing.Point(12, 6);
|
||||||
|
this.groupBox1.Name = "groupBox1";
|
||||||
|
this.groupBox1.Size = new System.Drawing.Size(460, 143);
|
||||||
|
this.groupBox1.TabIndex = 42;
|
||||||
|
this.groupBox1.TabStop = false;
|
||||||
|
this.groupBox1.Text = "Batch";
|
||||||
|
//
|
||||||
|
// label12
|
||||||
|
//
|
||||||
|
this.label12.AutoSize = true;
|
||||||
|
this.label12.Location = new System.Drawing.Point(197, 24);
|
||||||
|
this.label12.Name = "label12";
|
||||||
|
this.label12.Size = new System.Drawing.Size(69, 13);
|
||||||
|
this.label12.TabIndex = 47;
|
||||||
|
this.label12.Text = "Scale Range";
|
||||||
|
//
|
||||||
|
// label11
|
||||||
|
//
|
||||||
|
this.label11.AutoSize = true;
|
||||||
|
this.label11.Location = new System.Drawing.Point(8, 102);
|
||||||
|
this.label11.Name = "label11";
|
||||||
|
this.label11.Size = new System.Drawing.Size(87, 13);
|
||||||
|
this.label11.TabIndex = 7;
|
||||||
|
this.label11.Text = "Orient To Terrain";
|
||||||
|
//
|
||||||
|
// OrientToTerrainNumericUpDown
|
||||||
|
//
|
||||||
|
this.OrientToTerrainNumericUpDown.DecimalPlaces = 1;
|
||||||
|
this.OrientToTerrainNumericUpDown.Location = new System.Drawing.Point(121, 100);
|
||||||
|
this.OrientToTerrainNumericUpDown.Maximum = new decimal(new int[] {
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0});
|
||||||
|
this.OrientToTerrainNumericUpDown.Name = "OrientToTerrainNumericUpDown";
|
||||||
|
this.OrientToTerrainNumericUpDown.Size = new System.Drawing.Size(61, 20);
|
||||||
|
this.OrientToTerrainNumericUpDown.TabIndex = 6;
|
||||||
|
this.OrientToTerrainNumericUpDown.ValueChanged += new System.EventHandler(this.OrientToTerrainNumericUpDown_ValueChanged);
|
||||||
|
//
|
||||||
|
// ScaleRangeTextBox
|
||||||
|
//
|
||||||
|
this.ScaleRangeTextBox.Location = new System.Drawing.Point(270, 21);
|
||||||
|
this.ScaleRangeTextBox.Name = "ScaleRangeTextBox";
|
||||||
|
this.ScaleRangeTextBox.Size = new System.Drawing.Size(166, 20);
|
||||||
|
this.ScaleRangeTextBox.TabIndex = 46;
|
||||||
|
this.ScaleRangeTextBox.TextChanged += new System.EventHandler(this.ScaleRangeTextBox_TextChanged);
|
||||||
|
//
|
||||||
|
// label10
|
||||||
|
//
|
||||||
|
this.label10.AutoSize = true;
|
||||||
|
this.label10.Location = new System.Drawing.Point(8, 76);
|
||||||
|
this.label10.Name = "label10";
|
||||||
|
this.label10.Size = new System.Drawing.Size(107, 13);
|
||||||
|
this.label10.TabIndex = 5;
|
||||||
|
this.label10.Text = "Lod Inst Fade Range";
|
||||||
|
//
|
||||||
|
// LodFadeRangeNumericUpDown
|
||||||
|
//
|
||||||
|
this.LodFadeRangeNumericUpDown.DecimalPlaces = 4;
|
||||||
|
this.LodFadeRangeNumericUpDown.Increment = new decimal(new int[] {
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
65536});
|
||||||
|
this.LodFadeRangeNumericUpDown.Location = new System.Drawing.Point(121, 74);
|
||||||
|
this.LodFadeRangeNumericUpDown.Maximum = new decimal(new int[] {
|
||||||
|
99999,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0});
|
||||||
|
this.LodFadeRangeNumericUpDown.Name = "LodFadeRangeNumericUpDown";
|
||||||
|
this.LodFadeRangeNumericUpDown.Size = new System.Drawing.Size(61, 20);
|
||||||
|
this.LodFadeRangeNumericUpDown.TabIndex = 4;
|
||||||
|
this.LodFadeRangeNumericUpDown.ValueChanged += new System.EventHandler(this.LodFadeRangeNumericUpDown_ValueChanged);
|
||||||
|
//
|
||||||
|
// label9
|
||||||
|
//
|
||||||
|
this.label9.AutoSize = true;
|
||||||
|
this.label9.Location = new System.Drawing.Point(8, 50);
|
||||||
|
this.label9.Name = "label9";
|
||||||
|
this.label9.Size = new System.Drawing.Size(98, 13);
|
||||||
|
this.label9.TabIndex = 3;
|
||||||
|
this.label9.Text = "Lod Fade Start Dist";
|
||||||
|
//
|
||||||
|
// LodFadeStartDistanceNumericUpDown
|
||||||
|
//
|
||||||
|
this.LodFadeStartDistanceNumericUpDown.DecimalPlaces = 4;
|
||||||
|
this.LodFadeStartDistanceNumericUpDown.Location = new System.Drawing.Point(121, 48);
|
||||||
|
this.LodFadeStartDistanceNumericUpDown.Maximum = new decimal(new int[] {
|
||||||
|
99999,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0});
|
||||||
|
this.LodFadeStartDistanceNumericUpDown.Name = "LodFadeStartDistanceNumericUpDown";
|
||||||
|
this.LodFadeStartDistanceNumericUpDown.Size = new System.Drawing.Size(61, 20);
|
||||||
|
this.LodFadeStartDistanceNumericUpDown.TabIndex = 2;
|
||||||
|
this.LodFadeStartDistanceNumericUpDown.ValueChanged += new System.EventHandler(this.LodFadeStartDistanceNumericUpDown_ValueChanged);
|
||||||
|
//
|
||||||
|
// label6
|
||||||
|
//
|
||||||
|
this.label6.AutoSize = true;
|
||||||
|
this.label6.Location = new System.Drawing.Point(8, 24);
|
||||||
|
this.label6.Name = "label6";
|
||||||
|
this.label6.Size = new System.Drawing.Size(39, 13);
|
||||||
|
this.label6.TabIndex = 1;
|
||||||
|
this.label6.Text = "lodDist";
|
||||||
|
//
|
||||||
|
// LodDistNumericUpDown
|
||||||
|
//
|
||||||
|
this.LodDistNumericUpDown.Location = new System.Drawing.Point(121, 22);
|
||||||
|
this.LodDistNumericUpDown.Maximum = new decimal(new int[] {
|
||||||
|
99999,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0});
|
||||||
|
this.LodDistNumericUpDown.Name = "LodDistNumericUpDown";
|
||||||
|
this.LodDistNumericUpDown.Size = new System.Drawing.Size(61, 20);
|
||||||
|
this.LodDistNumericUpDown.TabIndex = 0;
|
||||||
|
this.LodDistNumericUpDown.ValueChanged += new System.EventHandler(this.LodDistNumericUpDown_ValueChanged);
|
||||||
|
//
|
||||||
|
// BrushTab
|
||||||
|
//
|
||||||
|
this.BrushTab.Controls.Add(this.groupBox2);
|
||||||
|
this.BrushTab.Controls.Add(this.brushSettingsGroupBox);
|
||||||
|
this.BrushTab.Location = new System.Drawing.Point(4, 22);
|
||||||
|
this.BrushTab.Name = "BrushTab";
|
||||||
|
this.BrushTab.Padding = new System.Windows.Forms.Padding(3);
|
||||||
|
this.BrushTab.Size = new System.Drawing.Size(478, 155);
|
||||||
|
this.BrushTab.TabIndex = 1;
|
||||||
|
this.BrushTab.Text = " Brush";
|
||||||
|
this.BrushTab.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
|
// groupBox2
|
||||||
|
//
|
||||||
|
this.groupBox2.Controls.Add(this.GrassColorLabel);
|
||||||
|
this.groupBox2.Controls.Add(this.label15);
|
||||||
|
this.groupBox2.Controls.Add(this.label4);
|
||||||
|
this.groupBox2.Controls.Add(this.AoNumericUpDown);
|
||||||
|
this.groupBox2.Controls.Add(this.PadTextBox);
|
||||||
|
this.groupBox2.Controls.Add(this.ScaleNumericUpDown);
|
||||||
|
this.groupBox2.Controls.Add(this.label2);
|
||||||
|
this.groupBox2.Controls.Add(this.label3);
|
||||||
|
this.groupBox2.Controls.Add(this.RandomizeScaleCheckBox);
|
||||||
|
this.groupBox2.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
||||||
|
this.groupBox2.Location = new System.Drawing.Point(191, 3);
|
||||||
|
this.groupBox2.Name = "groupBox2";
|
||||||
|
this.groupBox2.Size = new System.Drawing.Size(281, 146);
|
||||||
|
this.groupBox2.TabIndex = 39;
|
||||||
|
this.groupBox2.TabStop = false;
|
||||||
|
this.groupBox2.Text = "Instance Settings";
|
||||||
|
//
|
||||||
|
// GrassColorLabel
|
||||||
|
//
|
||||||
|
this.GrassColorLabel.BackColor = System.Drawing.Color.White;
|
||||||
|
this.GrassColorLabel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||||
|
this.GrassColorLabel.Location = new System.Drawing.Point(50, 22);
|
||||||
|
this.GrassColorLabel.Name = "GrassColorLabel";
|
||||||
|
this.GrassColorLabel.Size = new System.Drawing.Size(119, 21);
|
||||||
|
this.GrassColorLabel.TabIndex = 12;
|
||||||
|
this.GrassColorLabel.Click += new System.EventHandler(this.GrassColorLabel_Click);
|
||||||
|
//
|
||||||
|
// label15
|
||||||
|
//
|
||||||
|
this.label15.AutoSize = true;
|
||||||
|
this.label15.Location = new System.Drawing.Point(9, 106);
|
||||||
|
this.label15.Name = "label15";
|
||||||
|
this.label15.Size = new System.Drawing.Size(26, 13);
|
||||||
|
this.label15.TabIndex = 46;
|
||||||
|
this.label15.Text = "Pad";
|
||||||
|
//
|
||||||
|
// label4
|
||||||
|
//
|
||||||
|
this.label4.AutoSize = true;
|
||||||
|
this.label4.Location = new System.Drawing.Point(9, 79);
|
||||||
|
this.label4.Name = "label4";
|
||||||
|
this.label4.Size = new System.Drawing.Size(34, 13);
|
||||||
|
this.label4.TabIndex = 16;
|
||||||
|
this.label4.Text = "Scale";
|
||||||
|
//
|
||||||
|
// AoNumericUpDown
|
||||||
|
//
|
||||||
|
this.AoNumericUpDown.Location = new System.Drawing.Point(50, 51);
|
||||||
|
this.AoNumericUpDown.Maximum = new decimal(new int[] {
|
||||||
|
255,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0});
|
||||||
|
this.AoNumericUpDown.Name = "AoNumericUpDown";
|
||||||
|
this.AoNumericUpDown.Size = new System.Drawing.Size(120, 20);
|
||||||
|
this.AoNumericUpDown.TabIndex = 15;
|
||||||
|
this.AoNumericUpDown.Value = new decimal(new int[] {
|
||||||
|
255,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0});
|
||||||
|
//
|
||||||
|
// PadTextBox
|
||||||
|
//
|
||||||
|
this.PadTextBox.Location = new System.Drawing.Point(49, 103);
|
||||||
|
this.PadTextBox.Name = "PadTextBox";
|
||||||
|
this.PadTextBox.Size = new System.Drawing.Size(120, 20);
|
||||||
|
this.PadTextBox.TabIndex = 45;
|
||||||
|
this.PadTextBox.Text = "0, 0, 0";
|
||||||
|
//
|
||||||
|
// ScaleNumericUpDown
|
||||||
|
//
|
||||||
|
this.ScaleNumericUpDown.Location = new System.Drawing.Point(49, 77);
|
||||||
|
this.ScaleNumericUpDown.Maximum = new decimal(new int[] {
|
||||||
|
255,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0});
|
||||||
|
this.ScaleNumericUpDown.Name = "ScaleNumericUpDown";
|
||||||
|
this.ScaleNumericUpDown.Size = new System.Drawing.Size(120, 20);
|
||||||
|
this.ScaleNumericUpDown.TabIndex = 17;
|
||||||
|
this.ScaleNumericUpDown.Value = new decimal(new int[] {
|
||||||
|
255,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0});
|
||||||
|
//
|
||||||
|
// label2
|
||||||
|
//
|
||||||
|
this.label2.AutoSize = true;
|
||||||
|
this.label2.Location = new System.Drawing.Point(9, 53);
|
||||||
|
this.label2.Name = "label2";
|
||||||
|
this.label2.Size = new System.Drawing.Size(20, 13);
|
||||||
|
this.label2.TabIndex = 14;
|
||||||
|
this.label2.Text = "Ao";
|
||||||
|
//
|
||||||
|
// label3
|
||||||
|
//
|
||||||
|
this.label3.AutoSize = true;
|
||||||
|
this.label3.Location = new System.Drawing.Point(9, 26);
|
||||||
|
this.label3.Name = "label3";
|
||||||
|
this.label3.Size = new System.Drawing.Size(31, 13);
|
||||||
|
this.label3.TabIndex = 13;
|
||||||
|
this.label3.Text = "Color";
|
||||||
|
//
|
||||||
|
// RandomizeScaleCheckBox
|
||||||
|
//
|
||||||
|
this.RandomizeScaleCheckBox.AutoSize = true;
|
||||||
|
this.RandomizeScaleCheckBox.Location = new System.Drawing.Point(176, 79);
|
||||||
|
this.RandomizeScaleCheckBox.Name = "RandomizeScaleCheckBox";
|
||||||
|
this.RandomizeScaleCheckBox.Size = new System.Drawing.Size(66, 17);
|
||||||
|
this.RandomizeScaleCheckBox.TabIndex = 18;
|
||||||
|
this.RandomizeScaleCheckBox.Text = "Random";
|
||||||
|
this.RandomizeScaleCheckBox.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
|
// brushSettingsGroupBox
|
||||||
|
//
|
||||||
|
this.brushSettingsGroupBox.Controls.Add(this.DensityNumericUpDown);
|
||||||
|
this.brushSettingsGroupBox.Controls.Add(this.RadiusNumericUpDown);
|
||||||
|
this.brushSettingsGroupBox.Controls.Add(this.label5);
|
||||||
|
this.brushSettingsGroupBox.Controls.Add(this.radiusLabel);
|
||||||
|
this.brushSettingsGroupBox.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
||||||
|
this.brushSettingsGroupBox.Location = new System.Drawing.Point(8, 3);
|
||||||
|
this.brushSettingsGroupBox.Name = "brushSettingsGroupBox";
|
||||||
|
this.brushSettingsGroupBox.Size = new System.Drawing.Size(177, 146);
|
||||||
|
this.brushSettingsGroupBox.TabIndex = 38;
|
||||||
|
this.brushSettingsGroupBox.TabStop = false;
|
||||||
|
this.brushSettingsGroupBox.Text = "Brush Settings";
|
||||||
|
//
|
||||||
|
// DensityNumericUpDown
|
||||||
|
//
|
||||||
|
this.DensityNumericUpDown.Location = new System.Drawing.Point(76, 57);
|
||||||
|
this.DensityNumericUpDown.Maximum = new decimal(new int[] {
|
||||||
|
128,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0});
|
||||||
|
this.DensityNumericUpDown.Name = "DensityNumericUpDown";
|
||||||
|
this.DensityNumericUpDown.Size = new System.Drawing.Size(84, 20);
|
||||||
|
this.DensityNumericUpDown.TabIndex = 20;
|
||||||
|
this.DensityNumericUpDown.Value = new decimal(new int[] {
|
||||||
|
28,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0});
|
||||||
|
//
|
||||||
|
// RadiusNumericUpDown
|
||||||
|
//
|
||||||
|
this.RadiusNumericUpDown.DecimalPlaces = 2;
|
||||||
|
this.RadiusNumericUpDown.Increment = new decimal(new int[] {
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
131072});
|
||||||
|
this.RadiusNumericUpDown.Location = new System.Drawing.Point(76, 31);
|
||||||
|
this.RadiusNumericUpDown.Name = "RadiusNumericUpDown";
|
||||||
|
this.RadiusNumericUpDown.Size = new System.Drawing.Size(84, 20);
|
||||||
|
this.RadiusNumericUpDown.TabIndex = 11;
|
||||||
|
this.RadiusNumericUpDown.Value = new decimal(new int[] {
|
||||||
|
5,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0});
|
||||||
|
this.RadiusNumericUpDown.ValueChanged += new System.EventHandler(this.RadiusNumericUpDown_ValueChanged);
|
||||||
|
//
|
||||||
|
// label5
|
||||||
|
//
|
||||||
|
this.label5.AutoSize = true;
|
||||||
|
this.label5.Location = new System.Drawing.Point(21, 59);
|
||||||
|
this.label5.Name = "label5";
|
||||||
|
this.label5.Size = new System.Drawing.Size(42, 13);
|
||||||
|
this.label5.TabIndex = 19;
|
||||||
|
this.label5.Text = "Density";
|
||||||
|
//
|
||||||
|
// radiusLabel
|
||||||
|
//
|
||||||
|
this.radiusLabel.AutoSize = true;
|
||||||
|
this.radiusLabel.Location = new System.Drawing.Point(21, 33);
|
||||||
|
this.radiusLabel.Name = "radiusLabel";
|
||||||
|
this.radiusLabel.Size = new System.Drawing.Size(40, 13);
|
||||||
|
this.radiusLabel.TabIndex = 10;
|
||||||
|
this.radiusLabel.Text = "Radius";
|
||||||
|
//
|
||||||
|
// OptimizeBatchButton
|
||||||
|
//
|
||||||
|
this.OptimizeBatchButton.Location = new System.Drawing.Point(16, 280);
|
||||||
|
this.OptimizeBatchButton.Name = "OptimizeBatchButton";
|
||||||
|
this.OptimizeBatchButton.Size = new System.Drawing.Size(108, 24);
|
||||||
|
this.OptimizeBatchButton.TabIndex = 70;
|
||||||
|
this.OptimizeBatchButton.Text = "Optimize Batch";
|
||||||
|
this.OptimizeBatchButtonTooltip.SetToolTip(this.OptimizeBatchButton, "Will split your batch into multiple different batches based on the threshold. If " +
|
||||||
|
"your threshold is 5.0 then each batch will have a size of 5.0 meters.");
|
||||||
|
this.OptimizeBatchButton.UseVisualStyleBackColor = true;
|
||||||
|
this.OptimizeBatchButton.Click += new System.EventHandler(this.OptimizeBatchButton_Click);
|
||||||
|
//
|
||||||
|
// ExtentsMinTextBox
|
||||||
|
//
|
||||||
|
this.ExtentsMinTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.ExtentsMinTextBox.Location = new System.Drawing.Point(84, 319);
|
||||||
|
this.ExtentsMinTextBox.Name = "ExtentsMinTextBox";
|
||||||
|
this.ExtentsMinTextBox.ReadOnly = true;
|
||||||
|
this.ExtentsMinTextBox.Size = new System.Drawing.Size(380, 20);
|
||||||
|
this.ExtentsMinTextBox.TabIndex = 55;
|
||||||
|
//
|
||||||
|
// label14
|
||||||
|
//
|
||||||
|
this.label14.AutoSize = true;
|
||||||
|
this.label14.Location = new System.Drawing.Point(13, 349);
|
||||||
|
this.label14.Name = "label14";
|
||||||
|
this.label14.Size = new System.Drawing.Size(68, 13);
|
||||||
|
this.label14.TabIndex = 54;
|
||||||
|
this.label14.Text = "Extents Max:";
|
||||||
|
//
|
||||||
|
// ExtentsMaxTextBox
|
||||||
|
//
|
||||||
|
this.ExtentsMaxTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.ExtentsMaxTextBox.Location = new System.Drawing.Point(84, 345);
|
||||||
|
this.ExtentsMaxTextBox.Name = "ExtentsMaxTextBox";
|
||||||
|
this.ExtentsMaxTextBox.ReadOnly = true;
|
||||||
|
this.ExtentsMaxTextBox.Size = new System.Drawing.Size(380, 20);
|
||||||
|
this.ExtentsMaxTextBox.TabIndex = 53;
|
||||||
|
//
|
||||||
|
// label13
|
||||||
|
//
|
||||||
|
this.label13.AutoSize = true;
|
||||||
|
this.label13.Location = new System.Drawing.Point(13, 322);
|
||||||
|
this.label13.Name = "label13";
|
||||||
|
this.label13.Size = new System.Drawing.Size(65, 13);
|
||||||
|
this.label13.TabIndex = 52;
|
||||||
|
this.label13.Text = "Extents Min:";
|
||||||
|
//
|
||||||
|
// GrassDeleteButton
|
||||||
|
//
|
||||||
|
this.GrassDeleteButton.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
|
||||||
|
this.GrassDeleteButton.Location = new System.Drawing.Point(257, 397);
|
||||||
|
this.GrassDeleteButton.Name = "GrassDeleteButton";
|
||||||
|
this.GrassDeleteButton.Size = new System.Drawing.Size(95, 23);
|
||||||
|
this.GrassDeleteButton.TabIndex = 51;
|
||||||
|
this.GrassDeleteButton.Text = "Delete Batch";
|
||||||
|
this.GrassDeleteButton.UseVisualStyleBackColor = true;
|
||||||
|
this.GrassDeleteButton.Click += new System.EventHandler(this.GrassDeleteButton_Click);
|
||||||
|
//
|
||||||
|
// GrassAddToProjectButton
|
||||||
|
//
|
||||||
|
this.GrassAddToProjectButton.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
|
||||||
|
this.GrassAddToProjectButton.Location = new System.Drawing.Point(159, 397);
|
||||||
|
this.GrassAddToProjectButton.Name = "GrassAddToProjectButton";
|
||||||
|
this.GrassAddToProjectButton.Size = new System.Drawing.Size(95, 23);
|
||||||
|
this.GrassAddToProjectButton.TabIndex = 50;
|
||||||
|
this.GrassAddToProjectButton.Text = "Add to Project";
|
||||||
|
this.GrassAddToProjectButton.UseVisualStyleBackColor = true;
|
||||||
|
this.GrassAddToProjectButton.Click += new System.EventHandler(this.GrassAddToProjectButton_Click);
|
||||||
|
//
|
||||||
|
// HashLabel
|
||||||
|
//
|
||||||
|
this.HashLabel.AutoSize = true;
|
||||||
|
this.HashLabel.Location = new System.Drawing.Point(254, 14);
|
||||||
|
this.HashLabel.Name = "HashLabel";
|
||||||
|
this.HashLabel.Size = new System.Drawing.Size(44, 13);
|
||||||
|
this.HashLabel.TabIndex = 61;
|
||||||
|
this.HashLabel.Text = "Hash: 0";
|
||||||
|
//
|
||||||
|
// ArchetypeNameTextBox
|
||||||
|
//
|
||||||
|
this.ArchetypeNameTextBox.Location = new System.Drawing.Point(66, 12);
|
||||||
|
this.ArchetypeNameTextBox.Name = "ArchetypeNameTextBox";
|
||||||
|
this.ArchetypeNameTextBox.Size = new System.Drawing.Size(144, 20);
|
||||||
|
this.ArchetypeNameTextBox.TabIndex = 60;
|
||||||
|
this.ArchetypeNameTextBox.TextChanged += new System.EventHandler(this.ArchetypeNameTextBox_TextChanged);
|
||||||
|
//
|
||||||
|
// label7
|
||||||
|
//
|
||||||
|
this.label7.AutoSize = true;
|
||||||
|
this.label7.Location = new System.Drawing.Point(16, 15);
|
||||||
|
this.label7.Name = "label7";
|
||||||
|
this.label7.Size = new System.Drawing.Size(35, 13);
|
||||||
|
this.label7.TabIndex = 59;
|
||||||
|
this.label7.Text = "Name";
|
||||||
|
//
|
||||||
|
// PositionTextBox
|
||||||
|
//
|
||||||
|
this.PositionTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.PositionTextBox.Location = new System.Drawing.Point(66, 39);
|
||||||
|
this.PositionTextBox.Name = "PositionTextBox";
|
||||||
|
this.PositionTextBox.ReadOnly = true;
|
||||||
|
this.PositionTextBox.Size = new System.Drawing.Size(275, 20);
|
||||||
|
this.PositionTextBox.TabIndex = 58;
|
||||||
|
//
|
||||||
// label1
|
// label1
|
||||||
//
|
//
|
||||||
this.label1.AutoSize = true;
|
this.label1.AutoSize = true;
|
||||||
this.label1.Location = new System.Drawing.Point(52, 52);
|
this.label1.Location = new System.Drawing.Point(16, 42);
|
||||||
this.label1.Name = "label1";
|
this.label1.Name = "label1";
|
||||||
this.label1.Size = new System.Drawing.Size(105, 13);
|
this.label1.Size = new System.Drawing.Size(44, 13);
|
||||||
this.label1.TabIndex = 0;
|
this.label1.TabIndex = 57;
|
||||||
this.label1.Text = "Grass editing TODO!";
|
this.label1.Text = "Position";
|
||||||
|
//
|
||||||
|
// GrassGoToButton
|
||||||
|
//
|
||||||
|
this.GrassGoToButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.GrassGoToButton.Location = new System.Drawing.Point(347, 36);
|
||||||
|
this.GrassGoToButton.Name = "GrassGoToButton";
|
||||||
|
this.GrassGoToButton.Size = new System.Drawing.Size(56, 23);
|
||||||
|
this.GrassGoToButton.TabIndex = 56;
|
||||||
|
this.GrassGoToButton.Text = "Go To";
|
||||||
|
this.GrassGoToButton.UseVisualStyleBackColor = true;
|
||||||
|
this.GrassGoToButton.Click += new System.EventHandler(this.GrassGoToButton_Click);
|
||||||
|
//
|
||||||
|
// label17
|
||||||
|
//
|
||||||
|
this.label17.AutoSize = true;
|
||||||
|
this.label17.Location = new System.Drawing.Point(130, 286);
|
||||||
|
this.label17.Name = "label17";
|
||||||
|
this.label17.Size = new System.Drawing.Size(54, 13);
|
||||||
|
this.label17.TabIndex = 71;
|
||||||
|
this.label17.Text = "Threshold";
|
||||||
|
//
|
||||||
|
// OptmizationThresholdNumericUpDown
|
||||||
|
//
|
||||||
|
this.OptmizationThresholdNumericUpDown.Location = new System.Drawing.Point(190, 284);
|
||||||
|
this.OptmizationThresholdNumericUpDown.Maximum = new decimal(new int[] {
|
||||||
|
999999,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0});
|
||||||
|
this.OptmizationThresholdNumericUpDown.Name = "OptmizationThresholdNumericUpDown";
|
||||||
|
this.OptmizationThresholdNumericUpDown.Size = new System.Drawing.Size(51, 20);
|
||||||
|
this.OptmizationThresholdNumericUpDown.TabIndex = 69;
|
||||||
|
this.OptmizationThresholdNumericUpDown.Value = new decimal(new int[] {
|
||||||
|
5,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0});
|
||||||
|
//
|
||||||
|
// BrushModeCheckBox
|
||||||
|
//
|
||||||
|
this.BrushModeCheckBox.AutoSize = true;
|
||||||
|
this.BrushModeCheckBox.Location = new System.Drawing.Point(16, 252);
|
||||||
|
this.BrushModeCheckBox.Name = "BrushModeCheckBox";
|
||||||
|
this.BrushModeCheckBox.Size = new System.Drawing.Size(83, 17);
|
||||||
|
this.BrushModeCheckBox.TabIndex = 68;
|
||||||
|
this.BrushModeCheckBox.Text = "Brush Mode";
|
||||||
|
this.BrushModeCheckBox.UseVisualStyleBackColor = true;
|
||||||
|
this.BrushModeCheckBox.CheckedChanged += new System.EventHandler(this.BrushModeCheckBox_CheckedChanged);
|
||||||
|
//
|
||||||
|
// label8
|
||||||
|
//
|
||||||
|
this.label8.AutoSize = true;
|
||||||
|
this.label8.Location = new System.Drawing.Point(211, 14);
|
||||||
|
this.label8.Name = "label8";
|
||||||
|
this.label8.Size = new System.Drawing.Size(24, 13);
|
||||||
|
this.label8.TabIndex = 72;
|
||||||
|
this.label8.Text = ".ydr";
|
||||||
|
this.label8.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
||||||
//
|
//
|
||||||
// EditYmapGrassPanel
|
// EditYmapGrassPanel
|
||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
this.ClientSize = new System.Drawing.Size(554, 355);
|
this.ClientSize = new System.Drawing.Size(510, 443);
|
||||||
|
this.Controls.Add(this.label8);
|
||||||
|
this.Controls.Add(this.label17);
|
||||||
|
this.Controls.Add(this.OptmizationThresholdNumericUpDown);
|
||||||
|
this.Controls.Add(this.OptimizeBatchButton);
|
||||||
|
this.Controls.Add(this.BrushModeCheckBox);
|
||||||
|
this.Controls.Add(this.HashLabel);
|
||||||
|
this.Controls.Add(this.ExtentsMinTextBox);
|
||||||
|
this.Controls.Add(this.ArchetypeNameTextBox);
|
||||||
|
this.Controls.Add(this.ExtentsMaxTextBox);
|
||||||
|
this.Controls.Add(this.label7);
|
||||||
|
this.Controls.Add(this.PositionTextBox);
|
||||||
this.Controls.Add(this.label1);
|
this.Controls.Add(this.label1);
|
||||||
|
this.Controls.Add(this.GrassGoToButton);
|
||||||
|
this.Controls.Add(this.label14);
|
||||||
|
this.Controls.Add(this.label13);
|
||||||
|
this.Controls.Add(this.GrassDeleteButton);
|
||||||
|
this.Controls.Add(this.tabControl1);
|
||||||
|
this.Controls.Add(this.GrassAddToProjectButton);
|
||||||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
||||||
this.Name = "EditYmapGrassPanel";
|
this.Name = "EditYmapGrassPanel";
|
||||||
this.Text = "Grass Batch";
|
this.Text = "Grass Batch";
|
||||||
|
this.tabControl1.ResumeLayout(false);
|
||||||
|
this.GrassBatchTab.ResumeLayout(false);
|
||||||
|
this.groupBox1.ResumeLayout(false);
|
||||||
|
this.groupBox1.PerformLayout();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.OrientToTerrainNumericUpDown)).EndInit();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.LodFadeRangeNumericUpDown)).EndInit();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.LodFadeStartDistanceNumericUpDown)).EndInit();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.LodDistNumericUpDown)).EndInit();
|
||||||
|
this.BrushTab.ResumeLayout(false);
|
||||||
|
this.groupBox2.ResumeLayout(false);
|
||||||
|
this.groupBox2.PerformLayout();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.AoNumericUpDown)).EndInit();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.ScaleNumericUpDown)).EndInit();
|
||||||
|
this.brushSettingsGroupBox.ResumeLayout(false);
|
||||||
|
this.brushSettingsGroupBox.PerformLayout();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.DensityNumericUpDown)).EndInit();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.RadiusNumericUpDown)).EndInit();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.OptmizationThresholdNumericUpDown)).EndInit();
|
||||||
this.ResumeLayout(false);
|
this.ResumeLayout(false);
|
||||||
this.PerformLayout();
|
this.PerformLayout();
|
||||||
|
|
||||||
@ -57,6 +671,52 @@
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
private System.Windows.Forms.TabControl tabControl1;
|
||||||
|
private System.Windows.Forms.TabPage GrassBatchTab;
|
||||||
|
private System.Windows.Forms.TabPage BrushTab;
|
||||||
|
private System.Windows.Forms.GroupBox brushSettingsGroupBox;
|
||||||
|
private System.Windows.Forms.NumericUpDown RadiusNumericUpDown;
|
||||||
|
private System.Windows.Forms.Label radiusLabel;
|
||||||
|
private System.Windows.Forms.NumericUpDown DensityNumericUpDown;
|
||||||
|
private System.Windows.Forms.Label label5;
|
||||||
|
private System.Windows.Forms.GroupBox groupBox1;
|
||||||
|
private System.Windows.Forms.NumericUpDown LodDistNumericUpDown;
|
||||||
|
private System.Windows.Forms.Label label6;
|
||||||
|
private System.Windows.Forms.Label label9;
|
||||||
|
private System.Windows.Forms.NumericUpDown LodFadeStartDistanceNumericUpDown;
|
||||||
|
private System.Windows.Forms.Label label10;
|
||||||
|
private System.Windows.Forms.NumericUpDown LodFadeRangeNumericUpDown;
|
||||||
|
private System.Windows.Forms.Label label11;
|
||||||
|
private System.Windows.Forms.NumericUpDown OrientToTerrainNumericUpDown;
|
||||||
|
private System.Windows.Forms.Label label12;
|
||||||
|
private System.Windows.Forms.TextBox ScaleRangeTextBox;
|
||||||
|
private System.Windows.Forms.GroupBox groupBox2;
|
||||||
|
private System.Windows.Forms.Label label15;
|
||||||
|
private System.Windows.Forms.TextBox PadTextBox;
|
||||||
|
private System.Windows.Forms.Label label3;
|
||||||
|
private System.Windows.Forms.Label GrassColorLabel;
|
||||||
|
private System.Windows.Forms.CheckBox RandomizeScaleCheckBox;
|
||||||
|
private System.Windows.Forms.Label label2;
|
||||||
|
private System.Windows.Forms.NumericUpDown ScaleNumericUpDown;
|
||||||
|
private System.Windows.Forms.NumericUpDown AoNumericUpDown;
|
||||||
|
private System.Windows.Forms.Label label4;
|
||||||
|
private System.Windows.Forms.ToolTip OptimizeBatchButtonTooltip;
|
||||||
|
private System.Windows.Forms.TextBox ExtentsMinTextBox;
|
||||||
|
private System.Windows.Forms.Label label14;
|
||||||
|
private System.Windows.Forms.TextBox ExtentsMaxTextBox;
|
||||||
|
private System.Windows.Forms.Label label13;
|
||||||
|
private System.Windows.Forms.Button GrassDeleteButton;
|
||||||
|
private System.Windows.Forms.Button GrassAddToProjectButton;
|
||||||
|
private System.Windows.Forms.Label HashLabel;
|
||||||
|
private System.Windows.Forms.TextBox ArchetypeNameTextBox;
|
||||||
|
private System.Windows.Forms.Label label7;
|
||||||
|
private System.Windows.Forms.TextBox PositionTextBox;
|
||||||
private System.Windows.Forms.Label label1;
|
private System.Windows.Forms.Label label1;
|
||||||
|
private System.Windows.Forms.Button GrassGoToButton;
|
||||||
|
private System.Windows.Forms.Label label17;
|
||||||
|
private System.Windows.Forms.NumericUpDown OptmizationThresholdNumericUpDown;
|
||||||
|
private System.Windows.Forms.Button OptimizeBatchButton;
|
||||||
|
private System.Windows.Forms.CheckBox BrushModeCheckBox;
|
||||||
|
private System.Windows.Forms.Label label8;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,22 +1,30 @@
|
|||||||
using CodeWalker.GameFiles;
|
using System;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Data;
|
|
||||||
using System.Drawing;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using CodeWalker.GameFiles;
|
||||||
|
using CodeWalker.World;
|
||||||
|
using SharpDX;
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// - COMPLETED -- Optimization feature.
|
||||||
|
// - COMPLETED -- Remove grass instances using CTRL + SHIFT + LMB
|
||||||
|
|
||||||
|
// - Better gizmo for grass brush (like a circle with a little line in the middle sticking upwards)
|
||||||
|
// - Maybe some kind of auto coloring system? I've noticed that mostly all grass in GTA inherits it's color from the surface it's on.
|
||||||
|
// - Grass area fill (generate grass on ydr based on colision materials?)
|
||||||
|
// - Need to have a way to erase instances from other batches in the current batches ymap.
|
||||||
|
// if we optimize our instances, we'd have to go through each batch to erase, this is very monotonous.
|
||||||
|
|
||||||
|
// BUG
|
||||||
|
// - I've added a "zoom" kind of feature when hitting the goto button, but when the bounds of the
|
||||||
|
// grass batch are 0, the zoom of the camera is set to 0, which causes the end-user to have to scroll
|
||||||
|
// out a lot in order to use any movement controls. I will need to clamp that to a minimum value.
|
||||||
|
|
||||||
namespace CodeWalker.Project.Panels
|
namespace CodeWalker.Project.Panels
|
||||||
{
|
{
|
||||||
public partial class EditYmapGrassPanel : ProjectPanel
|
public partial class EditYmapGrassPanel : ProjectPanel
|
||||||
{
|
{
|
||||||
public ProjectForm ProjectForm;
|
public ProjectForm ProjectForm;
|
||||||
public YmapGrassInstanceBatch CurrentBatch { get; set; }
|
|
||||||
|
|
||||||
//private bool populatingui = false;
|
|
||||||
|
|
||||||
public EditYmapGrassPanel(ProjectForm owner)
|
public EditYmapGrassPanel(ProjectForm owner)
|
||||||
{
|
{
|
||||||
@ -24,12 +32,44 @@ namespace CodeWalker.Project.Panels
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public YmapGrassInstanceBatch CurrentBatch { get; set; }
|
||||||
|
|
||||||
|
#region Form
|
||||||
|
|
||||||
public void SetBatch(YmapGrassInstanceBatch batch)
|
public void SetBatch(YmapGrassInstanceBatch batch)
|
||||||
{
|
{
|
||||||
CurrentBatch = batch;
|
CurrentBatch = batch;
|
||||||
Tag = batch;
|
Tag = batch;
|
||||||
LoadGrassBatch();
|
|
||||||
UpdateFormTitle();
|
UpdateFormTitle();
|
||||||
|
UpdateControls();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateControls()
|
||||||
|
{
|
||||||
|
if (ProjectForm?.CurrentProjectFile == null) return;
|
||||||
|
if (ProjectForm.GrassBatchExistsInProject(CurrentBatch))
|
||||||
|
{
|
||||||
|
GrassAddToProjectButton.Enabled = false;
|
||||||
|
GrassDeleteButton.Enabled = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GrassAddToProjectButton.Enabled = true;
|
||||||
|
GrassDeleteButton.Enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArchetypeNameTextBox.Text = CurrentBatch.Batch.archetypeName.ToString();
|
||||||
|
PositionTextBox.Text = FloatUtil.GetVector3String(CurrentBatch.Position);
|
||||||
|
LodDistNumericUpDown.Value = CurrentBatch.Batch.lodDist;
|
||||||
|
LodFadeRangeNumericUpDown.Value = (decimal) CurrentBatch.Batch.LodInstFadeRange;
|
||||||
|
LodFadeStartDistanceNumericUpDown.Value = (decimal) CurrentBatch.Batch.LodFadeStartDist;
|
||||||
|
ScaleRangeTextBox.Text = FloatUtil.GetVector3String(CurrentBatch.Batch.ScaleRange);
|
||||||
|
OrientToTerrainNumericUpDown.Value = (decimal)CurrentBatch.Batch.OrientToTerrain;
|
||||||
|
OptmizationThresholdNumericUpDown.Value = 15;
|
||||||
|
BrushModeCheckBox.Checked = CurrentBatch.BrushEnabled;
|
||||||
|
RadiusNumericUpDown.Value = (decimal)CurrentBatch.BrushRadius;
|
||||||
|
ExtentsMinTextBox.Text = FloatUtil.GetVector3String(CurrentBatch.AABBMin);
|
||||||
|
ExtentsMaxTextBox.Text = FloatUtil.GetVector3String(CurrentBatch.AABBMax);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateFormTitle()
|
private void UpdateFormTitle()
|
||||||
@ -37,11 +77,225 @@ namespace CodeWalker.Project.Panels
|
|||||||
Text = CurrentBatch?.Batch.archetypeName.ToString() ?? "Grass Batch";
|
Text = CurrentBatch?.Batch.archetypeName.ToString() ?? "Grass Batch";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Events
|
||||||
|
|
||||||
private void LoadGrassBatch()
|
#region BrushSettings
|
||||||
|
|
||||||
|
private void GrassGoToButton_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
|
if (CurrentBatch == null) return;
|
||||||
|
ProjectForm.WorldForm?.GoToPosition(CurrentBatch.Position, CurrentBatch.AABBMax - CurrentBatch.AABBMin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void GrassAddToProjectButton_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
ProjectForm.SetProjectItem(CurrentBatch);
|
||||||
|
ProjectForm.AddGrassBatchToProject();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GrassDeleteButton_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
ProjectForm.SetProjectItem(CurrentBatch);
|
||||||
|
var ymap = CurrentBatch?.Ymap;
|
||||||
|
if (!ProjectForm.DeleteGrassBatch()) return;
|
||||||
|
|
||||||
|
ymap?.CalcExtents(); // Recalculate the extents after deleting the grass batch.
|
||||||
|
ProjectForm.WorldForm.SelectItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GrassColorLabel_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
var colDiag = new ColorDialog {Color = GrassColorLabel.BackColor};
|
||||||
|
if (colDiag.ShowDialog(this) == DialogResult.OK)
|
||||||
|
GrassColorLabel.BackColor = colDiag.Color;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BrushModeCheckBox_CheckedChanged(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (CurrentBatch == null) return;
|
||||||
|
CurrentBatch.BrushEnabled = BrushModeCheckBox.Checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RadiusNumericUpDown_ValueChanged(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (CurrentBatch == null) return;
|
||||||
|
CurrentBatch.BrushRadius = (float)RadiusNumericUpDown.Value;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Batch Settings
|
||||||
|
|
||||||
|
private void ArchetypeNameTextBox_TextChanged(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
var archetypeHash = JenkHash.GenHash(ArchetypeNameTextBox.Text);
|
||||||
|
var archetype = ProjectForm.GameFileCache.GetArchetype(archetypeHash);
|
||||||
|
if (archetype == null)
|
||||||
|
{
|
||||||
|
HashLabel.Text = $@"Hash: {archetypeHash} (invalid)";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CurrentBatch.Archetype = archetype;
|
||||||
|
var b = CurrentBatch.Batch;
|
||||||
|
b.archetypeName = archetypeHash;
|
||||||
|
CurrentBatch.Batch = b;
|
||||||
|
ProjectForm.WorldForm.UpdateGrassBatchGraphics(CurrentBatch);
|
||||||
|
HashLabel.Text = $@"Hash: {archetypeHash}";
|
||||||
|
UpdateFormTitle();
|
||||||
|
CurrentBatch.HasChanged = true;
|
||||||
|
ProjectForm.SetGrassBatchHasChanged(false);
|
||||||
|
ProjectForm.SetYmapHasChanged(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LodDistNumericUpDown_ValueChanged(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
var batch = CurrentBatch.Batch;
|
||||||
|
batch.lodDist = (uint) LodDistNumericUpDown.Value;
|
||||||
|
CurrentBatch.Batch = batch;
|
||||||
|
ProjectForm.WorldForm.UpdateGrassBatchGraphics(CurrentBatch);
|
||||||
|
ProjectForm.SetYmapHasChanged(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LodFadeStartDistanceNumericUpDown_ValueChanged(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
var batch = CurrentBatch.Batch;
|
||||||
|
batch.LodFadeStartDist = (float) LodFadeStartDistanceNumericUpDown.Value;
|
||||||
|
CurrentBatch.Batch = batch;
|
||||||
|
ProjectForm.WorldForm.UpdateGrassBatchGraphics(CurrentBatch);
|
||||||
|
ProjectForm.SetYmapHasChanged(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LodFadeRangeNumericUpDown_ValueChanged(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
var batch = CurrentBatch.Batch;
|
||||||
|
batch.LodInstFadeRange = (float) LodFadeRangeNumericUpDown.Value;
|
||||||
|
CurrentBatch.Batch = batch;
|
||||||
|
ProjectForm.WorldForm.UpdateGrassBatchGraphics(CurrentBatch);
|
||||||
|
ProjectForm.SetYmapHasChanged(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OrientToTerrainNumericUpDown_ValueChanged(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
var batch = CurrentBatch.Batch;
|
||||||
|
batch.OrientToTerrain = (float) OrientToTerrainNumericUpDown.Value;
|
||||||
|
CurrentBatch.Batch = batch;
|
||||||
|
ProjectForm.WorldForm.UpdateGrassBatchGraphics(CurrentBatch);
|
||||||
|
ProjectForm.SetYmapHasChanged(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ScaleRangeTextBox_TextChanged(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
var batch = CurrentBatch.Batch;
|
||||||
|
var v = FloatUtil.ParseVector3String(ScaleRangeTextBox.Text);
|
||||||
|
batch.ScaleRange = v;
|
||||||
|
CurrentBatch.Batch = batch;
|
||||||
|
ProjectForm.WorldForm.UpdateGrassBatchGraphics(CurrentBatch);
|
||||||
|
ProjectForm.SetYmapHasChanged(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OptimizeBatchButton_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (CurrentBatch.Instances == null || CurrentBatch.Instances.Length <= 0) return;
|
||||||
|
lock (ProjectForm.WorldForm.RenderSyncRoot)
|
||||||
|
{
|
||||||
|
var newBatches = CurrentBatch?.OptimizeInstances(CurrentBatch, (float)OptmizationThresholdNumericUpDown.Value);
|
||||||
|
if (newBatches == null || newBatches.Length <= 0) return;
|
||||||
|
|
||||||
|
// Remove our batch from the ymap
|
||||||
|
CurrentBatch.Ymap.RemoveGrassBatch(CurrentBatch);
|
||||||
|
foreach (var batch in newBatches)
|
||||||
|
{
|
||||||
|
var b = batch.Batch;
|
||||||
|
b.lodDist = CurrentBatch.Batch.lodDist;
|
||||||
|
b.LodInstFadeRange = CurrentBatch.Batch.LodInstFadeRange;
|
||||||
|
b.LodFadeStartDist = CurrentBatch.Batch.LodFadeStartDist;
|
||||||
|
b.ScaleRange = CurrentBatch.Batch.ScaleRange;
|
||||||
|
b.OrientToTerrain = CurrentBatch.Batch.OrientToTerrain;
|
||||||
|
b.archetypeName = CurrentBatch.Batch.archetypeName;
|
||||||
|
batch.Batch = b;
|
||||||
|
batch.Archetype = CurrentBatch.Archetype;
|
||||||
|
batch.UpdateInstanceCount();
|
||||||
|
ProjectForm.NewGrassBatch(batch);
|
||||||
|
}
|
||||||
|
CurrentBatch.Ymap.CalcExtents();
|
||||||
|
CurrentBatch.Ymap.Save();
|
||||||
|
|
||||||
|
// TODO: Select the last grass batch in the new list on the project explorer.
|
||||||
|
ProjectForm.ProjectExplorer.TrySelectGrassBatchTreeNode(CurrentBatch.Ymap.GrassInstanceBatches[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Publics
|
||||||
|
|
||||||
|
public void CreateInstancesAtMouse(SpaceRayIntersectResult mouseRay)
|
||||||
|
{
|
||||||
|
var wf = ProjectForm.WorldForm;
|
||||||
|
if (wf == null) return;
|
||||||
|
|
||||||
|
lock (wf.RenderSyncRoot)
|
||||||
|
{
|
||||||
|
CurrentBatch.CreateInstancesAtMouse(
|
||||||
|
CurrentBatch,
|
||||||
|
mouseRay,
|
||||||
|
(float) RadiusNumericUpDown.Value,
|
||||||
|
(int) DensityNumericUpDown.Value,
|
||||||
|
SpawnRayFunc,
|
||||||
|
new Color(GrassColorLabel.BackColor.R, GrassColorLabel.BackColor.G, GrassColorLabel.BackColor.B),
|
||||||
|
(int) AoNumericUpDown.Value,
|
||||||
|
(int) ScaleNumericUpDown.Value,
|
||||||
|
FloatUtil.ParseVector3String(PadTextBox.Text),
|
||||||
|
RandomizeScaleCheckBox.Checked
|
||||||
|
);
|
||||||
|
wf.UpdateGrassBatchGraphics(CurrentBatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
BatchChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EraseInstancesAtMouse(SpaceRayIntersectResult mouseRay)
|
||||||
|
{
|
||||||
|
var wf = ProjectForm.WorldForm;
|
||||||
|
if (wf == null) return;
|
||||||
|
var changed = false;
|
||||||
|
lock (wf.RenderSyncRoot)
|
||||||
|
{
|
||||||
|
if (CurrentBatch.EraseInstancesAtMouse(
|
||||||
|
CurrentBatch,
|
||||||
|
mouseRay,
|
||||||
|
(float) RadiusNumericUpDown.Value
|
||||||
|
))
|
||||||
|
{
|
||||||
|
wf.UpdateGrassBatchGraphics(CurrentBatch);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed) BatchChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Privates
|
||||||
|
|
||||||
|
private SpaceRayIntersectResult SpawnRayFunc(Vector3 spawnPos)
|
||||||
|
{
|
||||||
|
var res = ProjectForm.WorldForm.Raycast(new Ray(spawnPos, -Vector3.UnitZ));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BatchChanged()
|
||||||
|
{
|
||||||
|
UpdateControls();
|
||||||
|
CurrentBatch.UpdateInstanceCount();
|
||||||
|
CurrentBatch.HasChanged = true;
|
||||||
|
ProjectForm.SetGrassBatchHasChanged(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -117,6 +117,9 @@
|
|||||||
<resheader name="writer">
|
<resheader name="writer">
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
|
<metadata name="OptimizeBatchButtonTooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
|
<value>17, 17</value>
|
||||||
|
</metadata>
|
||||||
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||||
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
|
@ -641,6 +641,20 @@ namespace CodeWalker.Project.Panels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public void SetGrassBatchHasChanged(YmapGrassInstanceBatch batch, bool changed)
|
||||||
|
{
|
||||||
|
if (ProjectTreeView.Nodes.Count > 0)
|
||||||
|
{
|
||||||
|
var gbnode = FindGrassTreeNode(batch);
|
||||||
|
if (gbnode == null) return;
|
||||||
|
string changestr = changed ? "*" : "";
|
||||||
|
if (gbnode.Tag == batch)
|
||||||
|
{
|
||||||
|
string name = batch.ToString();
|
||||||
|
gbnode.Text = changestr + name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -698,6 +712,21 @@ namespace CodeWalker.Project.Panels
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
public TreeNode FindGrassTreeNode(YmapGrassInstanceBatch batch)
|
||||||
|
{
|
||||||
|
if (batch == null) return null;
|
||||||
|
TreeNode ymapnode = FindYmapTreeNode(batch.Ymap);
|
||||||
|
if (ymapnode == null) return null;
|
||||||
|
var batchnode = GetChildTreeNode(ymapnode, "GrassBatches");
|
||||||
|
if (batchnode == null) return null;
|
||||||
|
for (int i = 0; i < batchnode.Nodes.Count; i++)
|
||||||
|
{
|
||||||
|
TreeNode grassnode = batchnode.Nodes[i];
|
||||||
|
if (grassnode.Tag == batch) return grassnode;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public TreeNode FindYndTreeNode(YndFile ynd)
|
public TreeNode FindYndTreeNode(YndFile ynd)
|
||||||
{
|
{
|
||||||
if (ProjectTreeView.Nodes.Count <= 0) return null;
|
if (ProjectTreeView.Nodes.Count <= 0) return null;
|
||||||
@ -930,6 +959,21 @@ namespace CodeWalker.Project.Panels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public void TrySelectGrassBatchTreeNode(YmapGrassInstanceBatch grassBatch)
|
||||||
|
{
|
||||||
|
TreeNode grassNode = FindGrassTreeNode(grassBatch);
|
||||||
|
if (grassNode != null)
|
||||||
|
{
|
||||||
|
if (ProjectTreeView.SelectedNode == grassNode)
|
||||||
|
{
|
||||||
|
OnItemSelected?.Invoke(grassNode);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ProjectTreeView.SelectedNode = grassNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
public void TrySelectPathNodeTreeNode(YndNode node)
|
public void TrySelectPathNodeTreeNode(YndNode node)
|
||||||
{
|
{
|
||||||
TreeNode tnode = FindPathNodeTreeNode(node);
|
TreeNode tnode = FindPathNodeTreeNode(node);
|
||||||
@ -1240,6 +1284,16 @@ namespace CodeWalker.Project.Panels
|
|||||||
tn.Parent.Nodes.Remove(tn);
|
tn.Parent.Nodes.Remove(tn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void RemoveGrassBatchTreeNode(YmapGrassInstanceBatch batch)
|
||||||
|
{
|
||||||
|
var tn = FindGrassTreeNode(batch);
|
||||||
|
if ((tn != null) && (tn.Parent != null))
|
||||||
|
{
|
||||||
|
tn.Parent.Text = "Grass Batches (" + batch.Ymap.GrassInstanceBatches.Length.ToString() + ")";
|
||||||
|
tn.Parent.Nodes.Remove(tn);
|
||||||
|
}
|
||||||
|
}
|
||||||
public void RemovePathNodeTreeNode(YndNode node)
|
public void RemovePathNodeTreeNode(YndNode node)
|
||||||
{
|
{
|
||||||
var tn = FindPathNodeTreeNode(node);
|
var tn = FindPathNodeTreeNode(node);
|
||||||
|
1015
Project/ProjectForm.Designer.cs
generated
1015
Project/ProjectForm.Designer.cs
generated
File diff suppressed because it is too large
Load Diff
@ -104,7 +104,6 @@ namespace CodeWalker.Project
|
|||||||
RpfMan = GameFileCache.RpfMan;
|
RpfMan = GameFileCache.RpfMan;
|
||||||
})).Start();
|
})).Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateStatus(string text)
|
private void UpdateStatus(string text)
|
||||||
@ -705,6 +704,25 @@ namespace CodeWalker.Project
|
|||||||
|
|
||||||
//######## Public methods
|
//######## Public methods
|
||||||
|
|
||||||
|
// Possibly future proofing for procedural prop instances
|
||||||
|
public bool CanPaintInstances()
|
||||||
|
{
|
||||||
|
if (CurrentGrassBatch != null)
|
||||||
|
{
|
||||||
|
if (CurrentGrassBatch.BrushEnabled)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public float GetInstanceBrushRadius()
|
||||||
|
{
|
||||||
|
if (CurrentGrassBatch != null)
|
||||||
|
return CurrentGrassBatch.BrushRadius;
|
||||||
|
|
||||||
|
return 0f;
|
||||||
|
}
|
||||||
|
|
||||||
public void NewProject()
|
public void NewProject()
|
||||||
{
|
{
|
||||||
if (CurrentProjectFile != null)
|
if (CurrentProjectFile != null)
|
||||||
@ -1270,6 +1288,10 @@ namespace CodeWalker.Project
|
|||||||
{
|
{
|
||||||
ProjectExplorer?.TrySelectCarGenTreeNode(CurrentCarGen);
|
ProjectExplorer?.TrySelectCarGenTreeNode(CurrentCarGen);
|
||||||
}
|
}
|
||||||
|
else if (CurrentGrassBatch != null)
|
||||||
|
{
|
||||||
|
ProjectExplorer?.TrySelectGrassBatchTreeNode(CurrentGrassBatch);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
public void RemoveYmapFromProject()
|
public void RemoveYmapFromProject()
|
||||||
{
|
{
|
||||||
@ -1435,6 +1457,155 @@ namespace CodeWalker.Project
|
|||||||
return CurrentEntity == ent;
|
return CurrentEntity == ent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void NewGrassBatch(YmapGrassInstanceBatch copy = null)
|
||||||
|
{
|
||||||
|
if (CurrentYmapFile == null) return;
|
||||||
|
|
||||||
|
rage__fwGrassInstanceListDef fwBatch = new rage__fwGrassInstanceListDef();
|
||||||
|
rage__fwGrassInstanceListDef__InstanceData[] instances = new rage__fwGrassInstanceListDef__InstanceData[0];
|
||||||
|
|
||||||
|
if (copy != null)
|
||||||
|
{
|
||||||
|
fwBatch = copy.Batch;
|
||||||
|
instances = copy.Instances;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fwBatch.archetypeName = new MetaHash(JenkHash.GenHash("proc_grasses01"));
|
||||||
|
fwBatch.lodDist = 120;
|
||||||
|
fwBatch.LodFadeStartDist = 15;
|
||||||
|
fwBatch.LodInstFadeRange = 0.75f;
|
||||||
|
fwBatch.OrientToTerrain = 1.0f;
|
||||||
|
fwBatch.ScaleRange = new Vector3(0.3f, 0.2f, 0.7f);
|
||||||
|
}
|
||||||
|
|
||||||
|
YmapGrassInstanceBatch batch = new YmapGrassInstanceBatch
|
||||||
|
{
|
||||||
|
AABBMin = fwBatch.BatchAABB.min.XYZ(),
|
||||||
|
AABBMax = fwBatch.BatchAABB.max.XYZ(),
|
||||||
|
Archetype = GameFileCache.GetArchetype(fwBatch.archetypeName),
|
||||||
|
Batch = fwBatch,
|
||||||
|
Instances = instances
|
||||||
|
};
|
||||||
|
|
||||||
|
batch.Position = (batch.AABBMin + batch.AABBMax) * 0.5f;
|
||||||
|
batch.Radius = (batch.AABBMax - batch.AABBMin).Length() * 0.5f;
|
||||||
|
batch.Ymap = CurrentYmapFile;
|
||||||
|
|
||||||
|
if (WorldForm != null)
|
||||||
|
{
|
||||||
|
lock (WorldForm.RenderSyncRoot) //don't try to do this while rendering...
|
||||||
|
{
|
||||||
|
CurrentYmapFile.AddGrassBatch(batch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CurrentYmapFile.AddGrassBatch(batch);
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadProjectTree();
|
||||||
|
|
||||||
|
ProjectExplorer?.TrySelectGrassBatchTreeNode(batch);
|
||||||
|
CurrentGrassBatch = batch;
|
||||||
|
ShowEditYmapGrassBatchPanel(false);
|
||||||
|
}
|
||||||
|
public void AddGrassBatchToProject()
|
||||||
|
{
|
||||||
|
if (CurrentGrassBatch == null) return;
|
||||||
|
|
||||||
|
CurrentYmapFile = CurrentGrassBatch.Ymap;
|
||||||
|
if (!YmapExistsInProject(CurrentYmapFile))
|
||||||
|
{
|
||||||
|
var grassBatch = CurrentGrassBatch;
|
||||||
|
CurrentYmapFile.HasChanged = true;
|
||||||
|
AddYmapToProject(CurrentYmapFile);
|
||||||
|
|
||||||
|
CurrentGrassBatch = grassBatch; //bug fix for some reason the treeview selects the project node here.
|
||||||
|
CurrentYmapFile = grassBatch.Ymap;
|
||||||
|
ProjectExplorer?.TrySelectGrassBatchTreeNode(grassBatch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public bool DeleteGrassBatch()
|
||||||
|
{
|
||||||
|
if (CurrentYmapFile == null) return false;
|
||||||
|
if (CurrentGrassBatch == null) return false;
|
||||||
|
if (CurrentGrassBatch.Ymap != CurrentYmapFile) return false;
|
||||||
|
if (CurrentYmapFile.GrassInstanceBatches == null) return false; //nothing to delete..
|
||||||
|
|
||||||
|
if (MessageBox.Show("Are you sure you want to delete this grass batch?\n" + CurrentGrassBatch.ToString() + "\n\nThis operation cannot be undone. Continue?", "Confirm delete", MessageBoxButtons.YesNo) != DialogResult.Yes)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool res = false;
|
||||||
|
if (WorldForm != null)
|
||||||
|
{
|
||||||
|
lock (WorldForm.RenderSyncRoot) //don't try to do this while rendering...
|
||||||
|
{
|
||||||
|
res = CurrentYmapFile.RemoveGrassBatch(CurrentGrassBatch);
|
||||||
|
//WorldForm.SelectItem(null, null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = CurrentYmapFile.RemoveGrassBatch(CurrentGrassBatch);
|
||||||
|
}
|
||||||
|
if (!res)
|
||||||
|
{
|
||||||
|
MessageBox.Show("Unable to delete the grass batch. This shouldn't happen!");
|
||||||
|
}
|
||||||
|
|
||||||
|
var delbatch = CurrentGrassBatch;
|
||||||
|
|
||||||
|
ProjectExplorer?.RemoveGrassBatchTreeNode(CurrentGrassBatch);
|
||||||
|
ProjectExplorer?.SetYmapHasChanged(CurrentYmapFile, true);
|
||||||
|
|
||||||
|
ClosePanel((EditYmapGrassPanel p) => { return p.Tag == delbatch; });
|
||||||
|
|
||||||
|
CurrentGrassBatch = null;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public void PaintGrass(SpaceRayIntersectResult mouseRay, bool erase)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (InvokeRequired)
|
||||||
|
{
|
||||||
|
Invoke(new Action(() => { PaintGrass(mouseRay, erase); }));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mouseRay.Hit || !mouseRay.TestComplete) return;
|
||||||
|
if (CurrentGrassBatch == null || (!CurrentGrassBatch.BrushEnabled)) return; // brush isn't enabled right now
|
||||||
|
EditYmapGrassPanel panel = FindPanel<EditYmapGrassPanel>(x => x.CurrentBatch == CurrentGrassBatch);
|
||||||
|
if (panel == null) return; // no panels with this batch
|
||||||
|
|
||||||
|
// TODO: Maybe move these functions into the batch instead of the grass panel?
|
||||||
|
// although, the panel does have the brush settings.
|
||||||
|
if (!erase)
|
||||||
|
panel.CreateInstancesAtMouse(mouseRay);
|
||||||
|
else panel.EraseInstancesAtMouse(mouseRay);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
public bool GrassBatchExistsInProject(YmapGrassInstanceBatch batch)
|
||||||
|
{
|
||||||
|
if (CurrentProjectFile?.YmapFiles == null) return false;
|
||||||
|
if (CurrentProjectFile.YmapFiles.Count <= 0) return false;
|
||||||
|
foreach (var ymapFile in CurrentProjectFile.YmapFiles)
|
||||||
|
{
|
||||||
|
if (ymapFile.GrassInstanceBatches == null) continue;
|
||||||
|
foreach (var b in ymapFile.GrassInstanceBatches)
|
||||||
|
{
|
||||||
|
if (batch == b)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public void NewCarGen(YmapCarGen copy = null, bool copyPosition = false)
|
public void NewCarGen(YmapCarGen copy = null, bool copyPosition = false)
|
||||||
{
|
{
|
||||||
if (CurrentYmapFile == null) return;
|
if (CurrentYmapFile == null) return;
|
||||||
@ -3820,9 +3991,11 @@ namespace CodeWalker.Project
|
|||||||
for (int i = 0; i < CurrentProjectFile.YmapFiles.Count; i++)
|
for (int i = 0; i < CurrentProjectFile.YmapFiles.Count; i++)
|
||||||
{
|
{
|
||||||
var ymap = CurrentProjectFile.YmapFiles[i];
|
var ymap = CurrentProjectFile.YmapFiles[i];
|
||||||
|
// make sure we're not hiding ymaps that have been added by the end-user.
|
||||||
|
var isnew = ymap.RpfFileEntry.ShortNameHash == 0;
|
||||||
if (ymap.Loaded)
|
if (ymap.Loaded)
|
||||||
{
|
{
|
||||||
ymaps[ymap._CMapData.name] = ymap;
|
ymaps[isnew ? JenkHash.GenHash(ymap.Name) : ymap.RpfFileEntry.ShortNameHash] = ymap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4026,6 +4199,11 @@ namespace CodeWalker.Project
|
|||||||
{
|
{
|
||||||
ProjectExplorer?.TrySelectCarGenTreeNode(cargen);
|
ProjectExplorer?.TrySelectCarGenTreeNode(cargen);
|
||||||
}
|
}
|
||||||
|
if (grassbatch != CurrentGrassBatch)
|
||||||
|
{
|
||||||
|
ProjectExplorer?.TrySelectGrassBatchTreeNode(grassbatch);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (YndExistsInProject(ynd))
|
else if (YndExistsInProject(ynd))
|
||||||
{
|
{
|
||||||
@ -4699,7 +4877,19 @@ namespace CodeWalker.Project
|
|||||||
|
|
||||||
PromoteIfPreviewPanelActive();
|
PromoteIfPreviewPanelActive();
|
||||||
}
|
}
|
||||||
|
public void SetGrassBatchHasChanged(bool changed)
|
||||||
|
{
|
||||||
|
if (CurrentGrassBatch == null) return;
|
||||||
|
|
||||||
|
bool changechange = changed != CurrentGrassBatch.HasChanged;
|
||||||
|
if (!changechange) return;
|
||||||
|
|
||||||
|
CurrentGrassBatch.HasChanged = true;
|
||||||
|
|
||||||
|
ProjectExplorer?.SetGrassBatchHasChanged(CurrentGrassBatch, changed);
|
||||||
|
|
||||||
|
PromoteIfPreviewPanelActive();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -4877,6 +5067,7 @@ namespace CodeWalker.Project
|
|||||||
|
|
||||||
YmapNewEntityMenu.Enabled = enable && inproj;
|
YmapNewEntityMenu.Enabled = enable && inproj;
|
||||||
YmapNewCarGenMenu.Enabled = enable && inproj;
|
YmapNewCarGenMenu.Enabled = enable && inproj;
|
||||||
|
YmapNewGrassBatchMenu.Enabled = enable && inproj;
|
||||||
|
|
||||||
if (CurrentYmapFile != null)
|
if (CurrentYmapFile != null)
|
||||||
{
|
{
|
||||||
@ -5312,6 +5503,10 @@ namespace CodeWalker.Project
|
|||||||
{
|
{
|
||||||
NewCarGen();
|
NewCarGen();
|
||||||
}
|
}
|
||||||
|
private void YmapNewGrassBatchMenu_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
NewGrassBatch();
|
||||||
|
}
|
||||||
private void YmapAddToProjectMenu_Click(object sender, EventArgs e)
|
private void YmapAddToProjectMenu_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
AddYmapToProject(CurrentYmapFile);
|
AddYmapToProject(CurrentYmapFile);
|
||||||
|
@ -195,6 +195,14 @@ namespace CodeWalker.Rendering
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Invalidate(YmapGrassInstanceBatch batch)
|
||||||
|
{
|
||||||
|
lock (updateSyncRoot)
|
||||||
|
{
|
||||||
|
instbatches.Invalidate(batch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -380,6 +380,11 @@ namespace CodeWalker.Rendering
|
|||||||
renderableCache.Invalidate(path);
|
renderableCache.Invalidate(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Invalidate(YmapGrassInstanceBatch batch)
|
||||||
|
{
|
||||||
|
renderableCache.Invalidate(batch);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void UpdateSelectionDrawFlags(DrawableModel model, DrawableGeometry geom, bool rem)
|
public void UpdateSelectionDrawFlags(DrawableModel model, DrawableGeometry geom, bool rem)
|
||||||
{
|
{
|
||||||
@ -679,6 +684,33 @@ namespace CodeWalker.Rendering
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void RenderBrushRadiusOutline(Vector3 position, Vector3 dir, Vector3 up, float radius, uint col)
|
||||||
|
{
|
||||||
|
const int Reso = 36;
|
||||||
|
const float MaxDeg = 360f;
|
||||||
|
const float DegToRad = 0.0174533f;
|
||||||
|
const float Ang = MaxDeg / Reso;
|
||||||
|
|
||||||
|
var axis = Vector3.Cross(dir, up);
|
||||||
|
var c = new VertexTypePC[Reso];
|
||||||
|
|
||||||
|
for (var i = 0; i < Reso; i++)
|
||||||
|
{
|
||||||
|
var rDir = Quaternion.RotationAxis(dir, (i * Ang) * DegToRad).Multiply(axis);
|
||||||
|
c[i].Position = position + (rDir * radius);
|
||||||
|
c[i].Colour = col;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < c.Length; i++)
|
||||||
|
{
|
||||||
|
SelectionLineVerts.Add(c[i]);
|
||||||
|
SelectionLineVerts.Add(c[(i + 1) % c.Length]);
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectionLineVerts.Add(new VertexTypePC{Colour = col, Position = position});
|
||||||
|
SelectionLineVerts.Add(new VertexTypePC { Colour = col, Position = position + dir * 2f});
|
||||||
|
}
|
||||||
|
|
||||||
public void RenderSelectionArrowOutline(Vector3 pos, Vector3 dir, Vector3 up, Quaternion ori, float len, float rad, uint colour)
|
public void RenderSelectionArrowOutline(Vector3 pos, Vector3 dir, Vector3 up, Quaternion ori, float len, float rad, uint colour)
|
||||||
{
|
{
|
||||||
Vector3 ax = Vector3.Cross(dir, up);
|
Vector3 ax = Vector3.Cross(dir, up);
|
||||||
|
@ -695,6 +695,10 @@ namespace CodeWalker.Rendering
|
|||||||
{
|
{
|
||||||
var gb = batch.Key;
|
var gb = batch.Key;
|
||||||
|
|
||||||
|
// sanity check
|
||||||
|
if (batch.GrassInstanceBuffer == null)
|
||||||
|
return;
|
||||||
|
|
||||||
VSEntityVars.Vars.CamRel = new Vector4(gb.CamRel, 0.0f);
|
VSEntityVars.Vars.CamRel = new Vector4(gb.CamRel, 0.0f);
|
||||||
VSEntityVars.Vars.Orientation = Quaternion.Identity;
|
VSEntityVars.Vars.Orientation = Quaternion.Identity;
|
||||||
VSEntityVars.Vars.Scale = Vector3.One;
|
VSEntityVars.Vars.Scale = Vector3.One;
|
||||||
|
186
WorldForm.cs
186
WorldForm.cs
@ -75,6 +75,11 @@ namespace CodeWalker
|
|||||||
|
|
||||||
bool ControlFireToggle = false;
|
bool ControlFireToggle = false;
|
||||||
|
|
||||||
|
|
||||||
|
int ControlBrushTimer = 0;
|
||||||
|
bool ControlBrushEnabled;
|
||||||
|
float ControlBrushRadius;
|
||||||
|
|
||||||
Entity camEntity = new Entity();
|
Entity camEntity = new Entity();
|
||||||
PedEntity pedEntity = new PedEntity();
|
PedEntity pedEntity = new PedEntity();
|
||||||
|
|
||||||
@ -486,7 +491,7 @@ namespace CodeWalker
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (ControlMode == WorldControlMode.Free)
|
if (ControlMode == WorldControlMode.Free || ControlBrushEnabled)
|
||||||
{
|
{
|
||||||
if (Input.ShiftPressed)
|
if (Input.ShiftPressed)
|
||||||
{
|
{
|
||||||
@ -1252,7 +1257,7 @@ namespace CodeWalker
|
|||||||
if (MouseRayCollision.Hit)
|
if (MouseRayCollision.Hit)
|
||||||
{
|
{
|
||||||
var arup = GetPerpVec(MouseRayCollision.Normal);
|
var arup = GetPerpVec(MouseRayCollision.Normal);
|
||||||
Renderer.RenderSelectionArrowOutline(MouseRayCollision.Position, MouseRayCollision.Normal, arup, Quaternion.Identity, 2.0f, 0.15f, cgrn);
|
Renderer.RenderBrushRadiusOutline(MouseRayCollision.Position, MouseRayCollision.Normal, arup, ProjectForm.GetInstanceBrushRadius(), cgrn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1824,6 +1829,14 @@ namespace CodeWalker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void UpdateGrassBatchGraphics(YmapGrassInstanceBatch grassBatch)
|
||||||
|
{
|
||||||
|
lock (Renderer.RenderSyncRoot)
|
||||||
|
{
|
||||||
|
Renderer.Invalidate(grassBatch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public Vector3 GetCameraPosition()
|
public Vector3 GetCameraPosition()
|
||||||
{
|
{
|
||||||
@ -2049,8 +2062,8 @@ namespace CodeWalker
|
|||||||
|
|
||||||
if (mode == ControlMode) return;
|
if (mode == ControlMode) return;
|
||||||
|
|
||||||
bool wasfree = (ControlMode == WorldControlMode.Free);
|
bool wasfree = (ControlMode == WorldControlMode.Free || ControlBrushEnabled);
|
||||||
bool isfree = (mode == WorldControlMode.Free);
|
bool isfree = (mode == WorldControlMode.Free || ControlBrushEnabled);
|
||||||
|
|
||||||
if (isfree && !wasfree)
|
if (isfree && !wasfree)
|
||||||
{
|
{
|
||||||
@ -2104,17 +2117,17 @@ namespace CodeWalker
|
|||||||
//reset variables for beginning the mouse hit test
|
//reset variables for beginning the mouse hit test
|
||||||
CurMouseHit.Clear();
|
CurMouseHit.Clear();
|
||||||
|
|
||||||
|
// Get whether or not we can brush from the project form.
|
||||||
MouseRayCollisionEnabled = Input.CtrlPressed; //temporary...!
|
if (Input.CtrlPressed && ProjectForm != null && ProjectForm.CanPaintInstances())
|
||||||
if (MouseRayCollisionEnabled)
|
|
||||||
{
|
{
|
||||||
if (space.Inited && space.Grid != null)
|
ControlBrushEnabled = true;
|
||||||
{
|
MouseRayCollisionEnabled = true;
|
||||||
Ray mray = new Ray();
|
MouseRayCollision = GetSpaceMouseRay();
|
||||||
mray.Position = camera.MouseRay.Position + camera.Position;
|
|
||||||
mray.Direction = camera.MouseRay.Direction;
|
|
||||||
MouseRayCollision = space.RayIntersect(mray);
|
|
||||||
}
|
}
|
||||||
|
else if (MouseRayCollisionEnabled)
|
||||||
|
{
|
||||||
|
ControlBrushEnabled = false;
|
||||||
|
MouseRayCollisionEnabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2127,6 +2140,25 @@ namespace CodeWalker
|
|||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SpaceRayIntersectResult GetSpaceMouseRay()
|
||||||
|
{
|
||||||
|
SpaceRayIntersectResult ret = new SpaceRayIntersectResult();
|
||||||
|
if (space.Inited && space.Grid != null)
|
||||||
|
{
|
||||||
|
Ray mray = new Ray();
|
||||||
|
mray.Position = camera.MouseRay.Position + camera.Position;
|
||||||
|
mray.Direction = camera.MouseRay.Direction;
|
||||||
|
return space.RayIntersect(mray);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SpaceRayIntersectResult Raycast(Ray ray)
|
||||||
|
{
|
||||||
|
return space.RayIntersect(ray);
|
||||||
|
}
|
||||||
|
|
||||||
private void UpdateMouseHitsFromRenderer()
|
private void UpdateMouseHitsFromRenderer()
|
||||||
{
|
{
|
||||||
foreach (var rd in Renderer.RenderedDrawables)
|
foreach (var rd in Renderer.RenderedDrawables)
|
||||||
@ -4318,6 +4350,12 @@ namespace CodeWalker
|
|||||||
{
|
{
|
||||||
camera.FollowEntity.Position = p;
|
camera.FollowEntity.Position = p;
|
||||||
}
|
}
|
||||||
|
public void GoToPosition(Vector3 p, Vector3 bound)
|
||||||
|
{
|
||||||
|
camera.FollowEntity.Position = p;
|
||||||
|
var bl = bound.Length();
|
||||||
|
camera.TargetDistance = bl > 1f ? bl : 1f;
|
||||||
|
}
|
||||||
|
|
||||||
private MapMarker AddMarker(Vector3 pos, string name, bool addtotxtbox = false)
|
private MapMarker AddMarker(Vector3 pos, string name, bool addtotxtbox = false)
|
||||||
{
|
{
|
||||||
@ -5898,7 +5936,7 @@ namespace CodeWalker
|
|||||||
MouseDownPoint = e.Location;
|
MouseDownPoint = e.Location;
|
||||||
MouseLastPoint = MouseDownPoint;
|
MouseLastPoint = MouseDownPoint;
|
||||||
|
|
||||||
if (ControlMode == WorldControlMode.Free)
|
if (ControlMode == WorldControlMode.Free && !ControlBrushEnabled)
|
||||||
{
|
{
|
||||||
if (MouseLButtonDown)
|
if (MouseLButtonDown)
|
||||||
{
|
{
|
||||||
@ -5997,6 +6035,7 @@ namespace CodeWalker
|
|||||||
SelectedMarker = null;
|
SelectedMarker = null;
|
||||||
HideMarkerSelectionInfo();
|
HideMarkerSelectionInfo();
|
||||||
}
|
}
|
||||||
|
ControlBrushTimer = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -6011,47 +6050,11 @@ namespace CodeWalker
|
|||||||
dy = -dy;
|
dy = -dy;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ControlMode == WorldControlMode.Free)
|
if (ControlMode == WorldControlMode.Free && !ControlBrushEnabled)
|
||||||
{
|
{
|
||||||
if (MouseLButtonDown)
|
if (MouseLButtonDown)
|
||||||
{
|
{
|
||||||
if (GrabbedMarker == null)
|
RotateCam(dx, dy);
|
||||||
{
|
|
||||||
if (GrabbedWidget == null)
|
|
||||||
{
|
|
||||||
if (MapViewEnabled == false)
|
|
||||||
{
|
|
||||||
camera.MouseRotate(dx, dy);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//need to move the camera entity XY with mouse in mapview mode...
|
|
||||||
MapViewDragX += dx;
|
|
||||||
MapViewDragY += dy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//grabbed widget will move itself in Update() when IsDragging==true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//move the grabbed marker...
|
|
||||||
//float uptx = (CurrentMap != null) ? CurrentMap.UnitsPerTexelX : 1.0f;
|
|
||||||
//float upty = (CurrentMap != null) ? CurrentMap.UnitsPerTexelY : 1.0f;
|
|
||||||
//Vector3 wpos = GrabbedMarker.WorldPos;
|
|
||||||
//wpos.X += dx * uptx;
|
|
||||||
//wpos.Y += dy * upty;
|
|
||||||
//GrabbedMarker.WorldPos = wpos;
|
|
||||||
//UpdateMarkerTexturePos(GrabbedMarker);
|
|
||||||
//if (GrabbedMarker == LocatorMarker)
|
|
||||||
//{
|
|
||||||
// LocateTextBox.Text = LocatorMarker.ToString();
|
|
||||||
// WorldCoordTextBox.Text = LocatorMarker.Get2DWorldPosString();
|
|
||||||
// TextureCoordTextBox.Text = LocatorMarker.Get2DTexturePosString();
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (MouseRButtonDown)
|
if (MouseRButtonDown)
|
||||||
{
|
{
|
||||||
@ -6075,11 +6078,31 @@ namespace CodeWalker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseX = e.X;
|
UpdateMousePosition(e);
|
||||||
MouseY = e.Y;
|
|
||||||
MouseLastPoint = e.Location;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
else if (ControlBrushEnabled)
|
||||||
|
{
|
||||||
|
if (MouseRButtonDown)
|
||||||
|
{
|
||||||
|
RotateCam(dx, dy);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateMousePosition(e);
|
||||||
|
|
||||||
|
ControlBrushTimer++;
|
||||||
|
if (ControlBrushTimer > (Input.ShiftPressed ? 5 : 10))
|
||||||
|
{
|
||||||
|
lock (Renderer.RenderSyncRoot)
|
||||||
|
{
|
||||||
|
if (ProjectForm != null && MouseLButtonDown)
|
||||||
|
{
|
||||||
|
ProjectForm.PaintGrass(MouseRayCollision, Input.ShiftPressed);
|
||||||
|
}
|
||||||
|
ControlBrushTimer = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
lock (MouseControlSyncRoot)
|
lock (MouseControlSyncRoot)
|
||||||
@ -6120,11 +6143,59 @@ namespace CodeWalker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateMousePosition(MouseEventArgs e)
|
||||||
|
{
|
||||||
|
MouseX = e.X;
|
||||||
|
MouseY = e.Y;
|
||||||
|
MouseLastPoint = e.Location;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RotateCam(int dx, int dy)
|
||||||
|
{
|
||||||
|
if (GrabbedMarker == null)
|
||||||
|
{
|
||||||
|
if (GrabbedWidget == null)
|
||||||
|
{
|
||||||
|
if (MapViewEnabled == false)
|
||||||
|
{
|
||||||
|
camera.MouseRotate(dx, dy);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//need to move the camera entity XY with mouse in mapview mode...
|
||||||
|
MapViewDragX += dx;
|
||||||
|
MapViewDragY += dy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//grabbed widget will move itself in Update() when IsDragging==true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//move the grabbed marker...
|
||||||
|
//float uptx = (CurrentMap != null) ? CurrentMap.UnitsPerTexelX : 1.0f;
|
||||||
|
//float upty = (CurrentMap != null) ? CurrentMap.UnitsPerTexelY : 1.0f;
|
||||||
|
//Vector3 wpos = GrabbedMarker.WorldPos;
|
||||||
|
//wpos.X += dx * uptx;
|
||||||
|
//wpos.Y += dy * upty;
|
||||||
|
//GrabbedMarker.WorldPos = wpos;
|
||||||
|
//UpdateMarkerTexturePos(GrabbedMarker);
|
||||||
|
//if (GrabbedMarker == LocatorMarker)
|
||||||
|
//{
|
||||||
|
// LocateTextBox.Text = LocatorMarker.ToString();
|
||||||
|
// WorldCoordTextBox.Text = LocatorMarker.Get2DWorldPosString();
|
||||||
|
// TextureCoordTextBox.Text = LocatorMarker.Get2DTexturePosString();
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void WorldForm_MouseWheel(object sender, MouseEventArgs e)
|
private void WorldForm_MouseWheel(object sender, MouseEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.Delta != 0)
|
if (e.Delta != 0)
|
||||||
{
|
{
|
||||||
if (ControlMode == WorldControlMode.Free)
|
if (ControlMode == WorldControlMode.Free || ControlBrushEnabled)
|
||||||
{
|
{
|
||||||
camera.MouseZoom(e.Delta);
|
camera.MouseZoom(e.Delta);
|
||||||
}
|
}
|
||||||
@ -6254,7 +6325,7 @@ namespace CodeWalker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ControlMode != WorldControlMode.Free)
|
if (ControlMode != WorldControlMode.Free || ControlBrushEnabled)
|
||||||
{
|
{
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
@ -7555,7 +7626,6 @@ namespace CodeWalker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public enum WorldControlMode
|
public enum WorldControlMode
|
||||||
{
|
{
|
||||||
Free = 0,
|
Free = 0,
|
||||||
|
Loading…
Reference in New Issue
Block a user