using System; using UnityEngine; using UnityEngine.UI; public class ArcheryRangeModelGenerationController : MonoBehaviour { public MicrophoneStand microphoneStand; public PushableButton imageGenerationButton; public PushableButton modelGenerationButton; public string imageGenerationPromptSuffix = ", single object, front and side fully visible, realistic style, plain neutral background, clear details, soft studio lighting, true-to-scale"; public Texture2D GeneratedTexture { get; private set; } public Image imageDisplay; public GameObject GeneratedModel { get; private set; } public float generatedObjectRotationSpeed = 10f; public Transform modelDisplay; public Material modelDisplayActiveMaterial; public Transform wire; public Material wireActiveMaterial; private bool modelGenerationInProgress; // Start is called before the first frame update void Start() { imageGenerationButton.OnButtonPressed += InvokeImageGeneration; modelGenerationButton.OnButtonPressed += InvokeModelGeneration; modelGenerationInProgress = false; } // Update is called once per frame void Update() { if (GeneratedModel != null) { GeneratedModel.transform.Rotate(Vector3.up, generatedObjectRotationSpeed * Time.deltaTime); } } private async void InvokeImageGeneration() { string inputPrompt = microphoneStand.GetTextOutput(); string refinedPrompt = inputPrompt + imageGenerationPromptSuffix; byte[] imageBytes = await InvokeAiClient.Instance.GenerateImage(refinedPrompt); GeneratedTexture = CreateTexture(imageBytes); Sprite sprite = CreateSprite(GeneratedTexture); imageDisplay.sprite = sprite; imageGenerationButton.Deactivate(); if (!modelGenerationInProgress) { modelGenerationButton.Deactivate(); } } private async void InvokeModelGeneration() { modelGenerationInProgress = true; string encodedTexture = Convert.ToBase64String(GeneratedTexture.EncodeToJPG()); byte[] encodedModel = await TrellisClient.Instance.GenerateModel(encodedTexture); GameObject spawnedObject = await PipelineManager.Instance.SpawnModel(encodedModel); // Destroy previous generated object Destroy(GeneratedModel); spawnedObject.transform.parent = modelDisplay; spawnedObject.transform.position = modelDisplay.position; GeneratedModel = spawnedObject; OnModelReady(); modelGenerationButton.Deactivate(); modelGenerationInProgress = false; } private Texture2D CreateTexture(byte[] imageBytes) { var tex = new Texture2D(2, 2, TextureFormat.RGBA32, false); // ImageConversion.LoadImage returns bool (true = success) if (!ImageConversion.LoadImage(tex, imageBytes, markNonReadable: false)) { Destroy(tex); throw new InvalidOperationException("Failed to decode image bytes into Texture2D."); } tex.filterMode = FilterMode.Bilinear; tex.wrapMode = TextureWrapMode.Clamp; return tex; } private Sprite CreateSprite(Texture2D tex) { var sprite = Sprite.Create( tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f), pixelsPerUnit: 100f ); return sprite; } private void OnModelReady() { foreach (MeshRenderer meshRenderer in wire.GetComponentsInChildren()) { meshRenderer.material = wireActiveMaterial; } modelDisplay.GetComponent().material = modelDisplayActiveMaterial; } }