added grass painting

This commit is contained in:
Soloman N 2018-06-07 12:42:41 -04:00
parent 4c131fa0af
commit b971beb5bf
17 changed files with 2390 additions and 586 deletions

View File

@ -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" />

View File

@ -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,473 @@ 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; }
public bool BrushEnabled;
public bool HasChanged;
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 EraseInstancesAtMouse(
YmapGrassInstanceBatch batch,
SpaceRayIntersectResult mouseRay,
float radius)
{
// some inits
var instances = batch.Instances;
var delInstances = new List<rage__fwGrassInstanceListDef__InstanceData>();
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)
foreach (var instance in instances)
{
var grassPos = new Vector3
{
X = instance.Position.u0,
Y = instance.Position.u1,
Z = instance.Position.u2
};
// get the world pos
var worldPos = oldInstanceBounds.Minimum + oldInstanceBounds.Size() * (grassPos * BatchVertMultiplier);
// create a boundary around the instance.
var instanceBounds = new BoundingBox(worldPos - (Vector3.One * 1.5f), worldPos + (Vector3.One * 1.5f));
// 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
}
}
if (delInstances.Count <= 0)
return false;
// now we need to recalculate the bounds.
var insList = instances.ToList();
foreach (var inst in delInstances)
{
if (insList.Contains(inst))
{
insList.Remove(inst);
}
}
GetNewGrassBounds(insList, oldInstanceBounds, out var min, out var max);
var newBounds = new BoundingBox(min, max);
// 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)
{
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());
// Begin the spawn bounds.
var grassBound = positions.Count <= 0
? new BoundingBox(Vector3.Zero, Vector3.Zero)
: new BoundingBox(positions[0] - (Vector3.One * 1.5f), positions[0] + (Vector3.One * 1.5f));
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();
}
// 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 null;
}
// 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
GetNewGrassBounds(grassList, oldInstanceBounds, out var min, out var max);
var newInstanceBounds = new BoundingBox(min, max);
// 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
GetNewGrassBounds(mb, batchAABB, out var min, out var max);
var radius = (max - min).Length() * 0.5F; // get the 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.
GetNewGrassBounds(points, batchAABB, out var min, out var max);
var m = new BoundingBox(min, max);
// 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 void GetNewGrassBounds(IReadOnlyList<rage__fwGrassInstanceListDef__InstanceData> newGrass, BoundingBox oldAABB,
out Vector3 min, out Vector3 max)
{
if (newGrass.Count <= 0)
{
min = Vector3.Zero;
max = Vector3.Zero;
return;
}
var grassWorldPos = GetGrassWorldPos(newGrass[0].Position, oldAABB);
var bounds = new BoundingBox(grassWorldPos - (Vector3.One * 1.5f), grassWorldPos + (Vector3.One * 1.5f));
foreach (var point in newGrass)
{
var worldPos = GetGrassWorldPos(point.Position, oldAABB);
var tempBounds = new BoundingBox(worldPos - (Vector3.One * 1.5f), worldPos + (Vector3.One * 1.5f));
bounds = bounds.Encapsulate(tempBounds);
}
min = bounds.Minimum;
max = bounds.Maximum;
}
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);
var posOffset = origin + new Vector3(randX, randY, 2f);
var spaceRay = spawnRayFunc.Invoke(posOffset);
if (!spaceRay.Hit) 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 - (Vector3.One * 1.5f), pos + (Vector3.One * 1.5f));
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 +2041,6 @@ namespace CodeWalker.GameFiles
} }
} }
[TypeConverter(typeof(ExpandableObjectConverter))] [TypeConverter(typeof(ExpandableObjectConverter))]
public class YmapTimeCycleModifier public class YmapTimeCycleModifier
{ {

View File

@ -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();

View File

@ -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);

View File

@ -0,0 +1,34 @@
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();
}
}
}

View File

@ -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));
} }
} }

View File

@ -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>");

View File

