332 lines
10 KiB
C#
332 lines
10 KiB
C#
using Oculus.Interaction;
|
|
using Oculus.Interaction.Surfaces;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
/// <summary>
|
|
/// Base for BaseModelBehaviour and ChildModelBehaviour
|
|
/// </summary>
|
|
public class ModelBehaviour : MonoBehaviour, IResettable
|
|
{
|
|
public ModelManager ModelManager;
|
|
public Exploder Exploder;
|
|
Model _model;
|
|
|
|
internal Model Model
|
|
{
|
|
get { return _model; }
|
|
set
|
|
{
|
|
UpdateModel(value);
|
|
_model = value;
|
|
LateUpdateModel();
|
|
}
|
|
}
|
|
readonly List<GameObject> _children = new List<GameObject>();
|
|
public readonly Dictionary<int, ChildModel> portDict = new Dictionary<int, ChildModel>(); //for CMS marker
|
|
|
|
public MeshFilter meshFilter;
|
|
public MeshRenderer meshRenderer;
|
|
public MeshCollider meshCollider;
|
|
public ColliderSurface colliderSurface;
|
|
public InteractableUnityEventWrapper interactable;
|
|
public RayInteractable rayInteractable;
|
|
|
|
bool lateInited = false;
|
|
|
|
public bool afterBirth = false;
|
|
|
|
public PortSelector PortSelector;
|
|
public int Index = -1;
|
|
|
|
Color _color = Color.black;
|
|
Color Color
|
|
{
|
|
get { return _color; }
|
|
set
|
|
{
|
|
var clonedMaterial = new Material(meshRenderer.material); //clone da sonst alle anderen mit dem mat auch colorchanged werden
|
|
clonedMaterial.color = value;
|
|
meshRenderer.material = clonedMaterial;
|
|
Debug.Log($"{name}: Set color from {_color} to {value}");
|
|
_color = value;
|
|
if (Model.Passthrough)
|
|
{
|
|
Debug.Log($"{name}: Passthrough {_children.Count}");
|
|
foreach (GameObject go in _children)
|
|
{
|
|
Debug.Log($"{name}: coloring {go.name}");
|
|
go.GetComponent<ChildModelBehaviour>().Color = value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Start()
|
|
{
|
|
Init();
|
|
}
|
|
|
|
void Init()
|
|
{
|
|
if (ModelManager == null)
|
|
ModelManager = FindFirstObjectByType<ModelManager>();
|
|
}
|
|
|
|
void LateUpdate()
|
|
{
|
|
if (!lateInited)
|
|
{
|
|
if (interactable.WhenSelect == null)
|
|
{
|
|
return;
|
|
}
|
|
interactable.WhenSelect.AddListener(OnSelect); //TODO: the root of all "frequently called", which is wrong
|
|
lateInited = true;
|
|
}
|
|
}
|
|
|
|
void UpdateModel(Model newModel)
|
|
{
|
|
Debug.Log($"UpdateModel: {name} updates from {(Model == null ? "null" : Model.NameId)} to {newModel.NameId}");
|
|
Init(); // I love race conditions
|
|
if (newModel is BaseModel)
|
|
{
|
|
name = "BaseModel:" + newModel.NameId;
|
|
}
|
|
else if (newModel is ChildModel cModel)
|
|
{
|
|
Debug.Log($"UpdateModel Name: {name} to {cModel.ParentPort}:{newModel.NameId}");
|
|
name = cModel.ParentPort + ":" + newModel.NameId;
|
|
}
|
|
//unapply previous model offset
|
|
if (Model != null)
|
|
{
|
|
Debug.Log($"UpdateModel: Model {name} reset old {Model} offsets.");
|
|
transform.localRotation *= Quaternion.Inverse(Model.Rotation);
|
|
transform.localPosition -= Model.Offset;
|
|
transform.localScale -= Model.Scale;
|
|
}
|
|
|
|
//kill old children
|
|
foreach (var compo in GetComponentsInChildren<ChildModelBehaviour>())
|
|
{
|
|
if (this is ChildModelBehaviour cmb && compo == cmb) //dont delete ourselves
|
|
{
|
|
continue;
|
|
}
|
|
Debug.Log($"UpdateModel: Destroying {name}'s Child {compo.gameObject.name}");
|
|
Destroy(compo.gameObject); // will be destroyed next frame
|
|
_children.Remove(compo.gameObject);
|
|
}
|
|
//change ourselves
|
|
meshFilter.mesh = newModel.Mesh;
|
|
meshFilter.sharedMesh = newModel.Mesh;
|
|
meshRenderer.material = newModel.Material;
|
|
meshRenderer.sharedMaterial = newModel.Material;
|
|
meshCollider.sharedMesh = newModel.Mesh;
|
|
transform.localRotation = newModel.Rotation;
|
|
transform.localScale = newModel.Scale;
|
|
transform.localPosition += newModel.Offset;
|
|
|
|
//spawn new childPorts
|
|
if (newModel.HasPorts())
|
|
{
|
|
Debug.Log($"Spawning {gameObject.name}'s ports");
|
|
SpawnChildPorts(newModel);
|
|
}
|
|
|
|
afterBirth = true;
|
|
}
|
|
|
|
void LateUpdateModel()
|
|
{
|
|
Debug.Log($"LateUpdateModel");
|
|
Exploder.HandleModelChange();
|
|
}
|
|
|
|
public void UpdateChild(int portNum, string id) //onclick change model
|
|
{
|
|
if (portNum >= _children.Count)
|
|
{
|
|
Debug.LogWarning($"Index {portNum} out of bounds {_children.Count}");
|
|
}
|
|
Model.Ports[portNum].Unapply(_children[portNum].transform);
|
|
//set new
|
|
var cmb = _children[portNum].GetComponent<ChildModelBehaviour>();
|
|
cmb.ChildModel = ModelManager.GetById(id);
|
|
cmb.Parent = this;
|
|
Model.Ports[portNum].Apply(_children[portNum].transform);
|
|
portDict[portNum] = cmb.ChildModel;
|
|
Exploder.HandleModelChange();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Spawns all Port-GO's, should be called only on Model Switch <b>after</b> cleanup
|
|
/// </summary>
|
|
/// <param name="newModel"><c>Model</c> the New Model to spawn ports from</param>
|
|
void SpawnChildPorts(Model newModel)
|
|
{
|
|
Debug.Log($"SpawnChildPorts: {name} : {newModel.NameId} spawns {newModel.Ports.Count} Ports");
|
|
if (ModelManager == null)
|
|
{
|
|
Debug.LogError("ModelManager not found");
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < newModel.Ports.Count; i++)
|
|
{
|
|
var port = newModel.Ports[i];
|
|
Debug.Log(i + ". Creating port " + port.PortID);
|
|
// name is set in UpdateModel of own model
|
|
GameObject child = Spawn.GO(ModelManager.childModelPrefab, transform, Vector3.zero, "unattended child");
|
|
child.SetActive(true);
|
|
ChildModelBehaviour cmb = child.GetComponent<ChildModelBehaviour>();
|
|
_children.Add(child);
|
|
cmb.Index = i;
|
|
|
|
var childModel = ModelManager.GetById(port.DefaultId);
|
|
if (childModel.Mesh == null)
|
|
{
|
|
Debug.LogError("Default Mesh Not Found, destroying child");
|
|
Destroy(child);
|
|
continue;
|
|
}
|
|
|
|
//Debug.Log($"POS {child.transform.gameObject.name} pos {child.transform.localPosition} {child.transform.rotation} {child.transform.localScale}");
|
|
cmb.Parent = this;
|
|
cmb.ChildModel = childModel;
|
|
if (newModel.Passthrough)
|
|
{
|
|
cmb.meshRenderer.material = newModel.Material;
|
|
cmb.meshRenderer.sharedMaterial = newModel.Material;
|
|
}
|
|
port.Apply(child.transform); // move to correct position
|
|
portDict[i] = cmb.ChildModel;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// -> see docs/exampleExport.json
|
|
/// this doesn't include leading+trailing { } or commas, just the content
|
|
/// this is recursive
|
|
/// </summary>
|
|
/// <param name="chooseableOnly">Whether all Ports or just the Chooseable ones should be exported</param>
|
|
/// <returns>an unfinished Json of this Model and all its children</returns>
|
|
string ExportJsonBase(bool chooseableOnly = true)
|
|
{
|
|
string export = "";
|
|
if (Model is BaseModel)
|
|
{
|
|
export += "\"baseModel\": true,";
|
|
}
|
|
export += $"\"modelId\": \"{Model.NameId}\"";
|
|
if (Model is ChildModel cm)
|
|
{
|
|
export += $", \"portId\": \"{cm.ParentPort}\"";
|
|
}
|
|
if (Model.HasColor())
|
|
{
|
|
export += $", \"color\": \"{Color}\"";
|
|
}
|
|
if (!Model.HasPorts())
|
|
{
|
|
return export;
|
|
}
|
|
export += ", \"ports\": [";
|
|
for (int i = 0; i < Model.Ports.Count; i++)
|
|
{
|
|
if (chooseableOnly && !Model.Ports[i].Chooseable)
|
|
{
|
|
continue;
|
|
}
|
|
export += "{ \"i\": " + i + ", " + _children[i].GetComponent<ChildModelBehaviour>().ExportJsonBase() + "}";
|
|
if (i != Model.Ports.Count - 1) //if not the last
|
|
{
|
|
export += ",";
|
|
}
|
|
}
|
|
export += "]";
|
|
return export;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Exports the current Model as a correct JSON
|
|
/// -> see docs/exampleExport.json
|
|
/// </summary>
|
|
/// <returns>An 'unformatted' JSON</returns>
|
|
public string ExportJson()
|
|
{
|
|
return "{" + ExportJsonBase() + "}";
|
|
}
|
|
|
|
public void UpdateChildColor(int portIndex, Color color)
|
|
{
|
|
if (portIndex < 0 || portIndex >= _children.Count)
|
|
{
|
|
Debug.LogWarning($"PortIndex {portIndex} invalid: there are {_children.Count} children.");
|
|
return;
|
|
}
|
|
_children[portIndex].GetComponent<ChildModelBehaviour>().Color = color;
|
|
}
|
|
|
|
public void ClearChildren()
|
|
{
|
|
Debug.Log($"Removing Children of {name}...");
|
|
foreach (var child in _children)
|
|
{
|
|
if (child != null)
|
|
{
|
|
Destroy(child);
|
|
}
|
|
}
|
|
_children.Clear();
|
|
Debug.Log("Removed.");
|
|
}
|
|
|
|
//model clicked
|
|
void OnSelect()
|
|
{
|
|
Debug.Log($"OnClick model {name} {Index}");
|
|
if (Index == -1)
|
|
{
|
|
Debug.LogWarning($"Clicked on Initialized/BaseModel, doing nothing");
|
|
return;
|
|
}
|
|
if (this is ChildModelBehaviour cmb)
|
|
{
|
|
if (cmb.Parent is BaseModelBehaviour)
|
|
{
|
|
PortSelector.mapPSB[Index].OnClick();
|
|
}
|
|
else //subchild
|
|
{
|
|
var parent = cmb;
|
|
while (parent)
|
|
{
|
|
if (parent.Parent is BaseModelBehaviour)
|
|
{
|
|
Debug.Log($"Parent {parent.name} is root, clicking on him");
|
|
PortSelector.mapPSB[parent.Index].OnClick();
|
|
return;
|
|
}
|
|
if (parent.Parent is ChildModelBehaviour cmbParent)
|
|
{
|
|
parent = cmbParent;
|
|
continue;
|
|
}
|
|
Debug.LogWarning($"Skipping {parent.name}, weird");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void ResetThis()
|
|
{
|
|
Debug.Log($"Resetting {name}...");
|
|
ClearChildren();
|
|
afterBirth = false;
|
|
}
|
|
}
|