forked from cgvr/DeltaVR
scale spawned object to match given bounding sphere diameter
This commit is contained in:
@@ -1,5 +1,4 @@
|
|||||||
### TODO
|
### TODO
|
||||||
* võtta spawnitud mudeli mõõtmed: meshcollideri max x, max y, etc? bounding box? ja kontrollida spawnimisel scale'i
|
|
||||||
* shape scanner:
|
* shape scanner:
|
||||||
* peenikesemad kiired
|
* peenikesemad kiired
|
||||||
* mitte lihtsalt ontriggerenter ja -exit, sest kui mitu objekti lähevad samal ajal sisse
|
* mitte lihtsalt ontriggerenter ja -exit, sest kui mitu objekti lähevad samal ajal sisse
|
||||||
@@ -19,5 +18,10 @@ Can't/Won't Do:
|
|||||||
|
|
||||||
|
|
||||||
### Notes
|
### Notes
|
||||||
|
* Functionalities that were implemented using major help from AI:
|
||||||
|
* animating NPC mouth movement based on voice amplitude
|
||||||
|
* adjusting spawned gltf objects' scale
|
||||||
|
* porting InvokeAI client and TRELLIS client to Unity scripts
|
||||||
|
* Getting Whisper stream to work with FMOD instead of Unity default audio
|
||||||
* TRELLIS: added functionality to specify texture baking optimisation total steps as an argument (`texture_opt_total_steps`), to replace the hardcoded 2500. But this is not tracked in Git (because modified this https://github.com/IgorAherne/trellis-stable-projectorz/releases/tag/latest)
|
* TRELLIS: added functionality to specify texture baking optimisation total steps as an argument (`texture_opt_total_steps`), to replace the hardcoded 2500. But this is not tracked in Git (because modified this https://github.com/IgorAherne/trellis-stable-projectorz/releases/tag/latest)
|
||||||
* Custom Shader Variant Collection to include glTF-pbrMetallicRoughness shader in build
|
* Custom Shader Variant Collection to include glTF-pbrMetallicRoughness shader in build
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ public class ModelGenerationUtils : MonoBehaviour
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new System.Exception("Failed to generate 3D model!");
|
throw new Exception("Failed to generate 3D model!");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -96,22 +96,22 @@ public class ModelGenerationUtils : MonoBehaviour
|
|||||||
return await SpawnModel(gltf, objectName);
|
return await SpawnModel(gltf, objectName);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new System.Exception("Failed to load GameObject from model" + modelPath);
|
throw new Exception("Failed to load GameObject from model" + modelPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<GameObject> SpawnModel(byte[] modelBinary)
|
public async Task<GameObject> SpawnModel(byte[] modelBinary, float boundingSphereDiameter = 0)
|
||||||
{
|
{
|
||||||
var gltf = new GltfImport();
|
var gltf = new GltfImport();
|
||||||
bool loadSuccess = await gltf.Load(modelBinary);
|
bool loadSuccess = await gltf.Load(modelBinary);
|
||||||
if (loadSuccess)
|
if (loadSuccess)
|
||||||
{
|
{
|
||||||
return await SpawnModel(gltf, "GeneratedModel");
|
return await SpawnModel(gltf, "GeneratedModel", boundingSphereDiameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new System.Exception("Failed to load GameObject from binary!");
|
throw new Exception("Failed to load GameObject from binary!");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<GameObject> SpawnModel(GltfImport gltfImport, string objectName)
|
public async Task<GameObject> SpawnModel(GltfImport gltfImport, string objectName, float boundingSphereDiameter = 0)
|
||||||
{
|
{
|
||||||
GameObject spawningParent = new GameObject("Parent-" + objectName);
|
GameObject spawningParent = new GameObject("Parent-" + objectName);
|
||||||
|
|
||||||
@@ -125,11 +125,54 @@ public class ModelGenerationUtils : MonoBehaviour
|
|||||||
MeshRenderer renderer = spawnedObjectBody.GetComponent<MeshRenderer>();
|
MeshRenderer renderer = spawnedObjectBody.GetComponent<MeshRenderer>();
|
||||||
renderer.material.SetFloat("metallicFactor", 0);
|
renderer.material.SetFloat("metallicFactor", 0);
|
||||||
|
|
||||||
|
AdjustScale(spawnedObjectBody, collider, boundingSphereDiameter);
|
||||||
|
|
||||||
spawnedObjectBody.name = objectName;
|
spawnedObjectBody.name = objectName;
|
||||||
return spawnedObjectBody;
|
return spawnedObjectBody;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new System.Exception("Failed to spawn GameObject " + objectName);
|
throw new Exception("Failed to spawn GameObject " + objectName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void AdjustScale(GameObject obj, MeshCollider collider, float boundingSphereDiameter)
|
||||||
|
{
|
||||||
|
|
||||||
|
// Wait one frame to ensure collider cooking & bounds are valid
|
||||||
|
await Task.Yield();
|
||||||
|
|
||||||
|
// --- Compute current bounding sphere diameter (from world AABB) ---
|
||||||
|
Bounds b = collider.bounds; // world-space AABB
|
||||||
|
float currentDiameter = 2f * b.extents.magnitude;
|
||||||
|
|
||||||
|
if (boundingSphereDiameter <= 0f)
|
||||||
|
{
|
||||||
|
UnityEngine.Debug.Log("Not adjusting scale");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentDiameter <= Mathf.Epsilon)
|
||||||
|
throw new Exception("Spawned mesh has zero-sized bounds; cannot scale to target diameter.");
|
||||||
|
|
||||||
|
// --- Compute and apply uniform scale ---
|
||||||
|
float scaleFactor = boundingSphereDiameter / currentDiameter;
|
||||||
|
obj.transform.localScale *= scaleFactor;
|
||||||
|
|
||||||
|
// Give Unity a moment to recompute bounds after scaling, then do a corrective pass
|
||||||
|
await Task.Yield();
|
||||||
|
|
||||||
|
// Corrective pass (optional): re-measure and nudge scale to reduce residual error
|
||||||
|
Bounds b2 = collider.bounds;
|
||||||
|
float newDiameter = 2f * b2.extents.magnitude;
|
||||||
|
|
||||||
|
// Only correct if off by more than a tiny epsilon (1%)
|
||||||
|
const float tolerance = 0.01f; // 1%
|
||||||
|
float err = Mathf.Abs(newDiameter - boundingSphereDiameter) / boundingSphereDiameter;
|
||||||
|
if (err > tolerance && newDiameter > Mathf.Epsilon)
|
||||||
|
{
|
||||||
|
float correction = boundingSphereDiameter / newDiameter;
|
||||||
|
obj.transform.localScale *= correction;
|
||||||
|
// No need to wait again unless you need to read back the final diameter immediately
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ public class CafeWaiterNPC : NPCController
|
|||||||
public Transform backRoom;
|
public Transform backRoom;
|
||||||
public Transform plate;
|
public Transform plate;
|
||||||
public float backRoomMovingTime = 5f;
|
public float backRoomMovingTime = 5f;
|
||||||
|
public float spawnedFoodDiameter = 0.5f;
|
||||||
|
|
||||||
public int ignorePlayerCollisionLayer = 2;
|
public int ignorePlayerCollisionLayer = 2;
|
||||||
|
|
||||||
@@ -135,7 +136,7 @@ public class CafeWaiterNPC : NPCController
|
|||||||
Sprite sprite = ModelGenerationUtils.CreateSprite(GeneratedTexture);
|
Sprite sprite = ModelGenerationUtils.CreateSprite(GeneratedTexture);
|
||||||
string encodedTexture = Convert.ToBase64String(GeneratedTexture.EncodeToJPG());
|
string encodedTexture = Convert.ToBase64String(GeneratedTexture.EncodeToJPG());
|
||||||
byte[] encodedModel = await TrellisClient.Instance.GenerateModel(encodedTexture);
|
byte[] encodedModel = await TrellisClient.Instance.GenerateModel(encodedTexture);
|
||||||
GameObject spawnedObject = await ModelGenerationUtils.Instance.SpawnModel(encodedModel);
|
GameObject spawnedObject = await ModelGenerationUtils.Instance.SpawnModel(encodedModel, spawnedFoodDiameter);
|
||||||
|
|
||||||
// Come back
|
// Come back
|
||||||
transform.DOMove(startingPosition, backRoomMovingTime).OnComplete(() =>
|
transform.DOMove(startingPosition, backRoomMovingTime).OnComplete(() =>
|
||||||
@@ -160,6 +161,7 @@ public class CafeWaiterNPC : NPCController
|
|||||||
spawnedObjectCollider.convex = true;
|
spawnedObjectCollider.convex = true;
|
||||||
spawnedObject.layer = ignorePlayerCollisionLayer;
|
spawnedObject.layer = ignorePlayerCollisionLayer;
|
||||||
|
|
||||||
spawnedObject.AddComponent<XRGrabInteractable>();
|
XRGrabInteractable grabComponent = spawnedObject.AddComponent<XRGrabInteractable>();
|
||||||
|
grabComponent.useDynamicAttach = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user