@ -28,35 +28,711 @@
/// </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.label17 = new System.Windows.Forms.Label();
this.OptmizationThresholdNumericUpDown = new System.Windows.Forms.NumericUpDown();
this.textBox5 = new System.Windows.Forms.TextBox();
this.OptimizeBatchButton = new System.Windows.Forms.Button();
this.label14 = new System.Windows.Forms.Label();
this.textBox4 = new System.Windows.Forms.TextBox();
this.label13 = new System.Windows.Forms.Label();
this.HashLabel = new System.Windows.Forms.Label();
this.ArchetypeNameTextBox = new System.Windows.Forms.TextBox();
this.label7 = new System.Windows.Forms.Label();
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.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.GrassDeleteButton = new System.Windows.Forms.Button();
this.GrassAddToProjectButton = new System.Windows.Forms.Button();
this.BrushTab = new System.Windows.Forms.TabPage();
this.label16 = new System.Windows.Forms.Label();
this.label8 = new System.Windows.Forms.Label();
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.BrushModeCheckBox = 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.label18 = new System.Windows.Forms.Label();
this.OptimizeBatchButtonTooltip = new System.Windows.Forms.ToolTip(this.components);
this.tabControl1.SuspendLayout();
this.GrassBatchTab.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.OptmizationThresholdNumericUpDown)).BeginInit();
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();
this.SuspendLayout(); this.SuspendLayout();
// //
// tabControl1
//
this.tabControl1.Controls.Add(this.GrassBatchTab);
this.tabControl1.Controls.Add(this.BrushTab);
this.tabControl1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tabControl1.Location = new System.Drawing.Point(0, 0);
this.tabControl1.Name = "tabControl1";
this.tabControl1.SelectedIndex = 0;
this.tabControl1.Size = new System.Drawing.Size(554, 355);
this.tabControl1.TabIndex = 37;
//
// GrassBatchTab
//
this.GrassBatchTab.Controls.Add(this.label18);
this.GrassBatchTab.Controls.Add(this.label17);
this.GrassBatchTab.Controls.Add(this.OptmizationThresholdNumericUpDown);
this.GrassBatchTab.Controls.Add(this.textBox5);
this.GrassBatchTab.Controls.Add(this.OptimizeBatchButton);
this.GrassBatchTab.Controls.Add(this.label14);
this.GrassBatchTab.Controls.Add(this.textBox4);
this.GrassBatchTab.Controls.Add(this.label13);
this.GrassBatchTab.Controls.Add(this.HashLabel);
this.GrassBatchTab.Controls.Add(this.ArchetypeNameTextBox);
this.GrassBatchTab.Controls.Add(this.label7);
this.GrassBatchTab.Controls.Add(this.groupBox1);
this.GrassBatchTab.Controls.Add(this.PositionTextBox);
this.GrassBatchTab.Controls.Add(this.label1);
this.GrassBatchTab.Controls.Add(this.GrassGoToButton);
this.GrassBatchTab.Controls.Add(this.GrassDeleteButton);
this.GrassBatchTab.Controls.Add(this.GrassAddToProjectButton);
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(546, 329);
this.GrassBatchTab.TabIndex = 0;
this.GrassBatchTab.Text = "Grass Batch";
this.GrassBatchTab.UseVisualStyleBackColor = true;
//
// label17
//
this.label17.AutoSize = true;
this.label17.Location = new System.Drawing.Point(400, 241);
this.label17.Name = "label17";
this.label17.Size = new System.Drawing.Size(54, 13);
this.label17.TabIndex = 51;
this.label17.Text = "Threshold";
//
// OptmizationThresholdNumericUpDown
//
this.OptmizationThresholdNumericUpDown.Location = new System.Drawing.Point(460, 236);
this.OptmizationThresholdNumericUpDown.Maximum = new decimal(new int[] {
999999,
0,
0,
0});
this.OptmizationThresholdNumericUpDown.Name = "OptmizationThresholdNumericUpDown";
this.OptmizationThresholdNumericUpDown.Size = new System.Drawing.Size(66, 20);
this.OptmizationThresholdNumericUpDown.TabIndex = 50;
this.OptmizationThresholdNumericUpDown.Value = new decimal(new int[] {
5,
0,
0,
0});
//
// textBox5
//
this.textBox5.Location = new System.Drawing.Point(93, 237);
this.textBox5.Name = "textBox5";
this.textBox5.ReadOnly = true;
this.textBox5.Size = new System.Drawing.Size(282, 20);
this.textBox5.TabIndex = 49;
this.textBox5.Text = "TODO!";
//
// OptimizeBatchButton
//
this.OptimizeBatchButton.Location = new System.Drawing.Point(403, 207);
this.OptimizeBatchButton.Name = "OptimizeBatchButton";
this.OptimizeBatchButton.Size = new System.Drawing.Size(123, 24);
this.OptimizeBatchButton.TabIndex = 50;
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);
//
// label14
//
this.label14.AutoSize = true;
this.label14.Location = new System.Drawing.Point(22, 240);
this.label14.Name = "label14";
this.label14.Size = new System.Drawing.Size(68, 13);
this.label14.TabIndex = 48;
this.label14.Text = "Extents Max:";
//
// textBox4
//
this.textBox4.Location = new System.Drawing.Point(93, 210);
this.textBox4.Name = "textBox4";
this.textBox4.ReadOnly = true;
this.textBox4.Size = new System.Drawing.Size(282, 20);
this.textBox4.TabIndex = 47;
this.textBox4.Text = "TODO!";
//
// label13
//
this.label13.AutoSize = true;
this.label13.Location = new System.Drawing.Point(22, 213);
this.label13.Name = "label13";
this.label13.Size = new System.Drawing.Size(65, 13);
this.label13.TabIndex = 46;
this.label13.Text = "Extents Min:";
//
// HashLabel
//
this.HashLabel.AutoSize = true;
this.HashLabel.Location = new System.Drawing.Point(211, 16);
this.HashLabel.Name = "HashLabel";
this.HashLabel.Size = new System.Drawing.Size(35, 13);
this.HashLabel.TabIndex = 45;
this.HashLabel.Text = "Hash:";
//
// ArchetypeNameTextBox
//
this.ArchetypeNameTextBox.Location = new System.Drawing.Point(61, 13);
this.ArchetypeNameTextBox.Name = "ArchetypeNameTextBox";
this.ArchetypeNameTextBox.Size = new System.Drawing.Size(144, 20);
this.ArchetypeNameTextBox.TabIndex = 44;
this.ArchetypeNameTextBox.TextChanged += new System.EventHandler(this.ArchetypeNameTextBox_TextChanged);
//
// label7
//
this.label7.AutoSize = true;
this.label7.Location = new System.Drawing.Point(11, 16);
this.label7.Name = "label7";
this.label7.Size = new System.Drawing.Size(35, 13);
this.label7.TabIndex = 43;
this.label7.Text = "Name";
//
// 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(14, 67);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(384, 132);
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(104, 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);
//
// PositionTextBox
//
this.PositionTextBox.Location = new System.Drawing.Point(61, 40);
this.PositionTextBox.Name = "PositionTextBox";
this.PositionTextBox.ReadOnly = true;
this.PositionTextBox.Size = new System.Drawing.Size(185, 20);
this.PositionTextBox.TabIndex = 41;
//
// 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(11, 43);
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 = 40;
this.label1.Text = "Grass editing TODO!"; this.label1.Text = "Position";
//
// GrassGoToButton
//
this.GrassGoToButton.Location = new System.Drawing.Point(252, 38);
this.GrassGoToButton.Name = "GrassGoToButton";
this.GrassGoToButton.Size = new System.Drawing.Size(56, 23);
this.GrassGoToButton.TabIndex = 39;
this.GrassGoToButton.Text = "Go To";
this.GrassGoToButton.UseVisualStyleBackColor = true;
this.GrassGoToButton.Click += new System.EventHandler(this.GrassGoToButton_Click);
//
// GrassDeleteButton
//
this.GrassDeleteButton.Location = new System.Drawing.Point(278, 298);
this.GrassDeleteButton.Name = "GrassDeleteButton";
this.GrassDeleteButton.Size = new System.Drawing.Size(95, 23);
this.GrassDeleteButton.TabIndex = 38;
this.GrassDeleteButton.Text = "Delete Batch";
this.GrassDeleteButton.UseVisualStyleBackColor = true;
this.GrassDeleteButton.Click += new System.EventHandler(this.GrassDeleteButton_Click);
//
// GrassAddToProjectButton
//
this.GrassAddToProjectButton.Location = new System.Drawing.Point(180, 298);
this.GrassAddToProjectButton.Name = "GrassAddToProjectButton";
this.GrassAddToProjectButton.Size = new System.Drawing.Size(95, 23);
this.GrassAddToProjectButton.TabIndex = 37;
this.GrassAddToProjectButton.Text = "Add to Project";
this.GrassAddToProjectButton.UseVisualStyleBackColor = true;
this.GrassAddToProjectButton.Click += new System.EventHandler(this.GrassAddToProjectButton_Click);
//
// BrushTab
//
this.BrushTab.Controls.Add(this.label16);
this.BrushTab.Controls.Add(this.label8);
this.BrushTab.Controls.Add(this.groupBox2);
this.BrushTab.Controls.Add(this.BrushModeCheckBox);
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(546, 329);
this.BrushTab.TabIndex = 1;
this.BrushTab.Text = " Brush";
this.BrushTab.UseVisualStyleBackColor = true;
//
// label16
//
this.label16.AutoSize = true;
this.label16.Location = new System.Drawing.Point(9, 224);
this.label16.Name = "label16";
this.label16.Size = new System.Drawing.Size(114, 13);
this.label16.TabIndex = 40;
this.label16.Text = "SHIFT + CTRL - Erase";
//
// label8
//
this.label8.AutoSize = true;
this.label8.Location = new System.Drawing.Point(9, 202);
this.label8.Name = "label8";
this.label8.Size = new System.Drawing.Size(68, 13);
this.label8.TabIndex = 21;
this.label8.Text = "CTRL - Paint";
//
// 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(259, 3);
this.groupBox2.Name = "groupBox2";
this.groupBox2.Size = new System.Drawing.Size(279, 153);
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(79, 17);
this.RandomizeScaleCheckBox.TabIndex = 18;
this.RandomizeScaleCheckBox.Text = "Randomize";
this.RandomizeScaleCheckBox.UseVisualStyleBackColor = true;
//
// BrushModeCheckBox
//
this.BrushModeCheckBox.AutoSize = true;
this.BrushModeCheckBox.Location = new System.Drawing.Point(8, 174);
this.BrushModeCheckBox.Name = "BrushModeCheckBox";
this.BrushModeCheckBox.Size = new System.Drawing.Size(83, 17);
this.BrushModeCheckBox.TabIndex = 39;
this.BrushModeCheckBox.Text = "Brush Mode";
this.BrushModeCheckBox.UseVisualStyleBackColor = true;
this.BrushModeCheckBox.CheckedChanged += new System.EventHandler(this.BrushModeCheckBox_CheckedChanged);
//
// 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(245, 153);
this.brushSettingsGroupBox.TabIndex = 38;
this.brushSettingsGroupBox.TabStop = false;
this.brushSettingsGroupBox.Text = "Brush Settings";
//
// DensityNumericUpDown
//
this.DensityNumericUpDown.Location = new System.Drawing.Point(109, 57);
this.DensityNumericUpDown.Maximum = new decimal(new int[] {
128,
0,
0,
0});
this.DensityNumericUpDown.Name = "DensityNumericUpDown";
this.DensityNumericUpDown.Size = new System.Drawing.Size(120, 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(109, 31);
this.RadiusNumericUpDown.Name = "RadiusNumericUpDown";
this.RadiusNumericUpDown.Size = new System.Drawing.Size(120, 20);
this.RadiusNumericUpDown.TabIndex = 11;
this.RadiusNumericUpDown.Value = new decimal(new int[] {
5,
0,
0,
0});
//
// 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";
//
// label18
//
this.label18.Location = new System.Drawing.Point(400, 259);
this.label18.Name = "label18";
this.label18.Size = new System.Drawing.Size(126, 53);
this.label18.TabIndex = 52;
this.label18.Text = "* The higher the threshold, the larger each batch will be when they are split.";
// //
// 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(554, 355);
this.Controls.Add(this.label1); this.Controls.Add(this.tabControl1);
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.GrassBatchTab.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.OptmizationThresholdNumericUpDown)).EndInit();
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.BrushTab.PerformLayout();
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();
this.ResumeLayout(false); this.ResumeLayout(false);
this.PerformLayout();
} }
#endregion #endregion
private System.Windows.Forms.TabControl tabControl1;
private System.Windows.Forms.TabPage GrassBatchTab;
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.Button GrassDeleteButton;
private System.Windows.Forms.Button GrassAddToProjectButton;
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.CheckBox BrushModeCheckBox;
private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.NumericUpDown LodDistNumericUpDown;
private System.Windows.Forms.Label label6;
private System.Windows.Forms.TextBox ArchetypeNameTextBox;
private System.Windows.Forms.Label label7;
private System.Windows.Forms.Label HashLabel;
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.TextBox textBox4;
private System.Windows.Forms.Label label13;
private System.Windows.Forms.Label label12;
private System.Windows.Forms.TextBox ScaleRangeTextBox;
private System.Windows.Forms.TextBox textBox5;
private System.Windows.Forms.Label label14;
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.Label label16;
private System.Windows.Forms.Label label8;
private System.Windows.Forms.Button OptimizeBatchButton;
private System.Windows.Forms.Label label17;
private System.Windows.Forms.NumericUpDown OptmizationThresholdNumericUpDown;
private System.Windows.Forms.Label label18;
private System.Windows.Forms.ToolTip OptimizeBatchButtonTooltip;
} }
} }

