新增动态水物理插件
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
#region
|
||||
|
||||
using NWH.DWP2.WaterObjects;
|
||||
using UnityEngine;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2.DemoContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Demo script that spawns a 3D grid of cubes with WaterObjects attached.
|
||||
/// Used for stress-testing and demonstrating the buoyancy system with multiple objects.
|
||||
/// </summary>
|
||||
public class CubeGridSpawner : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// Spacing between cubes on the Z axis.
|
||||
/// </summary>
|
||||
public float depth = 1.1f;
|
||||
|
||||
/// <summary>
|
||||
/// Spacing between cubes on the Y axis.
|
||||
/// </summary>
|
||||
public float height = 1.1f;
|
||||
|
||||
/// <summary>
|
||||
/// Spacing between cubes on the X axis.
|
||||
/// </summary>
|
||||
public float width = 1.1f;
|
||||
|
||||
/// <summary>
|
||||
/// Number of cubes to spawn along the X axis.
|
||||
/// </summary>
|
||||
public int xResolution = 10;
|
||||
|
||||
/// <summary>
|
||||
/// Number of cubes to spawn along the Y axis.
|
||||
/// </summary>
|
||||
public int yResolution = 10;
|
||||
|
||||
/// <summary>
|
||||
/// Number of cubes to spawn along the Z axis.
|
||||
/// </summary>
|
||||
public int zResolution = 10;
|
||||
|
||||
|
||||
private void Start()
|
||||
{
|
||||
for (int x = 0; x < xResolution; x++)
|
||||
{
|
||||
for (int y = 0; y < yResolution; y++)
|
||||
{
|
||||
for (int z = 0; z < zResolution; z++)
|
||||
{
|
||||
GameObject spawnedObject = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||||
spawnedObject.transform.position = new Vector3(x * width, y * height, z * depth);
|
||||
Rigidbody rb = spawnedObject.AddComponent<Rigidbody>();
|
||||
rb.mass = 200f;
|
||||
spawnedObject.AddComponent<WaterObject>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d5de9b2339abbff43b28c94b9c1fb517
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a647402bad23c2e4eb3c4eb8efff52a8
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,56 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
#region
|
||||
|
||||
using NWH.NUI;
|
||||
using NWH.DWP2.WaterObjects;
|
||||
using UnityEditor;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom inspector for MassFromChildren.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(MassFromChildren))]
|
||||
[CanEditMultipleObjects]
|
||||
public class MassFromChildrenEditor : DWP2NUIEditor
|
||||
{
|
||||
private MassFromChildren _massFromChildren;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Draws custom inspector GUI for MassFromChildren.
|
||||
/// </summary>
|
||||
public override bool OnInspectorNUI()
|
||||
{
|
||||
_massFromChildren = (MassFromChildren)target;
|
||||
|
||||
if (!base.OnInspectorNUI() || _massFromChildren == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
drawer.Info("Sums mass of all 'MassFromVolume's attached to this and child objects.");
|
||||
|
||||
if (drawer.Button("Calculate Mass From Children"))
|
||||
{
|
||||
_massFromChildren.Calculate();
|
||||
}
|
||||
|
||||
drawer.EndEditor(this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e2a38dc6b814caa8f2638c49a2972c7
|
||||
timeCreated: 1593013502
|
||||
@@ -0,0 +1,84 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
#region
|
||||
|
||||
using NWH.NUI;
|
||||
using NWH.DWP2.WaterObjects;
|
||||
using UnityEditor;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom inspector for MassFromVolume.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(MassFromVolume))]
|
||||
[CanEditMultipleObjects]
|
||||
public class MassFromVolumeEditor : DWP2NUIEditor
|
||||
{
|
||||
private MassFromVolume _massFromVolume;
|
||||
|
||||
|
||||
public void OnEnable()
|
||||
{
|
||||
_massFromVolume = (MassFromVolume)target;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Draws custom inspector GUI for MassFromVolume.
|
||||
/// </summary>
|
||||
public override bool OnInspectorNUI()
|
||||
{
|
||||
if (!base.OnInspectorNUI() || _massFromVolume == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_massFromVolume = (MassFromVolume)target;
|
||||
|
||||
// Material settings
|
||||
drawer.Field("mass", true, "kg");
|
||||
drawer.Field("volume", false, "m3");
|
||||
drawer.Info("Volume is auto-calculated from the mesh when either Calculate option is used.");
|
||||
|
||||
drawer.BeginSubsection("Density");
|
||||
drawer.Field("density", true, "kg/m3");
|
||||
if (drawer.Button("Calculate Mass From Density"))
|
||||
{
|
||||
foreach (MassFromVolume mfm in targets)
|
||||
{
|
||||
mfm.CalculateAndApplyFromDensity(mfm.density);
|
||||
}
|
||||
}
|
||||
|
||||
drawer.EndSubsection();
|
||||
|
||||
drawer.BeginSubsection("Material");
|
||||
drawer.Field("material");
|
||||
if (drawer.Button("Calculate Mass From Material"))
|
||||
{
|
||||
foreach (MassFromVolume mfm in targets)
|
||||
{
|
||||
mfm.CalculateAndApplyFromMaterial();
|
||||
}
|
||||
}
|
||||
|
||||
drawer.EndSubsection();
|
||||
|
||||
drawer.EndEditor(this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e4d7cea0eaeb4875948ee3242c6d65fe
|
||||
timeCreated: 1593873668
|
||||
@@ -0,0 +1,365 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
#region
|
||||
|
||||
using NWH.NUI;
|
||||
using NWH.DWP2.WaterObjects;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom inspector for WaterObject.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(WaterObject))]
|
||||
[CanEditMultipleObjects]
|
||||
[ExecuteAlways]
|
||||
public class WaterObjectEditor : DWP2NUIEditor
|
||||
{
|
||||
private bool _editorHasWarnings;
|
||||
private Texture2D _greyTexture;
|
||||
private Texture2D _originalMeshPreviewTexture;
|
||||
private float _previewHeight;
|
||||
private Texture2D _simMeshPreviewTexture;
|
||||
|
||||
private WaterObject _waterObject;
|
||||
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
_greyTexture = new Texture2D(10, 10);
|
||||
FillInTexture(ref _greyTexture, new Color32(66, 66, 66, 255));
|
||||
}
|
||||
|
||||
|
||||
private static void FillInTexture(ref Texture2D tex, Color color)
|
||||
{
|
||||
Color[] fillColorArray = tex.GetPixels();
|
||||
|
||||
for (int i = 0; i < fillColorArray.Length; i++)
|
||||
{
|
||||
fillColorArray[i] = color;
|
||||
}
|
||||
|
||||
tex.SetPixels(fillColorArray);
|
||||
tex.Apply();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Draws custom inspector GUI for WaterObject.
|
||||
/// </summary>
|
||||
public override bool OnInspectorNUI()
|
||||
{
|
||||
if (!base.OnInspectorNUI())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get water object and make sure it is initialized
|
||||
_waterObject = (WaterObject)target;
|
||||
|
||||
// Draw logo texture
|
||||
Rect logoRect = drawer.positionRect;
|
||||
logoRect.height = 60f;
|
||||
drawer.DrawEditorTexture(logoRect, "Dynamic Water Physics 2/Logos/WaterObjectLogo");
|
||||
drawer.AdvancePosition(logoRect.height);
|
||||
|
||||
if (_waterObject.originalMesh == null || _waterObject.serializedSimulationMesh == null ||
|
||||
_waterObject.SimulationMesh == null)
|
||||
{
|
||||
drawer.Space();
|
||||
drawer.Info("Simulation mesh not generated. Click 'Update Simulation Mesh' button below to fix this.",
|
||||
MessageType.Error);
|
||||
}
|
||||
|
||||
if (_waterObject.CurrentWaterDataProvider)
|
||||
{
|
||||
drawer.Info($"Current water data source: '{_waterObject.CurrentWaterDataProvider.GetType().Name}' " +
|
||||
$"on object '{_waterObject.CurrentWaterDataProvider.name}'.");
|
||||
}
|
||||
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
drawer.Info($"Forces are being applied to '{_waterObject.targetRigidbody}'.");
|
||||
}
|
||||
|
||||
drawer.BeginSubsection("Buoyancy");
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
drawer.Field("submergedVolume", false, "l");
|
||||
}
|
||||
|
||||
drawer.Field("buoyantForceCoefficient");
|
||||
drawer.Field("fluidDensity");
|
||||
drawer.EndSubsection();
|
||||
|
||||
drawer.BeginSubsection("Hydrodynamics");
|
||||
drawer.Field("hydrodynamicForceCoefficient");
|
||||
drawer.Field("slamForceCoefficient");
|
||||
drawer.Field("suctionForceCoefficient");
|
||||
drawer.Field("skinDragCoefficient");
|
||||
drawer.Field("velocityDotPower");
|
||||
drawer.EndSubsection();
|
||||
|
||||
drawer.BeginSubsection("Water");
|
||||
drawer.Field("calculateWaterHeights");
|
||||
drawer.Field("calculateWaterNormals");
|
||||
drawer.Field("calculateWaterFlows");
|
||||
|
||||
drawer.Field("defaultWaterHeight");
|
||||
drawer.Field("defaultWaterNormal");
|
||||
drawer.Field("defaultWaterFlow");
|
||||
drawer.EndSubsection();
|
||||
|
||||
// Simulation mesh
|
||||
drawer.BeginSubsection("Simulation Mesh Settings");
|
||||
if (drawer.Field("simplifyMesh").boolValue)
|
||||
{
|
||||
drawer.Field("targetTriangleCount");
|
||||
}
|
||||
|
||||
drawer.Field("convexifyMesh");
|
||||
drawer.Field("weldColocatedVertices");
|
||||
|
||||
if (drawer.Button("Update Simulation Mesh"))
|
||||
{
|
||||
UpdateSimulationMesh();
|
||||
}
|
||||
|
||||
if (drawer.Button("Toggle In-Scene Preview"))
|
||||
{
|
||||
ToggleInScenePreview();
|
||||
}
|
||||
|
||||
if (targets.Length == 1)
|
||||
{
|
||||
drawer.Label("Simulation Mesh Preview:");
|
||||
drawer.Space();
|
||||
if (Event.current.type == EventType.Repaint)
|
||||
{
|
||||
DrawPreviewTexture(_waterObject, drawer.positionRect, out _previewHeight);
|
||||
}
|
||||
|
||||
drawer.Space(_previewHeight);
|
||||
}
|
||||
|
||||
drawer.EndSubsection();
|
||||
|
||||
// Warnings
|
||||
drawer.BeginSubsection("Messages");
|
||||
DrawWarnings();
|
||||
drawer.EndSubsection();
|
||||
|
||||
drawer.EndEditor(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Draws validation warnings and errors.
|
||||
/// </summary>
|
||||
private void DrawWarnings()
|
||||
{
|
||||
if (targets.Length == 1)
|
||||
{
|
||||
_editorHasWarnings = false;
|
||||
|
||||
// Missing rigidbody
|
||||
if (_waterObject.targetRigidbody == null)
|
||||
{
|
||||
_waterObject.targetRigidbody = _waterObject.transform.GetComponentInParent<Rigidbody>(true);
|
||||
if (_waterObject.targetRigidbody == null)
|
||||
{
|
||||
drawer.Info("WaterObject requires a rigidbody attached to the object or its parent(s) to " +
|
||||
$"function. Add a rigidbody to object {_waterObject.name} or one of its parents.",
|
||||
MessageType.Error);
|
||||
_editorHasWarnings = true;
|
||||
|
||||
if (drawer.Button("Add a Rigidbody"))
|
||||
{
|
||||
foreach (WaterObject wo in targets)
|
||||
{
|
||||
wo.gameObject.AddComponent<Rigidbody>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Collider count
|
||||
if (_waterObject.targetRigidbody != null)
|
||||
{
|
||||
int colliderCount = _waterObject.targetRigidbody.transform.GetComponentsInChildren<Collider>()
|
||||
.Length;
|
||||
if (_waterObject.targetRigidbody.transform.GetComponentsInChildren<Collider>().Length == 0)
|
||||
{
|
||||
drawer.Info(
|
||||
$"Found {colliderCount} colliders attached to rigidbody {_waterObject.targetRigidbody.name} " +
|
||||
"and its children. At least one collider is required for a rigidbody to work properly.",
|
||||
MessageType.Error);
|
||||
_editorHasWarnings = true;
|
||||
|
||||
if (drawer.Button("Add a MeshCollider"))
|
||||
{
|
||||
foreach (WaterObject wo in targets)
|
||||
{
|
||||
MeshCollider mc = wo.gameObject.AddComponent<MeshCollider>();
|
||||
mc.convex = true;
|
||||
mc.isTrigger = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Excessive triangle count
|
||||
if (_waterObject.triangleCount > 150)
|
||||
{
|
||||
drawer.Info($"Possible excessive number of triangles detected ({_waterObject.triangleCount})." +
|
||||
" Use simplify mesh option to reduce the number of triangles, or if this is intentional ignore this message." +
|
||||
" Recommended number is 16-128.", MessageType.Warning);
|
||||
}
|
||||
|
||||
// Scale error
|
||||
if (_waterObject.transform.localScale.x <= 0
|
||||
|| _waterObject.transform.localScale.y <= 0
|
||||
|| _waterObject.transform.localScale.z <= 0)
|
||||
{
|
||||
drawer.Info(
|
||||
"Scale of this object is negative or zero on one or more of axes. Scale less than or equal to zero is not supported." +
|
||||
" WaterObject will still be calculated but with unpredictable results. ", MessageType.Error);
|
||||
}
|
||||
|
||||
if (!_editorHasWarnings)
|
||||
{
|
||||
drawer.Info($"No warnings or errors for object {_waterObject.name}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Regenerates simulation mesh for selected objects.
|
||||
/// </summary>
|
||||
private void UpdateSimulationMesh()
|
||||
{
|
||||
foreach (WaterObject wo in targets)
|
||||
{
|
||||
wo.StopSimMeshPreview();
|
||||
wo.GenerateSimMesh();
|
||||
Undo.RecordObject(wo, "Updated Simulation Mesh");
|
||||
EditorUtility.SetDirty(wo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Toggles in-scene preview of simulation mesh.
|
||||
/// </summary>
|
||||
private void ToggleInScenePreview()
|
||||
{
|
||||
foreach (WaterObject wo in targets)
|
||||
{
|
||||
if (wo.PreviewEnabled)
|
||||
{
|
||||
wo.StopSimMeshPreview();
|
||||
}
|
||||
else
|
||||
{
|
||||
wo.StartSimMeshPreview();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Draws side-by-side preview of original and simulation meshes.
|
||||
/// </summary>
|
||||
private void DrawPreviewTexture(WaterObject waterObject, Rect rect, out float previewHeight)
|
||||
{
|
||||
previewHeight = 0;
|
||||
|
||||
if (waterObject == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (waterObject.originalMesh == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (waterObject.serializedSimulationMesh.vertices == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (waterObject.SimulationMesh == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Tri count
|
||||
int originalTriCount = waterObject.originalMesh == null ? 0 : waterObject.originalMesh.triangles.Length / 3;
|
||||
int originalVertCount = waterObject.originalMesh == null ? 0 : waterObject.originalMesh.vertices.Length;
|
||||
int simulationTriCount = waterObject.serializedSimulationMesh.triangles.Length / 3;
|
||||
int simulationVertCount = waterObject.serializedSimulationMesh.vertices.Length;
|
||||
|
||||
_originalMeshPreviewTexture = AssetPreview.GetAssetPreview(waterObject.originalMesh);
|
||||
_simMeshPreviewTexture = waterObject.SimulationMesh == null
|
||||
? AssetPreview.GetAssetPreview(waterObject.originalMesh)
|
||||
: AssetPreview.GetAssetPreview(waterObject.SimulationMesh);
|
||||
|
||||
float startY = rect.y;
|
||||
float previewWidth = rect.width;
|
||||
float maxPreviewWidth = 480f;
|
||||
previewWidth = Mathf.Clamp(previewWidth, 240f, maxPreviewWidth);
|
||||
float margin = (rect.width - previewWidth) * 0.5f;
|
||||
float halfWidth = previewWidth * 0.5f;
|
||||
|
||||
Rect leftRect = new(rect.x + margin, startY, halfWidth, halfWidth);
|
||||
Rect rightRect = new(rect.x + halfWidth + margin, startY, halfWidth, halfWidth);
|
||||
|
||||
Material previewMaterial = new(Shader.Find("UI/Default"));
|
||||
|
||||
GUI.DrawTexture(leftRect, _originalMeshPreviewTexture == null ? _greyTexture : _originalMeshPreviewTexture);
|
||||
GUI.DrawTexture(rightRect, _simMeshPreviewTexture == null ? _greyTexture : _simMeshPreviewTexture);
|
||||
|
||||
GUIStyle centeredStyle = GUI.skin.GetStyle("Label");
|
||||
centeredStyle.alignment = TextAnchor.MiddleCenter;
|
||||
centeredStyle.normal.textColor = Color.white;
|
||||
|
||||
Rect leftLabelRect = leftRect;
|
||||
leftLabelRect.height = 20f;
|
||||
GUI.Label(leftLabelRect, "ORIGINAL", centeredStyle);
|
||||
|
||||
Rect rightLabelRect = rightRect;
|
||||
rightLabelRect.height = 20f;
|
||||
GUI.Label(rightLabelRect, "SIMULATION", centeredStyle);
|
||||
|
||||
Rect leftBottomLabelRect = leftRect;
|
||||
leftBottomLabelRect.y = leftRect.y + halfWidth - 20f;
|
||||
leftBottomLabelRect.height = 20f;
|
||||
GUI.Label(leftBottomLabelRect, $"{originalTriCount} tris, {originalVertCount} verts");
|
||||
|
||||
Rect rightBottomLabelrect = rightRect;
|
||||
rightBottomLabelrect.y = rightRect.y + halfWidth - 20f;
|
||||
rightBottomLabelrect.height = 20f;
|
||||
GUI.Label(rightBottomLabelrect, $"{simulationTriCount} tris, {simulationVertCount} verts");
|
||||
|
||||
previewHeight = halfWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 02a48d5b199a504459439d000ff549c1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
@@ -0,0 +1,88 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 93c55739f324f5540b9c04732218a774
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
externalObjects: {}
|
||||
serializedVersion: 9
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 1
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: -1
|
||||
aniso: -1
|
||||
mipBias: -100
|
||||
wrapU: -1
|
||||
wrapV: -1
|
||||
wrapW: -1
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 0
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 0
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 2
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID:
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
spritePackingTag:
|
||||
pSDRemoveMatte: 0
|
||||
pSDShowRemoveMatteOption: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 81 KiB |
@@ -0,0 +1,88 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ff137dafe58a6324c908e3cb796b45b9
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
externalObjects: {}
|
||||
serializedVersion: 9
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 1
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: -1
|
||||
aniso: -1
|
||||
mipBias: -100
|
||||
wrapU: -1
|
||||
wrapV: -1
|
||||
wrapW: -1
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 0
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 0
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 2
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID:
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
spritePackingTag:
|
||||
pSDRemoveMatte: 0
|
||||
pSDShowRemoveMatteOption: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,88 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eeffff81a7107a9498f73a9ac807c6e8
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
externalObjects: {}
|
||||
serializedVersion: 9
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 1
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: -1
|
||||
aniso: -1
|
||||
mipBias: -100
|
||||
wrapU: -1
|
||||
wrapV: -1
|
||||
wrapW: -1
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 0
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 0
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 2
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID:
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
spritePackingTag:
|
||||
pSDRemoveMatte: 0
|
||||
pSDShowRemoveMatteOption: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,59 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
#region
|
||||
|
||||
using NWH.Common.CoM;
|
||||
using UnityEngine;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2.WaterObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// Calculates mass of a Rigidbody from the children that have MassFromVolume script attached.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(Rigidbody))]
|
||||
public class MassFromChildren : MonoBehaviour
|
||||
{
|
||||
private Rigidbody _rb;
|
||||
private string _result;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the total mass from all child MassFromVolume components and applies it to the Rigidbody.
|
||||
/// Also updates the VariableCenterOfMass component if present.
|
||||
/// </summary>
|
||||
public void Calculate()
|
||||
{
|
||||
_rb = GetComponent<Rigidbody>();
|
||||
float massSum = 0;
|
||||
|
||||
_result = "Calculated mass from: ";
|
||||
foreach (MassFromVolume mam in GetComponentsInChildren<MassFromVolume>())
|
||||
{
|
||||
massSum += mam.mass;
|
||||
_result += $"{mam.name} ({mam.mass})";
|
||||
}
|
||||
|
||||
_result += $". Total mass: {massSum}.";
|
||||
Debug.Log(_result);
|
||||
|
||||
if (massSum > 0.001f)
|
||||
{
|
||||
_rb.mass = massSum;
|
||||
|
||||
VariableCenterOfMass vcom = GetComponent<VariableCenterOfMass>();
|
||||
if (vcom != null)
|
||||
{
|
||||
vcom.baseMass = massSum;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a52b52ef952dc2147bf915776311b00b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,139 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
#region
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2.WaterObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// Script to calculate mass from mesh volume and material density.
|
||||
/// Removes need for guessing mass and ensures that all objects of same density are submerged equally.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(WaterObject))]
|
||||
public class MassFromVolume : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// Calculated density of the material in kg/m³.
|
||||
/// </summary>
|
||||
public float density;
|
||||
|
||||
/// <summary>
|
||||
/// Calculated mass of the object in kg.
|
||||
/// </summary>
|
||||
public float mass;
|
||||
|
||||
/// <summary>
|
||||
/// Material preset containing density value.
|
||||
/// </summary>
|
||||
public WaterObjectMaterial material;
|
||||
|
||||
/// <summary>
|
||||
/// Calculated volume of the simulation mesh in m³.
|
||||
/// </summary>
|
||||
public float volume;
|
||||
|
||||
private WaterObject _waterObject;
|
||||
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_waterObject = GetComponent<WaterObject>();
|
||||
if (_waterObject == null)
|
||||
{
|
||||
Debug.LogError("MassFromVolume requires WaterObject.");
|
||||
}
|
||||
|
||||
if (material == null)
|
||||
{
|
||||
SetDefaultAsMaterial();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sets the default WaterObjectMaterial from Resources.
|
||||
/// </summary>
|
||||
public void SetDefaultAsMaterial()
|
||||
{
|
||||
material = Resources.Load<WaterObjectMaterial>("Dynamic Water Physics 2/DefaultWaterObjectMaterial");
|
||||
}
|
||||
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
SetDefaultAsMaterial();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets volume of the simulation mesh. Scale-sensitive.
|
||||
/// </summary>
|
||||
public void CalculateSimulationMeshVolume()
|
||||
{
|
||||
if (_waterObject == null)
|
||||
{
|
||||
_waterObject = GetComponent<WaterObject>();
|
||||
}
|
||||
|
||||
if (_waterObject.SimulationMesh == null)
|
||||
{
|
||||
Debug.LogWarning(
|
||||
"No simulation mesh assigned/generated. Make sure that simulation mesh of WaterObject is not empty - " +
|
||||
"if this is the first time setup try clicking 'Update Simulation Mesh' on WaterObject.");
|
||||
}
|
||||
|
||||
volume = _waterObject.SimulationMesh == null
|
||||
? 0.00000001f
|
||||
: Mathf.Clamp(MeshUtility.VolumeOfMesh(_waterObject.SimulationMesh, _waterObject.transform),
|
||||
0f, Mathf.Infinity);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Calculates mass from the assigned material's density and applies it to the Rigidbody.
|
||||
/// </summary>
|
||||
/// <returns>Calculated mass value.</returns>
|
||||
public float CalculateAndApplyFromMaterial()
|
||||
{
|
||||
return CalculateAndApplyFromDensity(material.density);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Calculates mass from the given density and applies it to the Rigidbody.
|
||||
/// </summary>
|
||||
/// <param name="density">Material density in kg/m³.</param>
|
||||
/// <returns>Calculated mass value.</returns>
|
||||
public float CalculateAndApplyFromDensity(float density)
|
||||
{
|
||||
if (material != null)
|
||||
{
|
||||
if (_waterObject == null)
|
||||
{
|
||||
_waterObject = GetComponent<WaterObject>();
|
||||
}
|
||||
|
||||
CalculateSimulationMeshVolume();
|
||||
|
||||
mass = density * volume;
|
||||
if (_waterObject.targetRigidbody != null && mass > 0)
|
||||
{
|
||||
_waterObject.targetRigidbody.mass = mass;
|
||||
}
|
||||
|
||||
return mass;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5ca3428d119d4dc5a018840803e81994
|
||||
timeCreated: 1593873585
|
||||
@@ -0,0 +1,233 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
#region
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NWH.DWP2.MeshDecimation;
|
||||
using NWH.DWP2.MiConvexHull;
|
||||
using UnityEngine;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2.WaterObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility class for mesh processing operations used by WaterObject.
|
||||
/// Handles mesh simplification, convexification, vertex welding, and volume calculations.
|
||||
/// </summary>
|
||||
public static class MeshUtility
|
||||
{
|
||||
/// <summary>
|
||||
/// Generates a simulation mesh from the original mesh with optional processing steps.
|
||||
/// Processes the mesh according to the specified flags and simplification ratio.
|
||||
/// </summary>
|
||||
/// <param name="originalMesh">Source mesh to process.</param>
|
||||
/// <param name="simMesh">Output simulation mesh.</param>
|
||||
/// <param name="simplifyMesh">Should the mesh be simplified to reduce triangle count.</param>
|
||||
/// <param name="convexifyMesh">Should the mesh be made convex.</param>
|
||||
/// <param name="weldColocatedVertices">Should vertices at the same position be merged.</param>
|
||||
/// <param name="simplificationRatio">Target triangle count as ratio of original (0-1).</param>
|
||||
public static void GenerateSimMesh(ref Mesh originalMesh, ref Mesh simMesh,
|
||||
bool simplifyMesh = false, bool convexifyMesh = false, bool weldColocatedVertices = true,
|
||||
float simplificationRatio = 1f)
|
||||
{
|
||||
simMesh.vertices = originalMesh.vertices;
|
||||
simMesh.triangles = originalMesh.triangles;
|
||||
|
||||
if (simplifyMesh)
|
||||
{
|
||||
simMesh = GenerateSimplifiedMesh(ref originalMesh, ref simMesh, simplificationRatio);
|
||||
}
|
||||
|
||||
if (convexifyMesh)
|
||||
{
|
||||
simMesh = GenerateConvexMesh(simMesh);
|
||||
}
|
||||
|
||||
if (weldColocatedVertices)
|
||||
{
|
||||
WeldVertices(ref simMesh);
|
||||
}
|
||||
|
||||
simMesh.name = "DWP_SIM_MESH";
|
||||
simMesh.RecalculateNormals();
|
||||
simMesh.RecalculateTangents();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Generate mesh from vertices and triangles.
|
||||
/// </summary>
|
||||
/// <param name="vertices"> Array of vertices. </param>
|
||||
/// <param name="triangles"> Array of triangles (indices). </param>
|
||||
/// <returns> </returns>
|
||||
public static Mesh GenerateMesh(Vector3[] vertices, int[] triangles)
|
||||
{
|
||||
Mesh m = new();
|
||||
m.vertices = vertices;
|
||||
m.triangles = triangles;
|
||||
m.RecalculateBounds();
|
||||
m.RecalculateNormals();
|
||||
m.name = "DWP_SIM_MESH";
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Merges vertices that are closer than the specified distance threshold.
|
||||
/// Improves performance by reducing vertex count and removing duplicates.
|
||||
/// </summary>
|
||||
/// <param name="aMesh">Mesh to process.</param>
|
||||
/// <param name="aMaxDelta">Maximum distance between vertices to be considered duplicates.</param>
|
||||
public static void WeldVertices(ref Mesh aMesh, float aMaxDelta = 0.001f)
|
||||
{
|
||||
Vector3[] verts = aMesh.vertices;
|
||||
List<int> newVerts = new();
|
||||
int[] map = new int[verts.Length];
|
||||
// create mapping and filter duplicates.
|
||||
for (int i = 0; i < verts.Length; i++)
|
||||
{
|
||||
Vector3 p = verts[i];
|
||||
bool duplicate = false;
|
||||
for (int i2 = 0; i2 < newVerts.Count; i2++)
|
||||
{
|
||||
int a = newVerts[i2];
|
||||
if ((verts[a] - p).sqrMagnitude <= aMaxDelta)
|
||||
{
|
||||
map[i] = i2;
|
||||
duplicate = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!duplicate)
|
||||
{
|
||||
map[i] = newVerts.Count;
|
||||
newVerts.Add(i);
|
||||
}
|
||||
}
|
||||
|
||||
Vector3[] verts2 = new Vector3[newVerts.Count];
|
||||
for (int i = 0; i < newVerts.Count; i++)
|
||||
{
|
||||
int a = newVerts[i];
|
||||
verts2[i] = verts[a];
|
||||
}
|
||||
|
||||
int[] tris = aMesh.triangles;
|
||||
for (int i = 0; i < tris.Length; i++)
|
||||
{
|
||||
tris[i] = map[tris[i]];
|
||||
}
|
||||
|
||||
aMesh.triangles = tris;
|
||||
aMesh.vertices = verts2;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Reduces poly count of the mesh while trying to preserve features.
|
||||
/// </summary>
|
||||
/// <param name="om"> Mesh to simplify. </param>
|
||||
/// <param name="ratio"> Percent of the triangles the new mesh will have </param>
|
||||
/// <returns> </returns>
|
||||
private static Mesh GenerateSimplifiedMesh(ref Mesh om, ref Mesh dummyMesh, float ratio)
|
||||
{
|
||||
MeshDecimate meshDecimate = new();
|
||||
meshDecimate.ratio = ratio;
|
||||
meshDecimate.PreCalculate(om);
|
||||
meshDecimate.Calculate(om);
|
||||
|
||||
Mesh sm = new();
|
||||
sm.vertices = meshDecimate.finalVertices;
|
||||
sm.triangles = meshDecimate.finalTriangles;
|
||||
sm.normals = meshDecimate.finalNormals;
|
||||
sm.name = "DWP_SIM_MESH";
|
||||
return sm;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the signed volume of a triangle with respect to the origin.
|
||||
/// Used for mesh volume calculations.
|
||||
/// </summary>
|
||||
/// <param name="p1">First vertex.</param>
|
||||
/// <param name="p2">Second vertex.</param>
|
||||
/// <param name="p3">Third vertex.</param>
|
||||
/// <returns>Signed volume of the triangle.</returns>
|
||||
public static float SignedVolumeOfTriangle(Vector3 p1, Vector3 p2, Vector3 p3)
|
||||
{
|
||||
float v321 = p3.x * p2.y * p1.z;
|
||||
float v231 = p2.x * p3.y * p1.z;
|
||||
float v312 = p3.x * p1.y * p2.z;
|
||||
float v132 = p1.x * p3.y * p2.z;
|
||||
float v213 = p2.x * p1.y * p3.z;
|
||||
float v123 = p1.x * p2.y * p3.z;
|
||||
return 1.0f / 6.0f * (-v321 + v231 + v312 - v132 - v213 + v123);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the volume of a mesh in world space.
|
||||
/// Takes into account the transform's scale, position and rotation.
|
||||
/// </summary>
|
||||
/// <param name="mesh">Mesh to calculate volume for.</param>
|
||||
/// <param name="transform">Transform containing scale information.</param>
|
||||
/// <returns>Volume of the mesh in cubic meters.</returns>
|
||||
public static float VolumeOfMesh(Mesh mesh, Transform transform)
|
||||
{
|
||||
float volume = 0;
|
||||
Vector3[] vertices = mesh.vertices;
|
||||
int[] triangles = mesh.triangles;
|
||||
Matrix4x4 transformMatrix = transform.localToWorldMatrix;
|
||||
for (int i = 0; i < mesh.triangles.Length; i += 3)
|
||||
{
|
||||
Vector3 p1 = transformMatrix.MultiplyPoint(vertices[triangles[i + 0]]);
|
||||
Vector3 p2 = transformMatrix.MultiplyPoint(vertices[triangles[i + 1]]);
|
||||
Vector3 p3 = transformMatrix.MultiplyPoint(vertices[triangles[i + 2]]);
|
||||
volume += SignedVolumeOfTriangle(p1, p2, p3);
|
||||
}
|
||||
|
||||
return Mathf.Abs(volume);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a convex hull from the input mesh.
|
||||
/// Useful for partially hollow meshes or open hulls.
|
||||
/// </summary>
|
||||
/// <param name="mesh">Mesh to convexify.</param>
|
||||
/// <returns>Convex mesh wrapping the input mesh.</returns>
|
||||
public static Mesh GenerateConvexMesh(Mesh mesh)
|
||||
{
|
||||
IEnumerable<Vector3> stars = mesh.vertices;
|
||||
Mesh m = new();
|
||||
|
||||
List<int> triangles = new();
|
||||
List<Vertex> vertices = stars.Select(x => new Vertex(x)).ToList();
|
||||
|
||||
ConvexHull<Vertex, DefaultConvexFace<Vertex>> result = ConvexHull.Create(vertices);
|
||||
m.vertices = result.Points.Select(x => x.ToVec()).ToArray();
|
||||
List<Vertex> xxx = result.Points.ToList();
|
||||
|
||||
foreach (DefaultConvexFace<Vertex> face in result.Faces)
|
||||
{
|
||||
triangles.Add(xxx.IndexOf(face.Vertices[0]));
|
||||
triangles.Add(xxx.IndexOf(face.Vertices[1]));
|
||||
triangles.Add(xxx.IndexOf(face.Vertices[2]));
|
||||
}
|
||||
|
||||
m.triangles = triangles.ToArray();
|
||||
m.RecalculateNormals();
|
||||
m.name = "DWP_SIM_MESH";
|
||||
return m;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9bdaf727cb1f5724aa53bbd6e62bb7a9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,65 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
#region
|
||||
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2.WaterObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// Serializable representation of a mesh.
|
||||
/// Used to store simulation mesh data without keeping mesh references in game files.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class SerializedMesh
|
||||
{
|
||||
/// <summary>
|
||||
/// Triangle indices of the mesh.
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
public int[] triangles;
|
||||
|
||||
/// <summary>
|
||||
/// Vertex positions of the mesh.
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
public Vector3[] vertices;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Serializes a mesh into vertex and triangle arrays.
|
||||
/// </summary>
|
||||
/// <param name="mesh">Mesh to serialize.</param>
|
||||
public void Serialize(Mesh mesh)
|
||||
{
|
||||
vertices = mesh.vertices;
|
||||
triangles = mesh.triangles;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes the stored data back into a Mesh object.
|
||||
/// </summary>
|
||||
/// <returns>Reconstructed mesh or null if data is invalid.</returns>
|
||||
public Mesh Deserialize()
|
||||
{
|
||||
if (vertices != null && triangles != null)
|
||||
{
|
||||
Mesh m = MeshUtility.GenerateMesh(vertices, triangles);
|
||||
m.name = "DWP_SIM_MESH";
|
||||
return m;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc00acaede32ab3419ea82210d3abb43
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a203ee8c91bbba5449f330d55ce28696
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,72 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
#region
|
||||
|
||||
using NWH.Common.Utility;
|
||||
using NWH.DWP2.WaterObjects;
|
||||
using UnityEngine;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2.WaterData
|
||||
{
|
||||
/// <summary>
|
||||
/// Simple water data provider for flat, static water surfaces.
|
||||
/// Uses a constant water height based on the transform's Y position.
|
||||
/// Does not support water height queries, waves, normals, or flow.
|
||||
/// </summary>
|
||||
public class FlatWaterDataProvider : WaterDataProvider
|
||||
{
|
||||
public override bool SupportsWaterHeightQueries()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public override bool SupportsWaterNormalQueries()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public override bool SupportsWaterFlowQueries()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public override void GetWaterHeights(WaterObject waterObject, ref Vector3[] points, ref float[] waterHeights)
|
||||
{
|
||||
float waterHeight = transform.position.y;
|
||||
waterHeights.Fill(waterHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
namespace NWH.DWP2.WaterData
|
||||
{
|
||||
using UnityEditor;
|
||||
|
||||
[CustomEditor(typeof(FlatWaterDataProvider))]
|
||||
[CanEditMultipleObjects]
|
||||
public class FlatWaterDataProviderEditor : WaterDataProviderEditor
|
||||
{
|
||||
protected override void DrawStatus(WaterDataProvider provider)
|
||||
{
|
||||
drawer.BeginSubsection("Status");
|
||||
drawer.Info($"Water height: {provider.transform.position.y:F2}m (from transform Y position)");
|
||||
drawer.EndSubsection();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9f46e165d5079ce41ba5716d9518728c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,238 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
#region
|
||||
|
||||
using NWH.DWP2.WaterObjects;
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
using UnityEngine;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2.WaterData
|
||||
{
|
||||
/// <summary>
|
||||
/// Water data provider that uses raycasting to detect water surface.
|
||||
/// Works with any water system that has a collider.
|
||||
/// Supports water height and normal queries through raycasting.
|
||||
/// Uses Unity's job system for parallel raycasts.
|
||||
/// </summary>
|
||||
public class RaycastWaterDataProvider : WaterDataProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Number of raycast commands to process per job batch.
|
||||
/// Higher values may improve performance on systems with more CPU cores.
|
||||
/// </summary>
|
||||
[Tooltip("Minimum number of RaycastCommands per job.")]
|
||||
public int commandsPerJob = 16;
|
||||
|
||||
/// <summary>
|
||||
/// Layer that floating objects are on.
|
||||
/// Used to prevent physics collisions between water and objects.
|
||||
/// </summary>
|
||||
[Tooltip(
|
||||
" Layer the water object(s) are on. Required to be able to disable physics collisions between water and object.")]
|
||||
public int objectLayer = 12;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum distance raycasts can travel in each direction.
|
||||
/// Raycasts extend this distance above and below each point.
|
||||
/// Lower values improve performance but may miss water surfaces that are far from the object.
|
||||
/// </summary>
|
||||
[Tooltip(
|
||||
" Raycasts will start at this distance above the point and extend this distance below the point. This means\r\n that if the water surface is raycastDistance below or above the point, it will not be detected.\r\n Using lower value will slightly improve performance of Raycasts.")]
|
||||
public float raycastDistance = 100f;
|
||||
|
||||
/// <summary>
|
||||
/// Layer that water surface colliders are on.
|
||||
/// Used to filter raycasts and prevent physics collisions.
|
||||
/// </summary>
|
||||
[Tooltip(
|
||||
" Layer the water is on. Required to be able to disable physics collisions between water and object.")]
|
||||
public int waterLayer = 4;
|
||||
|
||||
protected Vector3 _flow;
|
||||
protected RaycastHit _hit;
|
||||
protected LayerMask _layerMask;
|
||||
protected Mesh _mesh;
|
||||
|
||||
protected Vector3[] _normals;
|
||||
protected int _prevDataSize;
|
||||
protected QueryParameters _queryParameters;
|
||||
protected Ray _ray;
|
||||
|
||||
protected NativeArray<RaycastCommand> _raycastCommands;
|
||||
protected NativeArray<RaycastHit> _raycastHits;
|
||||
protected JobHandle _raycastJobHandle;
|
||||
protected Vector3 _rayDirection;
|
||||
protected Vector3 _rayStartOffset;
|
||||
protected Vector3 _tmp;
|
||||
protected RaycastCommand _tmpCommand;
|
||||
protected Vector3 _upVector;
|
||||
protected Vector2 _uv4;
|
||||
protected Vector3 _vertDir;
|
||||
protected int _vertIndex;
|
||||
protected Vector3 _zeroVector;
|
||||
|
||||
|
||||
public override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
Physics.IgnoreLayerCollision(waterLayer, objectLayer);
|
||||
_rayDirection = -Vector3.up;
|
||||
_rayStartOffset = -_rayDirection * raycastDistance * 0.5f;
|
||||
_prevDataSize = -1;
|
||||
_zeroVector = Vector3.zero;
|
||||
_upVector = Vector3.up;
|
||||
|
||||
_queryParameters = new QueryParameters();
|
||||
}
|
||||
|
||||
|
||||
public override bool SupportsWaterHeightQueries()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public override bool SupportsWaterNormalQueries()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public override bool SupportsWaterFlowQueries()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public override void GetWaterHeights(WaterObject waterObject, ref Vector3[] points, ref float[] waterHeights)
|
||||
{
|
||||
int n = points.Length;
|
||||
|
||||
bool queriesHitBackfaces = Physics.queriesHitBackfaces;
|
||||
bool queriesHitTriggers = Physics.queriesHitTriggers;
|
||||
|
||||
try
|
||||
{
|
||||
Physics.queriesHitBackfaces = false;
|
||||
Physics.queriesHitTriggers = false;
|
||||
|
||||
if (n != _prevDataSize)
|
||||
{
|
||||
_normals = new Vector3[n];
|
||||
Deallocate();
|
||||
_raycastCommands = new NativeArray<RaycastCommand>(n, Allocator.Persistent);
|
||||
_raycastHits = new NativeArray<RaycastHit>(n, Allocator.Persistent);
|
||||
}
|
||||
|
||||
_queryParameters.layerMask = 1 << waterLayer;
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
_tmpCommand.from = points[i] + _rayStartOffset;
|
||||
_tmpCommand.direction = _rayDirection;
|
||||
_tmpCommand.distance = raycastDistance;
|
||||
_tmpCommand.queryParameters = _queryParameters;
|
||||
_raycastCommands[i] = _tmpCommand;
|
||||
}
|
||||
|
||||
// Schedule raycast batch (removed duplicate scheduling that wasted 50% CPU time)
|
||||
_raycastJobHandle = RaycastCommand.ScheduleBatch(_raycastCommands, _raycastHits, 16);
|
||||
_raycastJobHandle.Complete();
|
||||
|
||||
Vector3 hitNormal;
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
hitNormal = _raycastHits[i].normal;
|
||||
waterHeights[i] = _raycastHits[i].point.y;
|
||||
_normals[i] = hitNormal == _zeroVector ? _upVector : hitNormal;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Always restore global physics settings, even if an exception occurs
|
||||
Physics.queriesHitBackfaces = queriesHitBackfaces;
|
||||
Physics.queriesHitTriggers = queriesHitTriggers;
|
||||
}
|
||||
|
||||
_prevDataSize = n;
|
||||
}
|
||||
|
||||
|
||||
public override void GetWaterNormals(WaterObject waterObject, ref Vector3[] points, ref Vector3[] waterNormals)
|
||||
{
|
||||
waterNormals = _normals;
|
||||
}
|
||||
|
||||
|
||||
public virtual void OnDisable()
|
||||
{
|
||||
Deallocate();
|
||||
}
|
||||
|
||||
|
||||
public virtual void OnDestroy()
|
||||
{
|
||||
Deallocate();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Deallocates native arrays used for raycasting.
|
||||
/// Called automatically on disable or destroy.
|
||||
/// </summary>
|
||||
public virtual void Deallocate()
|
||||
{
|
||||
_raycastJobHandle.Complete();
|
||||
if (_raycastCommands.IsCreated)
|
||||
{
|
||||
_raycastCommands.Dispose();
|
||||
}
|
||||
|
||||
if (_raycastHits.IsCreated)
|
||||
{
|
||||
_raycastHits.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
namespace NWH.DWP2.WaterData
|
||||
{
|
||||
using UnityEditor;
|
||||
|
||||
[CustomEditor(typeof(RaycastWaterDataProvider))]
|
||||
[CanEditMultipleObjects]
|
||||
public class RaycastWaterDataProviderEditor : WaterDataProviderEditor
|
||||
{
|
||||
protected override void DrawStatus(WaterDataProvider provider)
|
||||
{
|
||||
drawer.BeginSubsection("Status");
|
||||
drawer.Info("Uses physics raycasts to detect water surface colliders.");
|
||||
drawer.EndSubsection();
|
||||
}
|
||||
|
||||
protected override void DrawSettings(WaterDataProvider provider)
|
||||
{
|
||||
drawer.BeginSubsection("Settings");
|
||||
drawer.Field("waterLayer");
|
||||
drawer.Field("objectLayer");
|
||||
drawer.Field("raycastDistance", true, "m");
|
||||
drawer.Field("commandsPerJob");
|
||||
drawer.EndSubsection();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 223c7c03b51c437d903c9d67b650402e
|
||||
timeCreated: 1594572266
|
||||
@@ -0,0 +1,316 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
#region
|
||||
|
||||
using NWH.DWP2.WaterObjects;
|
||||
using UnityEngine;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2.WaterData
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for providing water surface data to WaterObjects.
|
||||
/// Implementations provide water height, flow velocity, and surface normal information.
|
||||
/// Uses a trigger collider to automatically detect and register WaterObjects that enter the water.
|
||||
/// Inherit from this class to create adapters for different water systems.
|
||||
/// </summary>
|
||||
public abstract class WaterDataProvider : MonoBehaviour
|
||||
{
|
||||
protected float[] _singleHeightArray;
|
||||
protected Vector3[] _singlePointArray;
|
||||
protected Collider _triggerCollider;
|
||||
|
||||
|
||||
public virtual void Awake()
|
||||
{
|
||||
_singleHeightArray = new float[1];
|
||||
_singlePointArray = new Vector3[1];
|
||||
|
||||
_triggerCollider = GetComponent<Collider>();
|
||||
if (_triggerCollider == null)
|
||||
{
|
||||
// Debug.LogWarning("WaterDataProvider requires a Collider with 'Is Trigger' ticked to be present " +
|
||||
// "on the same GameObject to act as a trigger volume. Creating one.");
|
||||
_triggerCollider = gameObject.AddComponent<SphereCollider>();
|
||||
_triggerCollider.isTrigger = true;
|
||||
((SphereCollider)_triggerCollider).radius = 1000000f;
|
||||
}
|
||||
|
||||
if (!_triggerCollider.isTrigger)
|
||||
{
|
||||
Debug.LogWarning("WaterDataProvider Collider has to have 'Is Trigger' ticked. Fixing.");
|
||||
_triggerCollider.isTrigger = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void OnDrawGizmos()
|
||||
{
|
||||
Gizmos.color = Color.green;
|
||||
Gizmos.DrawSphere(transform.position, 0.1f);
|
||||
}
|
||||
|
||||
|
||||
private void OnTriggerEnter(Collider other)
|
||||
{
|
||||
// Assign the current water data provider
|
||||
Rigidbody targetRigidbody = other.attachedRigidbody;
|
||||
if (targetRigidbody != null)
|
||||
{
|
||||
WaterObject[] targetWaterObjects = targetRigidbody.GetComponentsInChildren<WaterObject>();
|
||||
for (int i = 0; i < targetWaterObjects.Length; i++)
|
||||
{
|
||||
targetWaterObjects[i].OnEnterWaterDataProvider(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void OnTriggerExit(Collider other)
|
||||
{
|
||||
// Assign the current water data provider
|
||||
Rigidbody targetRigidbody = other.attachedRigidbody;
|
||||
if (targetRigidbody != null)
|
||||
{
|
||||
WaterObject[] targetWaterObjects = targetRigidbody.GetComponentsInChildren<WaterObject>();
|
||||
for (int i = 0; i < targetWaterObjects.Length; i++)
|
||||
{
|
||||
targetWaterObjects[i].OnExitWaterDataProvider(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Does this water system support water height queries?
|
||||
/// </summary>
|
||||
/// <returns> True if it does, false if it does not. </returns>
|
||||
public abstract bool SupportsWaterHeightQueries();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Does this water system support water normal queries?
|
||||
/// </summary>
|
||||
/// <returns> True if it does, false if it does not. </returns>
|
||||
public abstract bool SupportsWaterNormalQueries();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Does this water system support water velocity queries?
|
||||
/// </summary>
|
||||
/// <returns> True if it does, false if it does not. </returns>
|
||||
public abstract bool SupportsWaterFlowQueries();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns water height at each given point.
|
||||
/// Override this method to provide water height data from your water system.
|
||||
/// </summary>
|
||||
/// <param name="waterObject">WaterObject requesting the data.</param>
|
||||
/// <param name="points">Position array in world coordinates.</param>
|
||||
/// <param name="waterHeights">Water height array in world coordinates. Corresponds to positions.</param>
|
||||
public virtual void GetWaterHeights(WaterObject waterObject, ref Vector3[] points, ref float[] waterHeights)
|
||||
{
|
||||
// Do nothing. This will use the initial values of water heights (0).
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns water flow velocity at each given point.
|
||||
/// Override this method to provide water flow data from your water system.
|
||||
/// Flow should be in world space and relative to the world, not the WaterObject.
|
||||
/// </summary>
|
||||
/// <param name="waterObject">WaterObject requesting the data.</param>
|
||||
/// <param name="points">Position array in world coordinates.</param>
|
||||
/// <param name="waterFlows">Water flow velocity array in world coordinates. Corresponds to positions.</param>
|
||||
public virtual void GetWaterFlows(WaterObject waterObject, ref Vector3[] points, ref Vector3[] waterFlows)
|
||||
{
|
||||
// Do nothing. This will use the initial values of water velocities (0,0,0).
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns water surface normals at each given point.
|
||||
/// Override this method to provide water normal data from your water system.
|
||||
/// </summary>
|
||||
/// <param name="waterObject">WaterObject requesting the data.</param>
|
||||
/// <param name="points">Position array in world coordinates.</param>
|
||||
/// <param name="waterNormals">Water surface normal array in world coordinates. Corresponds to positions.</param>
|
||||
public virtual void GetWaterNormals(WaterObject waterObject, ref Vector3[] points, ref Vector3[] waterNormals)
|
||||
{
|
||||
// Do nothing. This will use the initial values of water normals (0,0,0).
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Queries water data based on enabled flags.
|
||||
/// Calls the appropriate getter methods based on which data types are requested.
|
||||
/// </summary>
|
||||
/// <param name="waterObject">WaterObject requesting the data.</param>
|
||||
/// <param name="points">Position array in world coordinates.</param>
|
||||
/// <param name="waterHeights">Output water heights array.</param>
|
||||
/// <param name="waterFlows">Output water flows array.</param>
|
||||
/// <param name="waterNormals">Output water normals array.</param>
|
||||
/// <param name="useWaterHeight">Should water heights be queried.</param>
|
||||
/// <param name="useWaterNormals">Should water normals be queried.</param>
|
||||
/// <param name="useWaterFlow">Should water flows be queried.</param>
|
||||
public void GetWaterHeightsFlowsNormals(WaterObject waterObject, ref Vector3[] points, ref float[] waterHeights,
|
||||
ref Vector3[] waterFlows, ref Vector3[] waterNormals, bool useWaterHeight, bool useWaterNormals,
|
||||
bool useWaterFlow)
|
||||
{
|
||||
if (useWaterHeight)
|
||||
{
|
||||
GetWaterHeights(waterObject, ref points, ref waterHeights);
|
||||
}
|
||||
|
||||
if (useWaterFlow && SupportsWaterFlowQueries())
|
||||
{
|
||||
GetWaterFlows(waterObject, ref points, ref waterFlows);
|
||||
}
|
||||
|
||||
if (useWaterNormals && SupportsWaterNormalQueries())
|
||||
{
|
||||
GetWaterNormals(waterObject, ref points, ref waterNormals);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns water height at a single point.
|
||||
/// Less efficient than batch queries but useful for one-off checks.
|
||||
/// </summary>
|
||||
/// <param name="waterObject">WaterObject requesting the data.</param>
|
||||
/// <param name="point">Position in world coordinates.</param>
|
||||
/// <returns>Water height at the given point.</returns>
|
||||
public virtual float GetWaterHeightSingle(WaterObject waterObject, Vector3 point)
|
||||
{
|
||||
_singlePointArray[0] = point;
|
||||
GetWaterHeights(waterObject, ref _singlePointArray, ref _singleHeightArray);
|
||||
return _singleHeightArray[0];
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a point is underwater.
|
||||
/// Accounts for wave height when water normals are supported.
|
||||
/// </summary>
|
||||
/// <param name="waterObject">WaterObject making the query.</param>
|
||||
/// <param name="worldPoint">Position to check in world coordinates.</param>
|
||||
/// <returns>True if the point is below the water surface.</returns>
|
||||
public bool PointInWater(WaterObject waterObject, Vector3 worldPoint)
|
||||
{
|
||||
return GetWaterHeight(waterObject, worldPoint) > worldPoint.y;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns water height at the given world position.
|
||||
/// </summary>
|
||||
/// <param name="waterObject">WaterObject making the query.</param>
|
||||
/// <param name="worldPoint">Position in world coordinates.</param>
|
||||
/// <returns>Water surface height at the given point.</returns>
|
||||
public float GetWaterHeight(WaterObject waterObject, Vector3 worldPoint)
|
||||
{
|
||||
return GetWaterHeightSingle(waterObject, worldPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
namespace NWH.DWP2.WaterData
|
||||
{
|
||||
using NWH.NUI;
|
||||
using UnityEditor;
|
||||
|
||||
/// <summary>
|
||||
/// Base editor for WaterDataProvider implementations.
|
||||
/// Draws common UI elements (capabilities, trigger volume) and provides virtual methods for derived editors.
|
||||
/// </summary>
|
||||
public class WaterDataProviderEditor : DWP2NUIEditor
|
||||
{
|
||||
public override bool OnInspectorNUI()
|
||||
{
|
||||
if (!base.OnInspectorNUI()) return false;
|
||||
|
||||
var provider = (WaterDataProvider)target;
|
||||
|
||||
DrawStatus(provider);
|
||||
DrawTriggerVolume(provider);
|
||||
DrawCapabilities(provider);
|
||||
DrawSettings(provider);
|
||||
|
||||
drawer.EndEditor(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override to draw provider-specific status information.
|
||||
/// </summary>
|
||||
protected virtual void DrawStatus(WaterDataProvider provider)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws the trigger volume information.
|
||||
/// </summary>
|
||||
protected virtual void DrawTriggerVolume(WaterDataProvider provider)
|
||||
{
|
||||
drawer.BeginSubsection("Trigger Volume");
|
||||
var collider = provider.GetComponent<UnityEngine.Collider>();
|
||||
if (collider != null)
|
||||
{
|
||||
string colliderType = collider.GetType().Name;
|
||||
string size = collider switch
|
||||
{
|
||||
UnityEngine.BoxCollider box => $"{box.size.x:F1} x {box.size.y:F1} x {box.size.z:F1}",
|
||||
UnityEngine.SphereCollider sphere => $"Radius: {sphere.radius:F1}",
|
||||
UnityEngine.CapsuleCollider capsule => $"Radius: {capsule.radius:F1}, Height: {capsule.height:F1}",
|
||||
UnityEngine.MeshCollider mesh => mesh.sharedMesh != null ? mesh.sharedMesh.name : "No mesh",
|
||||
_ => ""
|
||||
};
|
||||
drawer.Info($"{colliderType}{(size.Length > 0 ? $" ({size})" : "")}");
|
||||
if (!collider.isTrigger)
|
||||
{
|
||||
drawer.Info("Collider is not set as trigger!", MessageType.Warning);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
drawer.Info("No collider found. A large sphere trigger will be auto-created at runtime.", MessageType.Warning);
|
||||
}
|
||||
drawer.EndSubsection();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws the capabilities section showing supported query types.
|
||||
/// </summary>
|
||||
protected virtual void DrawCapabilities(WaterDataProvider provider)
|
||||
{
|
||||
drawer.BeginSubsection("Capabilities");
|
||||
drawer.Label($"Water Height: {(provider.SupportsWaterHeightQueries() ? "Yes" : "No")}");
|
||||
drawer.Label($"Water Normal: {(provider.SupportsWaterNormalQueries() ? "Yes" : "No")}");
|
||||
drawer.Label($"Water Flow: {(provider.SupportsWaterFlowQueries() ? "Yes" : "No")}");
|
||||
drawer.EndSubsection();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override to draw provider-specific settings.
|
||||
/// </summary>
|
||||
protected virtual void DrawSettings(WaterDataProvider provider)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool UseDefaultMargins() => false;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c4206096a6ed32444ab698262cf0dfa5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 211ca72301d713c4b8a753757f4d03f6
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dcbce59eb9e305848972c15acdf24261
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,88 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
#region
|
||||
|
||||
using NWH.NUI;
|
||||
using NWH.DWP2.WaterObjects;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2.WaterEffects
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom inspector for WaterParticleSystem.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(WaterParticleSystem))]
|
||||
[CanEditMultipleObjects]
|
||||
public class WaterParticleSystemEditor : DWP2NUIEditor
|
||||
{
|
||||
private WaterParticleSystem wps;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Draws custom inspector GUI for WaterParticleSystem.
|
||||
/// </summary>
|
||||
public override bool OnInspectorNUI()
|
||||
{
|
||||
if (!base.OnInspectorNUI())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
wps = (WaterParticleSystem)target;
|
||||
|
||||
// Draw logo texture
|
||||
Rect logoRect = drawer.positionRect;
|
||||
logoRect.height = 60f;
|
||||
drawer.DrawEditorTexture(logoRect, "Dynamic Water Physics 2/Logos/WaterParticleSystemLogo");
|
||||
drawer.AdvancePosition(logoRect.height);
|
||||
|
||||
drawer.BeginSubsection("Particle Settings");
|
||||
drawer.Field("emit");
|
||||
drawer.Field("renderQueue");
|
||||
drawer.Field("startSize");
|
||||
drawer.Field("sleepThresholdVelocity");
|
||||
drawer.Field("initialVelocityModifier");
|
||||
drawer.Field("maxInitialAlpha");
|
||||
drawer.Field("initialAlphaModifier");
|
||||
drawer.Field("emitPerCycle");
|
||||
drawer.Field("emitTimeInterval");
|
||||
drawer.Field("positionExtrapolationFrames");
|
||||
drawer.Field("surfaceElevation");
|
||||
drawer.EndSubsection();
|
||||
|
||||
/* // TODO - move this from editor script
|
||||
if(!wps.GetComponent<ParticleSystem>())
|
||||
{
|
||||
GameObject waterParticleSystemPrefab = Resources.Load<GameObject>("Dynamic Water Physics
|
||||
2/WaterParticleSystemPrefab");
|
||||
if (waterParticleSystemPrefab == null)
|
||||
{
|
||||
Debug.LogError("Could not load WaterParticleSystemPrefab from Resources.");
|
||||
}
|
||||
else
|
||||
{
|
||||
UnityEditorInternal.ComponentUtility.CopyComponent(waterParticleSystemPrefab
|
||||
.GetComponent<ParticleSystem>());
|
||||
UnityEditorInternal.ComponentUtility.PasteComponentAsNew(wps.gameObject);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
drawer.EndEditor(this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 53231489df986aa4cb71dddc0b72afaf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,65 @@
|
||||
Shader "WaterFX/WaterParticle" {
|
||||
Properties{
|
||||
_TintColor("Tint Color", Color) = (0.5,0.5,0.5,0.5)
|
||||
_MainTex("Particle Texture", 2D) = "white" {}
|
||||
}
|
||||
|
||||
Category
|
||||
{
|
||||
Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" "PreviewType" = "Plane" }
|
||||
|
||||
Blend SrcAlpha OneMinusSrcAlpha
|
||||
ColorMask RGB
|
||||
Cull Off Lighting Off ZWrite Off
|
||||
|
||||
SubShader
|
||||
{
|
||||
Pass
|
||||
{
|
||||
CGPROGRAM
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
|
||||
#include "UnityCG.cginc"
|
||||
|
||||
sampler2D _MainTex;
|
||||
fixed4 _TintColor;
|
||||
|
||||
struct appdata_t {
|
||||
float4 vertex : POSITION;
|
||||
fixed4 color : COLOR;
|
||||
float2 texcoord : TEXCOORD0;
|
||||
UNITY_VERTEX_INPUT_INSTANCE_ID
|
||||
};
|
||||
|
||||
struct v2f {
|
||||
float4 vertex : SV_POSITION;
|
||||
fixed4 color : COLOR;
|
||||
float2 texcoord : TEXCOORD0;
|
||||
UNITY_FOG_COORDS(1)
|
||||
UNITY_VERTEX_OUTPUT_STEREO
|
||||
};
|
||||
|
||||
float4 _MainTex_ST;
|
||||
|
||||
v2f vert(appdata_t v)
|
||||
{
|
||||
v2f o;
|
||||
UNITY_SETUP_INSTANCE_ID(v);
|
||||
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
|
||||
o.vertex = UnityObjectToClipPos(v.vertex);
|
||||
o.color = v.color * _TintColor;
|
||||
o.texcoord = TRANSFORM_TEX(v.texcoord,_MainTex);
|
||||
return o;
|
||||
}
|
||||
|
||||
fixed4 frag(v2f i) : SV_Target
|
||||
{
|
||||
fixed4 col = 2 * i.color * tex2D(_MainTex, i.texcoord);
|
||||
return col;
|
||||
}
|
||||
ENDCG
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 44e1bf9aba10f314fbc1aacd1e602f07
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures: []
|
||||
nonModifiableTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,320 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
#region
|
||||
|
||||
using NWH.Common.Utility;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
using Random = UnityEngine.Random;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2.WaterObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// Generates water spray and splash particles based on WaterObject simulation data.
|
||||
/// Emits particles at the waterline where triangles intersect the water surface.
|
||||
/// Particle emission is controlled by object velocity and triangle forces.
|
||||
/// Requires a ParticleSystem component.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(ParticleSystem))]
|
||||
public class WaterParticleSystem : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// Should the particle system emit?
|
||||
/// </summary>
|
||||
[Tooltip("Should the particle system emit?")]
|
||||
public bool emit = true;
|
||||
|
||||
/// <summary>
|
||||
/// How many particles should be emitted each 'emitTimeInterval' seconds.
|
||||
/// </summary>
|
||||
[Tooltip("How many particles should be emitted each 'emitTimeInterval' seconds.")]
|
||||
[Range(0f, 20f)]
|
||||
public int emitPerCycle = 6;
|
||||
|
||||
/// <summary>
|
||||
/// Determines how often the particles will be emitted.
|
||||
/// </summary>
|
||||
[Tooltip("Determines how often the particles will be emitted.")]
|
||||
[Range(0f, 0.1f)]
|
||||
public float emitTimeInterval = 0.04f;
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies initial alpha by this value. Alpha cannot be higher than maxInitialAlpha.
|
||||
/// </summary>
|
||||
[Tooltip("Multiplies initial alpha by this value. Alpha cannot be higher than maxInitialAlpha.")]
|
||||
[Range(0f, 10f)]
|
||||
public float initialAlphaModifier = 0.4f;
|
||||
|
||||
/// <summary>
|
||||
/// Determines how much velocity of the object will affect initial particle speed.
|
||||
/// </summary>
|
||||
[Tooltip("Determines how much velocity of the object will affect initial particle speed.")]
|
||||
[Range(0f, 5f)]
|
||||
public float initialVelocityModifier = 0.01f;
|
||||
|
||||
/// <summary>
|
||||
/// Limit initial alpha to this value.
|
||||
/// </summary>
|
||||
[Tooltip("Limit initial alpha to this value.")]
|
||||
[Range(0f, 1f)]
|
||||
public float maxInitialAlpha = 0.15f;
|
||||
|
||||
/// <summary>
|
||||
/// Number of frames ahead to predict particle emission position.
|
||||
/// Higher values spawn particles ahead of the object for better visual continuity.
|
||||
/// </summary>
|
||||
[Tooltip("Script will try to predict where the object will be in the next n frames.")]
|
||||
public int positionExtrapolationFrames = 4;
|
||||
|
||||
/// <summary>
|
||||
/// WaterObject to read simulation data from.
|
||||
/// Will auto-detect from parent if not assigned.
|
||||
/// </summary>
|
||||
public WaterObject ReferenceWaterObject;
|
||||
|
||||
/// <summary>
|
||||
/// Render queue of the particle material.
|
||||
/// </summary>
|
||||
[Tooltip("Render queue of the particle material.")]
|
||||
public int renderQueue = 2700;
|
||||
|
||||
/// <summary>
|
||||
/// Velocity object has to have to emit particles.
|
||||
/// </summary>
|
||||
[FormerlySerializedAs("sleepTresholdVelocity")]
|
||||
[Tooltip("Velocity object has to have to emit particles.")]
|
||||
[Range(0.1f, 5f)]
|
||||
public float sleepThresholdVelocity = 1.5f;
|
||||
|
||||
/// <summary>
|
||||
/// Initial size of the particle.
|
||||
/// </summary>
|
||||
[Tooltip("Initial size of the particle.")]
|
||||
[Range(0f, 64f)]
|
||||
public float startSize = 4f;
|
||||
|
||||
/// <summary>
|
||||
/// Elevation above water at which the particles will spawn. Used to avoid clipping.
|
||||
/// </summary>
|
||||
[Tooltip("Elevation above water at which the particles will spawn. Used to avoid clipping.")]
|
||||
[Range(0f, 0.1f)]
|
||||
public float surfaceElevation = 0.016f;
|
||||
|
||||
private ParticleSystem.NoiseModule _noiseModule;
|
||||
private ParticleSystem _particleSystem;
|
||||
private int _prevTriCount;
|
||||
private WaterObject _targetWaterObject;
|
||||
|
||||
private float _timeElapsed;
|
||||
private int _waterlineCount;
|
||||
private int[] _waterlineIndices;
|
||||
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (ReferenceWaterObject == null)
|
||||
{
|
||||
ReferenceWaterObject = GetComponentInParent<WaterObject>();
|
||||
}
|
||||
|
||||
_targetWaterObject = transform.GetComponentInParentsOrChildren<WaterObject>();
|
||||
if (_targetWaterObject == null)
|
||||
{
|
||||
Debug.LogError(
|
||||
$"{name}: WaterParticleSystem requires WaterObject attached to the same object or one of parent objects to function.");
|
||||
return;
|
||||
}
|
||||
|
||||
_particleSystem = GetComponent<ParticleSystem>();
|
||||
if (_particleSystem == null)
|
||||
{
|
||||
Debug.LogError("No ParticleSystem found.");
|
||||
}
|
||||
|
||||
_particleSystem.GetComponent<Renderer>().material.renderQueue = renderQueue;
|
||||
_noiseModule = _particleSystem.noise;
|
||||
|
||||
_prevTriCount = -999;
|
||||
}
|
||||
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
if (!emit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int triCount = _targetWaterObject.triangleCount;
|
||||
if (triCount > 0 && _prevTriCount != triCount)
|
||||
{
|
||||
_waterlineIndices = new int[triCount];
|
||||
}
|
||||
|
||||
if (_targetWaterObject.targetRigidbody.linearVelocity.magnitude > sleepThresholdVelocity)
|
||||
{
|
||||
EmitNew();
|
||||
}
|
||||
|
||||
_timeElapsed += Time.deltaTime;
|
||||
_prevTriCount = triCount;
|
||||
}
|
||||
|
||||
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
if (_waterlineIndices == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Gizmos.color = Color.magenta;
|
||||
for (int i = 0; i < _waterlineIndices.Length; i++)
|
||||
{
|
||||
if (_targetWaterObject.ResultStates[_waterlineIndices[i]] != 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector3 a = _targetWaterObject.ResultP0s[_waterlineIndices[i] * 6 + 2];
|
||||
Vector3 b = _targetWaterObject.ResultP0s[_waterlineIndices[i] * 6 + 1];
|
||||
Gizmos.DrawLine(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
DestroyImmediate(_particleSystem);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void EmitNew()
|
||||
{
|
||||
if (_targetWaterObject == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int triCount = _targetWaterObject.triangleCount;
|
||||
if (emit && _timeElapsed >= emitTimeInterval && triCount > 0f)
|
||||
{
|
||||
_timeElapsed = 0;
|
||||
|
||||
int emitted = 0;
|
||||
|
||||
// Emit allowed number of particles
|
||||
float elevation = 0;
|
||||
if (ReferenceWaterObject != null)
|
||||
{
|
||||
elevation = ReferenceWaterObject.GetWaterHeightSingle(Vector3.zero);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("Will not emit. WaterDataProvider is not present in the scene.");
|
||||
}
|
||||
|
||||
_waterlineCount = 0;
|
||||
for (int i = 0; i < triCount; i++)
|
||||
{
|
||||
if (_targetWaterObject.ResultStates[i] != 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_waterlineIndices[_waterlineCount] = i;
|
||||
_waterlineCount++;
|
||||
}
|
||||
|
||||
if (_waterlineCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float noise = startSize > 1f ? Mathf.Sqrt(startSize) * 0.1f : startSize * 0.1f;
|
||||
_noiseModule.strengthX = noise;
|
||||
_noiseModule.strengthY = 0f;
|
||||
_noiseModule.strengthZ = noise;
|
||||
|
||||
while (emitted < emitPerCycle)
|
||||
{
|
||||
int i = Random.Range(0, _waterlineCount);
|
||||
int waterLineTriIndex = _waterlineIndices[i];
|
||||
|
||||
EmitParticle(
|
||||
_targetWaterObject.ResultP0s[waterLineTriIndex * 6 + 2],
|
||||
_targetWaterObject.ResultP0s[waterLineTriIndex * 6 + 1],
|
||||
elevation,
|
||||
_targetWaterObject.ResultVelocities[waterLineTriIndex],
|
||||
_targetWaterObject.ResultNormals[waterLineTriIndex],
|
||||
_targetWaterObject.ResultForces[waterLineTriIndex],
|
||||
_targetWaterObject.ResultAreas[waterLineTriIndex]);
|
||||
|
||||
emitted++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Emits a single particle at the waterline between two points.
|
||||
/// Particle properties are calculated from triangle simulation data.
|
||||
/// </summary>
|
||||
/// <param name="p0">First waterline point.</param>
|
||||
/// <param name="p1">Second waterline point.</param>
|
||||
/// <param name="elevation">Water surface elevation.</param>
|
||||
/// <param name="velocity">Triangle velocity.</param>
|
||||
/// <param name="normal">Triangle normal.</param>
|
||||
/// <param name="force">Force acting on triangle.</param>
|
||||
/// <param name="area">Triangle area.</param>
|
||||
private void EmitParticle(Vector3 p0, Vector3 p1, float elevation, Vector3 velocity, Vector3 normal,
|
||||
Vector3 force, float area)
|
||||
{
|
||||
if (area < 0.0001f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Start velocity
|
||||
Vector3 startVelocity = normal * velocity.magnitude;
|
||||
startVelocity.y = 0f;
|
||||
startVelocity *= initialVelocityModifier;
|
||||
|
||||
// Start position
|
||||
Vector3 emissionPoint = (p0 + p1) / 2f;
|
||||
emissionPoint += Time.deltaTime * positionExtrapolationFrames * velocity;
|
||||
emissionPoint.y = elevation + surfaceElevation;
|
||||
|
||||
float normalizedForce = force.magnitude / area;
|
||||
float startAlpha = Mathf.Clamp(normalizedForce * 0.00005f * initialAlphaModifier, 0f, maxInitialAlpha);
|
||||
Color startColor = new(1f, 1f, 1f, startAlpha);
|
||||
float size = startSize;
|
||||
|
||||
if (startAlpha < 0.001f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ParticleSystem.EmitParams emitParams = new()
|
||||
{
|
||||
startColor = startColor,
|
||||
position = emissionPoint,
|
||||
velocity = startVelocity,
|
||||
startSize = size,
|
||||
};
|
||||
_particleSystem.Emit(emitParams, 1);
|
||||
_particleSystem.Play();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fd49d8bec9d80fd4ba7c4b663a177c6e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9118365f988afd4419c64e108a8bbc64
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 100
|
||||
icon: {fileID: 2800000, guid: ff137dafe58a6324c908e3cb796b45b9, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,40 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
#region
|
||||
|
||||
using NWH.DWP2.WaterObjects;
|
||||
using UnityEngine;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2.DemoContent
|
||||
{
|
||||
/// <summary>
|
||||
/// An example on how to add WaterObject to an existing object at runtime.
|
||||
/// </summary>
|
||||
public class WaterObjectFromScript : MonoBehaviour
|
||||
{
|
||||
private void Start()
|
||||
{
|
||||
WaterObject waterObject = gameObject.AddComponent<WaterObject>();
|
||||
waterObject.convexifyMesh = true;
|
||||
waterObject.simplifyMesh = true;
|
||||
waterObject.targetTriangleCount = 64;
|
||||
waterObject.GenerateSimMesh();
|
||||
|
||||
MassFromVolume massFromVolume = gameObject.AddComponent<MassFromVolume>();
|
||||
massFromVolume.SetDefaultAsMaterial(); // Use massFromVolume.SetMaterial() to use a different material
|
||||
// instead of the default one.
|
||||
|
||||
// Important. Without running Synchronize() WaterObject will not be registered by the WaterObjectManager and
|
||||
// the physics will not work. Just note that running Synchronize() can be expensive, so call it only after
|
||||
// all WaterObject setup is complete.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6635e16fc8df26d42ac0ca2af54e0d0e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d9c6f1ca4a146478a4b043413dbbebe
|
||||
timeCreated: 1593008450
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6155482a401f431aa1d81663d1ef6328
|
||||
timeCreated: 1593008532
|
||||
@@ -0,0 +1,46 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
#region
|
||||
|
||||
using NWH.NUI;
|
||||
using NWH.DWP2.WaterObjects;
|
||||
using UnityEditor;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom inspector for WaterObjectMaterial.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(WaterObjectMaterial))]
|
||||
[CanEditMultipleObjects]
|
||||
public class WaterObjectMaterialEditor : DWP2NUIEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// Draws custom inspector GUI for WaterObjectMaterial.
|
||||
/// </summary>
|
||||
public override bool OnInspectorNUI()
|
||||
{
|
||||
if (!base.OnInspectorNUI())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
drawer.Field("density");
|
||||
|
||||
drawer.EndEditor(this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7eea0dd9f8424a68a8be74e7efd1b4fa
|
||||
timeCreated: 1593008530
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 42d77d1ef45b8a3409309e441a100e2b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,15 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 8588a0103d7149e58d7a81d9673d5330, type: 3}
|
||||
m_Name: Aluminum
|
||||
m_EditorClassIdentifier:
|
||||
density: 2700
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e662794a6e68dba4cadc5c4d1e2ae943
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,15 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 8588a0103d7149e58d7a81d9673d5330, type: 3}
|
||||
m_Name: Bottle - Empty
|
||||
m_EditorClassIdentifier:
|
||||
density: 80
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: be16c1016c27c3542ac565a3f3c35650
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,15 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 8588a0103d7149e58d7a81d9673d5330, type: 3}
|
||||
m_Name: Brick
|
||||
m_EditorClassIdentifier:
|
||||
density: 1800
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a9bf2850b5467f94385f721462f69115
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,15 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 8588a0103d7149e58d7a81d9673d5330, type: 3}
|
||||
m_Name: Concrete
|
||||
m_EditorClassIdentifier:
|
||||
density: 2300
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b4c3c2ad764665344be08dd3dd062838
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,15 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 8588a0103d7149e58d7a81d9673d5330, type: 3}
|
||||
m_Name: Copper
|
||||
m_EditorClassIdentifier:
|
||||
density: 8300
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 89fbf5fa6b420cc4085be0019b14d787
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,15 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 8588a0103d7149e58d7a81d9673d5330, type: 3}
|
||||
m_Name: Cork
|
||||
m_EditorClassIdentifier:
|
||||
density: 120
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7a1c17fc0b7d09546b49c0193ea37d7f
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,15 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 8588a0103d7149e58d7a81d9673d5330, type: 3}
|
||||
m_Name: Default
|
||||
m_EditorClassIdentifier:
|
||||
density: 500
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a43f47272e61c6544964556b82effb8f
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,15 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 8588a0103d7149e58d7a81d9673d5330, type: 3}
|
||||
m_Name: Glass
|
||||
m_EditorClassIdentifier:
|
||||
density: 2700
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f119dee50e10486419f9a6b0133099dc
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,15 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 8588a0103d7149e58d7a81d9673d5330, type: 3}
|
||||
m_Name: HumanBody
|
||||
m_EditorClassIdentifier:
|
||||
density: 950
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eca70d469947b9844b97db7f30aac137
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,15 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 8588a0103d7149e58d7a81d9673d5330, type: 3}
|
||||
m_Name: Ice
|
||||
m_EditorClassIdentifier:
|
||||
density: 920
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 306f40901719f4040955d6e5dd9d963f
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,15 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 8588a0103d7149e58d7a81d9673d5330, type: 3}
|
||||
m_Name: Lead
|
||||
m_EditorClassIdentifier:
|
||||
density: 11300
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 247bba41d6378e44db048875c6da8551
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,15 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 8588a0103d7149e58d7a81d9673d5330, type: 3}
|
||||
m_Name: Oil
|
||||
m_EditorClassIdentifier:
|
||||
density: 920
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 33968881da0b22e44bc7d19bf2db5b7e
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,15 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 8588a0103d7149e58d7a81d9673d5330, type: 3}
|
||||
m_Name: Plastic
|
||||
m_EditorClassIdentifier:
|
||||
density: 1200
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 093f7874d7a6d6445abf82fd6c58ae02
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,15 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 8588a0103d7149e58d7a81d9673d5330, type: 3}
|
||||
m_Name: Rubber
|
||||
m_EditorClassIdentifier:
|
||||
density: 1200
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1e0a6964be0e52847b9601e732c2b54a
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,15 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 8588a0103d7149e58d7a81d9673d5330, type: 3}
|
||||
m_Name: Sand
|
||||
m_EditorClassIdentifier:
|
||||
density: 1500
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 01475980b546ea94fa509e12808073c2
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,15 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 8588a0103d7149e58d7a81d9673d5330, type: 3}
|
||||
m_Name: Snow
|
||||
m_EditorClassIdentifier:
|
||||
density: 560
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ac07bed869159c24ba7a8b21bf87ed01
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,15 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 8588a0103d7149e58d7a81d9673d5330, type: 3}
|
||||
m_Name: Steel
|
||||
m_EditorClassIdentifier:
|
||||
density: 7800
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d0d15a947a6ba224fb501c3b65fa59ba
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,15 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 8588a0103d7149e58d7a81d9673d5330, type: 3}
|
||||
m_Name: Titanium
|
||||
m_EditorClassIdentifier:
|
||||
density: 4500
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 317fa8ed3a25ba745b8cafd75e9dccb9
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,15 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 8588a0103d7149e58d7a81d9673d5330, type: 3}
|
||||
m_Name: Wood - Aspen
|
||||
m_EditorClassIdentifier:
|
||||
density: 420
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b86190ebd65a8704cbea83fd71b016df
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,15 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 8588a0103d7149e58d7a81d9673d5330, type: 3}
|
||||
m_Name: Wood - Balsa
|
||||
m_EditorClassIdentifier:
|
||||
density: 160
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 806fe45366f6c0d408aa620e96395d53
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,15 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 8588a0103d7149e58d7a81d9673d5330, type: 3}
|
||||
m_Name: Wood - Beech
|
||||
m_EditorClassIdentifier:
|
||||
density: 800
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e2aa2f3f85f035545a44a19bf82c0db5
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,15 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 8588a0103d7149e58d7a81d9673d5330, type: 3}
|
||||
m_Name: Wood - Birch
|
||||
m_EditorClassIdentifier:
|
||||
density: 670
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee8809750e051d944a439de7b897dbfe
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,15 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 8588a0103d7149e58d7a81d9673d5330, type: 3}
|
||||
m_Name: Wooden Crate - Empty
|
||||
m_EditorClassIdentifier:
|
||||
density: 90
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 97b293688a6d65641853dea8bdf789a8
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,33 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
#region
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2.WaterObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// ScriptableObject defining material properties for mass calculation.
|
||||
/// Contains density value used by MassFromVolume to calculate object mass.
|
||||
/// Create via Assets > Create > Dynamic Water Physics 2 > Water Object Material.
|
||||
/// </summary>
|
||||
[CreateAssetMenu(fileName = "WaterObjectMaterial", menuName = "Dynamic Water Physics 2/Water Object Material",
|
||||
order = 0)]
|
||||
public class WaterObjectMaterial : ScriptableObject
|
||||
{
|
||||
/// <summary>
|
||||
/// Material density in kg/m³.
|
||||
/// Used with mesh volume to calculate realistic object mass.
|
||||
/// Examples: Wood ~600, Ice ~920, Aluminum ~2700, Steel ~7850.
|
||||
/// </summary>
|
||||
public float density = 600;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8588a0103d7149e58d7a81d9673d5330
|
||||
timeCreated: 1593008450
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 10a3aeb7928d1c24cac05e4ee0d33433
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 754900265e733354f805bd57279cdfb7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,167 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
#region
|
||||
|
||||
using NWH.Common.CoM;
|
||||
using NWH.NUI;
|
||||
using NWH.DWP2.WaterObjects;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom inspector for WaterObjectWizard that automates WaterObject setup.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(WaterObjectWizard))]
|
||||
[CanEditMultipleObjects]
|
||||
public class WaterObjectWizardEditor : DWP2NUIEditor
|
||||
{
|
||||
private bool _wizardFinished;
|
||||
private WaterObjectWizard wow;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Draws custom inspector GUI for WaterObjectWizard.
|
||||
/// </summary>
|
||||
public override bool OnInspectorNUI()
|
||||
{
|
||||
if (!base.OnInspectorNUI())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
serializedObject.Update();
|
||||
wow = (WaterObjectWizard)target;
|
||||
|
||||
Rect logoRect = drawer.positionRect;
|
||||
logoRect.height = 60f;
|
||||
drawer.DrawEditorTexture(logoRect, "Dynamic Water Physics 2/Logos/WaterObjectWizardLogo");
|
||||
drawer.AdvancePosition(logoRect.height);
|
||||
|
||||
drawer.BeginSubsection("Options");
|
||||
drawer.Field("addWaterParticleSystem");
|
||||
drawer.EndSubsection();
|
||||
|
||||
if (drawer.Button("Auto-Setup"))
|
||||
{
|
||||
foreach (WaterObjectWizard wow in targets)
|
||||
{
|
||||
RunWizard(wow);
|
||||
}
|
||||
}
|
||||
|
||||
MeshFilter mf = wow.GetComponent<MeshFilter>();
|
||||
if (mf != null)
|
||||
{
|
||||
if (mf.sharedMesh != null)
|
||||
{
|
||||
if (mf.sharedMesh.triangles.Length / 3 > 4000)
|
||||
{
|
||||
drawer.Info(
|
||||
"Large mesh detected. Expect WaterObjectWizard to take a few moments to setup this object.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drawer.EndEditor();
|
||||
|
||||
if (_wizardFinished)
|
||||
{
|
||||
DestroyImmediate(wow);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Executes wizard setup for WaterObject component and dependencies.
|
||||
/// </summary>
|
||||
private void RunWizard(WaterObjectWizard wow)
|
||||
{
|
||||
GameObject target = wow.gameObject;
|
||||
|
||||
// Check for existing water object
|
||||
if (target.GetComponent<WaterObject>() != null)
|
||||
{
|
||||
Debug.LogWarning($"WaterObjectWizard: {target.name} already contains WaterObject component.");
|
||||
}
|
||||
|
||||
// Check for mesh filter
|
||||
MeshFilter mf = target.GetComponent<MeshFilter>();
|
||||
if (mf == null)
|
||||
{
|
||||
Debug.LogError("WaterObjectWizard: MeshFilter not found. WaterObject requires MeshFilter to work.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Add rigidbody
|
||||
Rigidbody parentRigidbody = target.transform.GetComponentInParent<Rigidbody>(true);
|
||||
if (parentRigidbody == null)
|
||||
{
|
||||
Debug.Log("WaterObjectWizard: Parent rigidbody not found. Adding new.");
|
||||
parentRigidbody = target.AddComponent<Rigidbody>();
|
||||
parentRigidbody.angularDamping = 0.15f;
|
||||
parentRigidbody.linearDamping = 0.05f;
|
||||
parentRigidbody.interpolation = RigidbodyInterpolation.None;
|
||||
|
||||
VariableCenterOfMass com = target.AddComponent<VariableCenterOfMass>();
|
||||
}
|
||||
|
||||
// Add collider
|
||||
int colliderCount = parentRigidbody.transform.GetComponentsInChildren<Collider>().Length;
|
||||
if (colliderCount == 0)
|
||||
{
|
||||
Debug.Log(
|
||||
$"WaterObjectWizard: Found 0 colliders on object {parentRigidbody.name}. Adding new mesh collider.");
|
||||
MeshCollider mc = target.AddComponent<MeshCollider>();
|
||||
mc.convex = true;
|
||||
mc.isTrigger = false;
|
||||
}
|
||||
|
||||
// Add water object
|
||||
if (target.GetComponent<WaterObject>() == null)
|
||||
{
|
||||
WaterObject wo = target.AddComponent<WaterObject>();
|
||||
wo.convexifyMesh = true;
|
||||
wo.simplifyMesh = true;
|
||||
wo.targetTriangleCount = 64;
|
||||
wo.GenerateSimMesh();
|
||||
|
||||
MassFromVolume massFromVolume = target.AddComponent<MassFromVolume>();
|
||||
massFromVolume.SetDefaultAsMaterial();
|
||||
massFromVolume.CalculateAndApplyFromMaterial();
|
||||
}
|
||||
|
||||
// Add Water Particle System and Particle System
|
||||
if (wow.addWaterParticleSystem)
|
||||
{
|
||||
GameObject waterParticleSystemPrefab =
|
||||
Resources.Load<GameObject>("Dynamic Water Physics 2/DefaultWaterParticleSystem");
|
||||
if (waterParticleSystemPrefab == null)
|
||||
{
|
||||
Debug.LogError("Could not load WaterParticleSystemPrefab from Resources.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Instantiate(waterParticleSystemPrefab, target.transform);
|
||||
}
|
||||
}
|
||||
|
||||
_wizardFinished = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 64ab77e709b22bb4586c4204dab18bf4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,28 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
#region
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2.WaterObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// Configuration for the WaterObject setup wizard.
|
||||
/// Used by the editor to automate WaterObject component setup.
|
||||
/// </summary>
|
||||
public class WaterObjectWizard : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// Should the wizard add a WaterParticleSystem component for splash effects.
|
||||
/// </summary>
|
||||
public bool addWaterParticleSystem;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user