273 lines
11 KiB
C#
273 lines
11 KiB
C#
#if UNITY_5_3_OR_NEWER
|
|
using System.Collections;
|
|
using UnityEngine;
|
|
using UnityEngine.Networking;
|
|
#if GLTF
|
|
using GLTFast;
|
|
#endif
|
|
|
|
namespace RoboidControl.Unity {
|
|
|
|
/// <summary>
|
|
/// The Unity representation fo a Roboid Control Thing
|
|
/// </summary>
|
|
public class Thing : MonoBehaviour {
|
|
|
|
/// <summary>
|
|
/// The core C# thing
|
|
/// </summary>
|
|
public RoboidControl.Thing core { get; set; }
|
|
|
|
/// <summary>
|
|
/// The owner of this thing
|
|
/// </summary>
|
|
public Participant owner;
|
|
|
|
/// <summary>
|
|
/// Create a Unity representation of a Thing
|
|
/// </summary>
|
|
/// <param name="core">The core of the thing</param>
|
|
/// <returns>The created thing</returns>
|
|
public static Thing Create(RoboidControl.Thing core) {
|
|
// Debug.Log("Creating new Unity thing");
|
|
GameObject gameObj = string.IsNullOrEmpty(core.name) ?
|
|
new("Thing") :
|
|
new(core.name);
|
|
Thing component = gameObj.AddComponent<Thing>();
|
|
component.Init(core);
|
|
return component;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initialize the Thing
|
|
/// </summary>
|
|
/// <param name="core">The core of the thing</param>
|
|
/// This affects the parent and pose of the thing
|
|
protected void Init(RoboidControl.Thing core) {
|
|
this.core = core;
|
|
this.core.component = this;
|
|
this.owner = FindAnyObjectByType<SiteServer>();
|
|
core.owner = this.owner.coreParticipant;
|
|
|
|
if (core.parent != null && core.parent.component != null) {
|
|
this.transform.SetParent(core.parent.component.transform, false);
|
|
this.transform.localPosition = Vector3.zero;
|
|
}
|
|
|
|
if (core.position != null)
|
|
this.transform.localPosition = core.position.ToVector3();
|
|
if (core.orientation != null)
|
|
this.transform.localRotation = core.orientation.ToQuaternion();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update the Unity rendering
|
|
/// </summary>
|
|
protected virtual void Update() {
|
|
if (core == null)
|
|
return;
|
|
|
|
if (core.linearVelocity != null && core.linearVelocity.distance != 0) {
|
|
Vector3 direction = Quaternion.AngleAxis(core.linearVelocity.direction.horizontal, Vector3.up) * Vector3.forward;
|
|
this.transform.Translate(core.linearVelocity.distance * Time.deltaTime * direction, Space.Self);
|
|
}
|
|
|
|
if (core.angularVelocity != null && core.angularVelocity.distance != 0) {
|
|
Vector3 axis = core.angularVelocity.direction.ToVector3();
|
|
this.transform.localRotation *= Quaternion.AngleAxis(core.angularVelocity.distance * Time.deltaTime, axis);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update the Unity state (just calls UpdateThing)
|
|
/// </summary>
|
|
protected virtual void FixedUpdate() {
|
|
UpdateThing();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update the Unity state
|
|
/// </summary>
|
|
public void UpdateThing() {
|
|
if (core == null) {
|
|
Debug.Log($"{this} core thing is gone, self destruct in 0 seconds...");
|
|
Destroy(this);
|
|
return;
|
|
}
|
|
|
|
while (core.updateQueue.TryDequeue(out RoboidControl.Thing.CoreEvent e))
|
|
HandleCoreEvent(e);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handle events from the core thing
|
|
/// </summary>
|
|
/// <param name="coreEvent">The core event to handle</param>
|
|
private void HandleCoreEvent(RoboidControl.Thing.CoreEvent coreEvent) {
|
|
switch (coreEvent.messageId) {
|
|
case ThingMsg.id:
|
|
Debug.Log($"{this.core.id} Handle Thing");
|
|
if (core.parent == null)
|
|
this.transform.SetParent(null, true);
|
|
else if (core.parent.component != null)
|
|
this.transform.SetParent(core.parent.component.transform, true);
|
|
break;
|
|
case NameMsg.Id:
|
|
Debug.Log($"{this.core.id} Handle Name");
|
|
this.gameObject.name = core.name;
|
|
break;
|
|
case ModelUrlMsg.Id:
|
|
Debug.Log("{this.id} Handle Model URL");
|
|
string extension = core.modelUrl[core.modelUrl.LastIndexOf(".")..];
|
|
if (extension == ".jpg" || extension == ".png")
|
|
StartCoroutine(LoadJPG());
|
|
else if (extension == ".gltf" || extension == ".glb")
|
|
ProcessGltfModel(core);
|
|
break;
|
|
case PoseMsg.Id:
|
|
Debug.Log($"{this.core.id} Handle Pose");
|
|
this.HandlePose();
|
|
break;
|
|
case BinaryMsg.Id:
|
|
Debug.Log($"{this.core.id} Handle Binary");
|
|
this.HandleBinary();
|
|
break;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Load and attach a JPG sprite visualization of the thing
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private IEnumerator LoadJPG() {
|
|
UnityWebRequest request = UnityWebRequestTexture.GetTexture(core.modelUrl);
|
|
yield return request.SendWebRequest();
|
|
|
|
if (request.result == UnityWebRequest.Result.Success) {
|
|
Texture2D texture = ((DownloadHandlerTexture)request.downloadHandler).texture;
|
|
float aspectRatio = (float)texture.width / (float)texture.height;
|
|
|
|
GameObject modelQuad = GameObject.CreatePrimitive(PrimitiveType.Quad);
|
|
Collider c = modelQuad.GetComponent<Collider>();
|
|
c.enabled = false;
|
|
Destroy(c);
|
|
modelQuad.transform.SetParent(this.transform, false);
|
|
modelQuad.transform.localEulerAngles = new(90, -90, 0);
|
|
modelQuad.transform.localScale = new Vector3(aspectRatio, 1, 1) / 5;
|
|
if (this.name == "Ant")
|
|
modelQuad.transform.localScale *= 2;
|
|
Material quadMaterial = new(Shader.Find("Unlit/Transparent")) {
|
|
mainTexture = texture
|
|
};
|
|
modelQuad.GetComponent<Renderer>().material = quadMaterial;
|
|
}
|
|
else {
|
|
Debug.LogError("Failed to load image: " + request.error);
|
|
}
|
|
}
|
|
|
|
bool loadingModel = false;
|
|
private async void ProcessGltfModel(RoboidControl.Thing coreThing) {
|
|
#if GLTF
|
|
if (!loadingModel) {
|
|
loadingModel = true;
|
|
|
|
Debug.Log("Loading GLTF model from :" + coreThing.modelUrl);
|
|
GltfImport gltfImport = new GltfImport();
|
|
bool success = await gltfImport.Load(coreThing.modelUrl);
|
|
if (success) {
|
|
Transform parentTransform = this.transform;
|
|
|
|
Thing[] things = FindObjectsOfType<Thing>();
|
|
Thing parentThing = null;
|
|
foreach (Thing thing in things) {
|
|
if (thing.core.id == coreThing.id) {
|
|
parentTransform = thing.transform;
|
|
parentThing = thing;
|
|
}
|
|
}
|
|
await gltfImport.InstantiateMainSceneAsync(parentTransform);
|
|
SkinnedMeshRenderer[] meshRenderers = parentTransform.GetComponentsInChildren<SkinnedMeshRenderer>();
|
|
|
|
#if pHUMANOID4
|
|
if (parentThing.objectType == 7) {
|
|
HumanoidControl hc = parentThing.gameObject.GetComponent<HumanoidControl>();
|
|
if (hc == null)
|
|
hc = parentThing.gameObject.AddComponent<HumanoidControl>();
|
|
|
|
foreach (SkinnedMeshRenderer meshRenderer in meshRenderers) {
|
|
if (meshRenderer.rootBone != null) {
|
|
Debug.Log("Found a skinned mesh with bones");
|
|
hc.RetrieveBonesFrom(meshRenderer.rootBone);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
parentTransform.localScale = Vector3.one;
|
|
if (meshRenderers.Length > 0) {
|
|
foreach (SkinnedMeshRenderer meshRenderer in meshRenderers) {
|
|
if (meshRenderer.rootBone != null) {
|
|
Debug.Log("Found a skinned mesh with bones");
|
|
ScanForThings(meshRenderer.rootBone);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
ScanForThings(parentTransform);
|
|
}
|
|
}
|
|
else {
|
|
this.transform.localScale = Vector3.one * 1;
|
|
}
|
|
}
|
|
loadingModel = true;
|
|
#endif
|
|
}
|
|
|
|
private void ScanForThings(Transform rootTransform) {
|
|
// Thing[] thingArray = allThings.ToArray();
|
|
|
|
// for (int thingIx = 0; thingIx < thingArray.Length; thingIx++) {
|
|
// Thing thing = thingArray[thingIx];
|
|
// GameObject foundObj = FindThingByName(thing, rootTransform);
|
|
// if (foundObj != null && foundObj != thing.gameObject) {
|
|
// Thing foundThing = foundObj.GetComponent<Thing>();
|
|
// if (foundThing == null) {
|
|
// allThings.Remove(thing);
|
|
|
|
// foundThing = foundObj.AddComponent<Thing>();
|
|
// foundThing.networkId = thing.networkId;
|
|
// foundThing.objectId = thing.objectId;
|
|
// allThings.Add(foundThing);
|
|
// Destroy(thing.gameObject);
|
|
// }
|
|
// }
|
|
// }
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Handle a Pose event
|
|
/// </summary>
|
|
/// This can update the position and/or orientation when the velocity of the thing is zero.
|
|
/// If a velocity is not zero, the position and/or orientation update will be ignored
|
|
protected virtual void HandlePose() {
|
|
if (core.linearVelocity.distance == 0)
|
|
this.transform.localPosition = core.position.ToVector3();
|
|
if (core.angularVelocity.distance == 0)
|
|
this.transform.localRotation = core.orientation.ToQuaternion();
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handle a Binary event
|
|
/// </summary>
|
|
protected virtual void HandleBinary() { }
|
|
|
|
}
|
|
|
|
}
|
|
#endif
|