View File

@ -1,22 +1,27 @@
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.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
using CodeWalker.GameFiles;
using CodeWalker.Utils;
using CodeWalker.World;
using SharpDX;
// THINGS TODO
// - COMPLETED -- Better gizmo for grass brush (like a circle with a little line in the middle sticking upwards)
// - COMPLETED -- Remove grass instances using CTRL + SHIFT + LMB
// - 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?)
// As far as everything else goes, the brush works just fine. No issues so far besides the UpdateGraphics() method. (causes flicker sometimes)
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 +29,41 @@ 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;
} }
private void UpdateFormTitle() private void UpdateFormTitle()
@ -37,11 +71,228 @@ 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;
}
#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));
//if (res.HitBounds != null) // shouldn't happen but just in case..
//{
// var mat = BoundsMaterialTypes.GetMaterial(res.HitBounds.MaterialIndex);
// if (mat.Name != "DEFAULT")
// {
// var c = mat.Colour;
// }
//}
return res;
}
private void BatchChanged()
{
UpdateControls();
CurrentBatch.UpdateInstanceCount();
CurrentBatch.HasChanged = true;
ProjectForm.SetGrassBatchHasChanged(false);
}
#endregion
} }
} }

View File

@ -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>

View File

@ -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);

File diff suppressed because it is too large Load Diff

