mirror of
https://mirror.ghproxy.com/https://github.com/dexyfex/CodeWalker
synced 2026-05-16 14:14:45 +08:00
added grass painting
This commit is contained in:
@@ -1,22 +1,27 @@
|
||||
using CodeWalker.GameFiles;
|
||||
using System;
|
||||
using System;
|
||||
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 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
|
||||
{
|
||||
public partial class EditYmapGrassPanel : ProjectPanel
|
||||
{
|
||||
public ProjectForm ProjectForm;
|
||||
public YmapGrassInstanceBatch CurrentBatch { get; set; }
|
||||
|
||||
//private bool populatingui = false;
|
||||
|
||||
public EditYmapGrassPanel(ProjectForm owner)
|
||||
{
|
||||
@@ -24,12 +29,41 @@ namespace CodeWalker.Project.Panels
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public YmapGrassInstanceBatch CurrentBatch { get; set; }
|
||||
|
||||
#region Form
|
||||
|
||||
public void SetBatch(YmapGrassInstanceBatch batch)
|
||||
{
|
||||
CurrentBatch = batch;
|
||||
Tag = batch;
|
||||
LoadGrassBatch();
|
||||
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()
|
||||
@@ -37,11 +71,228 @@ namespace CodeWalker.Project.Panels
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user