#if UNITY_EDITOR
using FIMSpace.FEditor;
using UnityEditor;
#endif
using UnityEngine;
namespace FIMSpace.Generating
{
///
/// Implementation of spawning prefabs with selected OStamperSet with gizmos preview
///
[AddComponentMenu("FImpossible Creations/Level Design/Object Stamp Emitter", 1)]
public class ObjectStampEmitter : ObjectStampEmitterBase, IGenerating
{
public bool AlwaysDrawPreview = false;
public bool ReplaceAlreadySpawned = true;
[Range(0f, 1f)] public float SpawnPropability = 1f;
public OStamperSet PrefabsSet;
public OStampPhysicalPlacementSetup PhysicalPlacement;
public OStamperSet.StamperSetOverrider Overrider;
// Spawning related
public ObjectStamperEmittedInfo spawningInfo;
public GameObject SpawnedObject;
// Editor related
public GameObject _editorPreview;
public bool _displaySimplifiedSet = true;
private void Start()
{
if (PrefabsSet == null)
{
Debug.Log("[Objects Stamper] No 'Objects Stamper Set' assigned to '" + name + "'!");
return;
}
if (SpawnOnStart)
{
if (RandomizeOnStart)
{
spawningInfo = GetEmit(false, transform.parent); // Random emission from set
}
SpawnedObject = SpawnEmitPrefab();
IG_CallAfterGenerated();
}
else
if (_editorPreview)
SpawnedObject = _editorPreview;
}
public virtual ObjectStamperEmittedInfo GetEmit(bool noRepetition, Transform parent)
{
var emissionSet = PrefabsSet;
if (Overrider != null)
{
emissionSet = Overrider.GetOverridedSetup();
if (emissionSet == null) emissionSet = PrefabsSet;
}
if (emissionSet == null) return new ObjectStamperEmittedInfo();
return emissionSet.Emit(noRepetition, parent);
}
public void Generate()
{
if (RandomizeOnStart || spawningInfo.ChoosedPrefab == null) spawningInfo = GetEmit(false, transform.parent); // Random emission from set
SpawnEmitPrefab();
}
public void PreviewGenerate() { }
protected override OStamperSet GetStamperSet()
{
return PrefabsSet;
}
protected override ObjectStamperEmittedInfo GetSpawnInfo()
{
if (spawningInfo.ChoosedPrefab == null) spawningInfo = GetEmit(false, transform.parent);
return spawningInfo;
}
public void _EditorEmitPreview()
{
ClearPreviews();
_editorPreview = SpawnEmitPrefab(PrefabsSet);
IG_CallAfterGenerated();
}
public void _EditorEmitAndDetach()
{
ClearPreviews();
GameObject emitted = SpawnEmitPrefab(PrefabsSet);
if (emitted != null)
{
GameObject pre = SpawnedObject;
SpawnedObject = emitted;
IG_CallAfterGenerated();
SpawnedObject = pre;
emitted.transform.SetParent(transform.parent, true);
emitted.transform.SetAsLastSibling();
}
}
public void ClearPreviews()
{
if (_editorPreview) FGenerators.DestroyObject(_editorPreview);
if (SpawnedObject) FGenerators.DestroyObject(SpawnedObject);
}
protected override GameObject InternalInstatiatePrefab(bool raycasted, bool setParent = true)
{
if (SpawnPropability < 1f)
if (SpawnPropability < FGenerators.GetRandom(0f, 1f)) return null;
if (ReplaceAlreadySpawned)
{
if (_editorPreview != null) FGenerators.DestroyObject(_editorPreview);
if (SpawnedObject != null) FGenerators.DestroyObject(SpawnedObject);
}
else
if (_editorPreview != null) _editorPreview.transform.SetParent(null, true);
return base.InternalInstatiatePrefab(raycasted, setParent);
}
public void IG_CallAfterGenerated()
{
if (PhysicalPlacement.Enabled == false) return;
if (SpawnedObject) PhysicalPlacement.ProceedOn(SpawnedObject);
else if (_editorPreview) PhysicalPlacement.ProceedOn(_editorPreview);
}
#region Gizmos (Most of the code for the component is here)
#if UNITY_EDITOR
///
/// Drawing Green or Yellow mesh when object is not selected (only when AlwaysDrawPreview == true)
///
private void OnDrawGizmos()
{
if (Application.isPlaying) return;
if (PrefabsSet == null) return;
if (PrefabsSet.Prefabs == null) return;
if (PrefabsSet.Prefabs.Count == 0) return;
if (Selection.activeGameObject == gameObject) return;
if (AlwaysDrawPreview == false) return;
Color c = Gizmos.color;
DrawGizmosPreview(false);
if (RaycastSpawn)
{
OStamperSet.PlacementVolumeRaycastingData volume = PrefabsSet.GetRaycastingVolumeFor(GetSpawnInfo(), transform);
spawningResult = PrefabsSet.CheckRestrictionsOn(volume);
if (spawningResult.allow == false)
{
var overlapCheckResult = PrefabsSet.CheckOverlapOnFullLineCast(GetSpawnInfo(), volume);
if (overlapCheckResult.allow) spawningResult = overlapCheckResult;
}
if (spawningResult.allow || UseRestrictions == false)
{
DrawGizmosSpawningPreview(false);
}
}
Gizmos.color = c;
}
private void OnDrawGizmosSelected()
{
if (Application.isPlaying) return;
if (PrefabsSet == null) return;
if (PrefabsSet.Prefabs == null) return;
if (PrefabsSet.Prefabs.Count == 0) return;
Color bc = Gizmos.color;
Color bh = Handles.color;
// Draw navigation -----------------------------------------
Gizmos.color = new Color(1f, 0.4f, 0.4f, 0.75f);
Handles.color = new Color(1f, 0.4f, 0.4f, 0.5f);
// Draw Preview basing on choosed spawn info
if (spawningInfo.ChoosedPrefab == null) spawningInfo = GetEmit(false, transform.parent);
else if (spawningInfo.SetReference != PrefabsSet) spawningInfo = GetEmit(false, transform.parent);
Bounds refBounds = PrefabsSet.ReferenceBounds;
if (spawningInfo.ChoosedPrefab != null) refBounds = spawningInfo.PrefabReference.ReferenceBounds;
Gizmos.matrix = transform.localToWorldMatrix;
Handles.matrix = transform.localToWorldMatrix;
// Draw yellow preview mesh
DrawGizmosPreview(true);
Gizmos.matrix = Matrix4x4.identity;
Handles.matrix = Matrix4x4.identity;
#region Drawing raycasting arrow guide
if (RaycastSpawn)
{
if (PrefabsSet.RayDistanceMul > 0f)
{
Vector3 startCast = GetRayOrigin(false);
Vector3 castRay = GetCastVector(false);
//castRay = castRay.normalized * spawningInfo.SetReference.ReferenceBounds.size.magnitude * spawningInfo.SetReference.RayDistanceMul;
//Vector3 rotatedBoundAxis = GetSpawnInfo().PrefabReference.GetRotatedBoundsDimension(transform.rotation, castRay.normalized);
Vector3 castSide = OStamperSet.GetShiftedAxis(castRay).normalized;
Vector3 castEnd = startCast + castRay;
float refScale = GetStamperSet().ReferenceBounds.extents.magnitude * 0.15f;
// Drawing raycasting arrow guide
Gizmos.DrawLine(startCast, castEnd);
Gizmos.DrawRay(castEnd, -castRay.normalized * refScale + castSide * refScale);
Gizmos.DrawRay(castEnd, -castRay.normalized * refScale - castSide * refScale);
}
else
{
PrefabsSet.RayDistanceMul = 0f;
}
}
#endregion Drawing raycasting arrow guide
// Drawing green mesh preview on raycasted surface
if (RaycastSpawn)
if (Selection.activeGameObject == gameObject)
{
OStamperSet.PlacementVolumeRaycastingData volume = PrefabsSet.GetRaycastingVolumeFor(GetSpawnInfo(), transform);
spawningResult = PrefabsSet.CheckRestrictionsOn(volume);
if (spawningResult.allow == false)
{
var overlapCheckResult = PrefabsSet.CheckOverlapOnFullLineCast(GetSpawnInfo(), volume);
if (overlapCheckResult.allow) spawningResult = overlapCheckResult;
}
if (spawningResult.allow || UseRestrictions == false)
{
DrawGizmosSpawningPreview(true);
}
else
{
Handles.Label(spawningResult.originHit.point == Vector3.zero ? (transform.position + GetSpawnInfo().PrefabReference.ReferenceBoundsFull.extents) : spawningResult.originHit.point, new GUIContent(" Enter for info", FGUI_Resources.Tex_Error, spawningResult.info));
}
}
Handles.color = bh;
Gizmos.color = bc;
}
private void DrawGizmosPreview(bool drawBounds)
{
if (Application.isPlaying) return;
Matrix4x4 preMatrix = Gizmos.matrix;
Color cg = Gizmos.color;
Bounds refBounds = PrefabsSet.ReferenceBounds;
if (spawningInfo.ChoosedPrefab != null) refBounds = spawningInfo.PrefabReference.ReferenceBounds;
RaycastHit h = spawningResult.originHit;
bool draw = true;
if (h.transform != null) if (Vector3.Distance(spawningResult.targetPosition, transform.position) < GetCastVector(false).magnitude * 0.1f) draw = false;
if (draw)
if (spawningInfo.ChoosedPrefab != null)
{
Gizmos.matrix = spawningInfo.GetMatrixFor(transform);
OSPrefabReference prr = spawningInfo.PrefabReference;
// Draw preview Mesh
if (prr != null)
if (prr.GetMesh() != null)
{
Gizmos.color = new Color(.75f, .75f, .2f, h.transform == null ? 0.75f : 0.125f);
Vector3 scale = Vector3.one; if (prr.GameObject) scale = prr.GameObject.transform.localScale;
Gizmos.DrawMesh(prr.GetMesh(), Gizmos.matrix.inverse.MultiplyPoint(transform.position), Quaternion.identity, scale);
}
}
if (drawBounds)
{
Gizmos.color = new Color(1f, 1f, 1f, 0.4f);
Gizmos.DrawWireCube(refBounds.center, refBounds.size);
}
if (GetStamperSet() != null)
{
if (GetStamperSet().MinimumStandSpace > 0f)
{
Gizmos.color = new Color(0f, 0f, 0f, 0.4f);
Gizmos.DrawCube(refBounds.center, refBounds.size * GetStamperSet().MinimumStandSpace);
}
}
Gizmos.color = cg;
Gizmos.matrix = preMatrix;
}
private void DrawGizmosSpawningPreview(bool drawHitCross)
{
OStamperSet.PlacementVolumeRaycastingData volume = PrefabsSet.GetRaycastingVolumeFor(GetSpawnInfo(), transform);
spawningResult = PrefabsSet.CheckRestrictionsOn(volume);
if (spawningResult.allow == false)
{
var overlapCheckResult = PrefabsSet.CheckOverlapOnFullLineCast(GetSpawnInfo(), volume);
if (overlapCheckResult.allow) spawningResult = overlapCheckResult;
}
if (spawningResult.allow || UseRestrictions == false)
{
RaycastHit h = spawningResult.originHit;
if (volume.backupFullLineCast.transform) h = volume.backupFullLineCast;
Vector3 spawnPos = spawningResult.targetPosition;
if (h.transform)
{
if (drawHitCross)
{
// Yellow Normal Line
Gizmos.color = new Color(1f, 1f, 0.1f, 0.85f);
float refScale = PrefabsSet.ReferenceBounds.extents.magnitude;
Gizmos.DrawRay(h.point, h.normal * refScale * 0.3f);
// Yellow Normal Cross
Quaternion hitLook = Quaternion.LookRotation(h.normal, transform.up);
Vector3 off = hitLook * Vector3.up * refScale * 0.3f;
Gizmos.DrawRay(h.point - off, off * 2f);
off = hitLook * Vector3.right * refScale * 0.3f;
Gizmos.DrawRay(h.point - off, off * 2f);
}
// Preparing green mesh draw
Gizmos.matrix = spawningInfo.GetMatrixFor(transform, h.point, spawningInfo.GetRotationOn(transform, h.normal));
OSPrefabReference spawnPrefabRef = spawningInfo.PrefabReference;
if (spawnPrefabRef != null)
if (spawnPrefabRef.GetMesh() != null)
{
Gizmos.color = new Color(0.2f, .85f, 0.2f, 0.55f);
Vector3 drawPos = Gizmos.matrix.inverse.MultiplyPoint(spawningInfo.GetSpawnPosition(transform, h, spawnPos));
Vector3 scale = Vector3.one; if (spawnPrefabRef.GameObject) scale = spawnPrefabRef.GameObject.transform.localScale;
Gizmos.DrawMesh(spawnPrefabRef.GetMesh(), drawPos, Quaternion.identity, scale);
}
}
}
}
#endif
public override void SpawnIfNotEmittedYet()
{
if (SpawnedObject != null) return;
SpawnEmitPrefab(PrefabsSet);
}
#endregion Gizmos (Most of the code for the component is here)
}
#if UNITY_EDITOR
[UnityEditor.CanEditMultipleObjects]
[UnityEditor.CustomEditor(typeof(ObjectStampEmitter))]
public class ObjectsStampEmitterEditor : ObjectsStampEmitterBaseEditor
{
public ObjectStampEmitter Get { get { if (_get == null) _get = (ObjectStampEmitter)target; return _get; } }
private ObjectStampEmitter _get;
private SerializedProperty sp_pf;
private SerializedProperty sp_pfDrawPrev;
private SerializedProperty sp_PhysicalPlacement;
private SerializedProperty sp_PhysicalPlacementEnabled;
protected override void OnEnable()
{
base.OnEnable();
sp_pf = serializedObject.FindProperty("PrefabsSet");
sp_pfDrawPrev = serializedObject.FindProperty("AlwaysDrawPreview");
sp_PhysicalPlacement = serializedObject.FindProperty("PhysicalPlacement");
sp_PhysicalPlacementEnabled = sp_PhysicalPlacement.FindPropertyRelative("Enabled");
}
protected override void DrawProperties()
{
base.DrawProperties(); // Draw spawning params
GUILayout.Space(5);
#region Drawing Scriptable Stamper Set Inspector Preview
if (Get.PrefabsSet) EditorGUILayout.BeginVertical(FGUI_Resources.BGInBoxStyle);
//if (Get.PrefabsSet == null) EditorGUILayout.HelpBox("Stamper Set preset is needed for component to work!", MessageType.Info);
// Prefabs Set Field -------------------------------
EditorGUILayout.BeginHorizontal();
if (Get.PrefabsSet)
if (GUILayout.Button(new GUIContent(FGUI_Resources.Tex_Default), new GUILayoutOption[] { GUILayout.Width(24), GUILayout.Height(20) })) Get._displaySimplifiedSet = !Get._displaySimplifiedSet;
EditorGUIUtility.labelWidth = 90;
EditorGUILayout.PropertyField(sp_pf);
if (Get.PrefabsSet && Get.PrefabsSet.Prefabs != null) EditorGUILayout.LabelField("(" + Get.PrefabsSet.Prefabs.Count + ")", GUILayout.Width(24));
if (GUILayout.Button("Create New", GUILayout.Width(84))) Get.PrefabsSet = (OStamperSet)FGenerators.GenerateScriptable(CreateInstance(), "OS_");
EditorGUILayout.EndHorizontal();
EditorGUIUtility.labelWidth = 0;
GUILayout.Space(4f);
// Prefabs settings quick view ---------------------
if (Get.PrefabsSet)
{
EditorGUI.BeginChangeCheck();
SerializedObject pfs = new SerializedObject(Get.PrefabsSet);
SerializedProperty sp = pfs.GetIterator();
sp.Next(true);
if (Get._displaySimplifiedSet)
{
sp.NextVisible(false);
sp.NextVisible(false); EditorGUILayout.PropertyField(sp);
sp.NextVisible(false);
sp.NextVisible(false);// EditorGUILayout.PropertyField(sp);
// Draw Min Max Slider for rotation ranges
GUILayout.Space(6);
EditorGUILayout.BeginHorizontal();
float n = Get.PrefabsSet.RotationRanges.x;
float p = Get.PrefabsSet.RotationRanges.y;
EditorGUILayout.MinMaxSlider(new GUIContent(sp.displayName, sp.tooltip), ref n, ref p, -180f, 180f);
Get.PrefabsSet.RotationRanges = new Vector2(Mathf.Round(n), Mathf.Round(p));
EditorGUILayout.LabelField(Get.PrefabsSet.RotationRanges.x + "\x00B0 to " + Get.PrefabsSet.RotationRanges.y + "\x00B0", EditorStyles.centeredGreyMiniLabel, GUILayout.Width(80));
EditorGUILayout.EndHorizontal();
sp.NextVisible(false);
sp.NextVisible(false);/* EditorGUILayout.PropertyField(sp);*/
sp.NextVisible(false); EditorGUILayout.PropertyField(sp);
sp.NextVisible(false);
sp.NextVisible(false); EditorGUILayout.PropertyField(sp);
sp.NextVisible(false);
sp.NextVisible(false); EditorGUILayout.PropertyField(sp);
sp.NextVisible(false);
sp.NextVisible(false);
sp.NextVisible(false);
sp.NextVisible(false); EditorGUILayout.PropertyField(sp);
}
else
{
sp.NextVisible(false);
sp.NextVisible(false);
do { EditorGUILayout.PropertyField(sp); } while (sp.NextVisible(false));
}
pfs.ApplyModifiedProperties();
if (EditorGUI.EndChangeCheck()) Get.spawningInfo = Get.PrefabsSet.RefreshEmitInfo(Get.spawningInfo, Get.transform.parent);
EditorGUILayout.EndVertical();
}
GUILayout.Space(4f);
EditorGUIUtility.labelWidth = 145;
SerializedProperty sp_bottom = sp_pfDrawPrev.Copy();
EditorGUILayout.PropertyField(sp_bottom);
EditorGUIUtility.labelWidth = 165;
sp_bottom.NextVisible(false); EditorGUILayout.PropertyField(sp_bottom);
sp_bottom.NextVisible(false); EditorGUILayout.PropertyField(sp_bottom);
EditorGUIUtility.labelWidth = 0;
GUILayout.Space(4f);
if (Get.PrefabsSet == null)
{
EditorGUILayout.HelpBox(" First assign Objects Stamper Set!", MessageType.Info);
serializedObject.ApplyModifiedProperties();
return;
}
#endregion Drawing Scriptable Stamper Set Inspector Preview
GUILayout.Space(5);
Get.Overrider.Source = Get.PrefabsSet;
if (OStamperSet.Editor_DrawCompositionGUI(Get.Overrider, false))
{
EditorUtility.SetDirty(Get);
}
GUILayout.Space(5);
if (GUILayout.Button(new GUIContent(" Randomize Preview", FGUI_Resources.Tex_Refresh), GUILayout.Height(22)))
{
if (Get._editorPreview) FGenerators.DestroyObject(Get._editorPreview);
Get.spawningInfo = Get.GetEmit(false, Get.transform.parent);
repaint = true;
}
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button(new GUIContent(" Test Emit", FGUI_Resources.Tex_Movement), GUILayout.Height(22)))
{
Get._EditorEmitPreview();
repaint = true;
}
if (GUILayout.Button(new GUIContent(" Emit and Detach", FGUI_Resources.Tex_Movement), GUILayout.Height(22)))
{
Get._EditorEmitAndDetach();
Get.spawningInfo = Get.GetEmit(false, Get.transform.parent);
repaint = true;
}
EditorGUILayout.EndHorizontal();
if (Get._editorPreview || Get.SpawnedObject)
{
if (GUILayout.Button(new GUIContent(" Clear Spawned", FGUI_Resources.Tex_Remove), GUILayout.Height(22)))
{
Get.ClearPreviews();
EditorUtility.SetDirty(Get);
//if (Get._editorPreview) FGenerators.DestroyObject(Get._editorPreview);
//Get.spawningInfo = Get.PrefabsSet.Emit();
}
}
#region Hide
//if (GUILayout.Button(new GUIContent(" Check Placement", FGUI_Resources.TexMotionIcon), GUILayout.Height(22)))
//{
// OStamperSet.PlacementVolumeRaycastingData volume = Get.PrefabsSet.GetRaycastingVolumeFor(Get.GetSpawnInfo(), Get.transform);
// OStamperSet.RaycastingRestrictionsCheckResult result = Get.PrefabsSet.CheckRestrictionsOn(Get.GetSpawnInfo(), volume);
// if ( result.allow == false)
// {
// var overlapCheckResult = Get.PrefabsSet.CheckOverlapOnFullLineCast(Get.GetSpawnInfo(), volume);
// //Debug.Log("Result: " + overlapCheckResult.info);
// }
//}
#endregion Hide
}
protected override void _DrawLastProperties()
{
GUILayout.Space(8);
EditorGUI.BeginChangeCheck();
Get.PhysicalPlacement._Editor_DrawSetupToggle(sp_PhysicalPlacementEnabled);
if (Get.PhysicalPlacement._Editor_Foldout)
{
Get.PhysicalPlacement._Editor_DrawSetup(sp_PhysicalPlacement, false);
GUILayout.Space(4);
}
if (EditorGUI.EndChangeCheck()) EditorUtility.SetDirty(Get);
GUILayout.Space(6);
}
}
#endif
}