View File

@ -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)
@ -1270,6 +1269,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 +1438,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 +3972,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 +4180,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 +4858,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 +5048,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 +5484,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);

View File

@ -195,6 +195,14 @@ namespace CodeWalker.Rendering
} }
} }
public void Invalidate(YmapGrassInstanceBatch batch)
{
lock (updateSyncRoot)
{
instbatches.Invalidate(batch);
}
}
} }

View File

@ -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)
{ {

View File

@ -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;

View File

@ -75,6 +75,10 @@ namespace CodeWalker
bool ControlFireToggle = false; bool ControlFireToggle = false;
int ControlBrushTimer = 0;
bool ControlBrushEnabled;
Entity camEntity = new Entity(); Entity camEntity = new Entity();
PedEntity pedEntity = new PedEntity(); PedEntity pedEntity = new PedEntity();
@ -486,7 +490,7 @@ namespace CodeWalker
} }
if (ControlMode == WorldControlMode.Free) if (ControlMode == WorldControlMode.Free || ControlBrushEnabled)
{ {
if (Input.ShiftPressed) if (Input.ShiftPressed)
{ {
@ -1824,6 +1828,14 @@ namespace CodeWalker
} }
} }
public void UpdateGrassBatchGraphics(YmapGrassInstanceBatch grassBatch)
{
lock (Renderer.RenderSyncRoot)
{
Renderer.Invalidate(grassBatch);
}
}
public Vector3 GetCameraPosition() public Vector3 GetCameraPosition()
{ {
@ -2049,8 +2061,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)
{ {
@ -2105,16 +2117,17 @@ namespace CodeWalker
CurMouseHit.Clear(); CurMouseHit.Clear();
MouseRayCollisionEnabled = Input.CtrlPressed; //temporary...! //MouseRayCollisionEnabled = Input.CtrlPressed; //temporary...!
if (MouseRayCollisionEnabled) if (Input.CtrlPressed)
{ {
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,24 @@ 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 +4349,11 @@ namespace CodeWalker
{ {
camera.FollowEntity.Position = p; camera.FollowEntity.Position = p;
} }
public void GoToPosition(Vector3 p, Vector3 bound)
{
camera.FollowEntity.Position = p;
camera.TargetDistance = bound.Length();
}
private MapMarker AddMarker(Vector3 pos, string name, bool addtotxtbox = false) private MapMarker AddMarker(Vector3 pos, string name, bool addtotxtbox = false)
{ {
@ -5898,7 +5934,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 +6033,7 @@ namespace CodeWalker
SelectedMarker = null; SelectedMarker = null;
HideMarkerSelectionInfo(); HideMarkerSelectionInfo();
} }
ControlBrushTimer = 0;
} }
} }
@ -6011,47 +6048,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 +6076,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 +6141,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 +6323,7 @@ namespace CodeWalker
} }
} }
if (ControlMode != WorldControlMode.Free) if (ControlMode != WorldControlMode.Free || ControlBrushEnabled)
{ {
e.Handled = true; e.Handled = true;
} }
@ -7555,7 +7624,6 @@ namespace CodeWalker
} }
} }
public enum WorldControlMode public enum WorldControlMode
{ {
Free = 0, Free = 0,