@@ -1353,24 +1353,192 @@ private static GameObject GetGameObject (Object obj)
1353
1353
}
1354
1354
1355
1355
/// <summary>
1356
- /// Get a mesh renderer's mesh.
1356
+ /// If your MonoBehaviour knows about some custom geometry that
1357
+ /// isn't in a MeshFilter or SkinnedMeshRenderer, use
1358
+ /// RegisterMeshCallback to get a callback when the exporter tries
1359
+ /// to export your component.
1360
+ ///
1361
+ /// The callback should return true, and output the mesh you want.
1362
+ ///
1363
+ /// Return false if you don't want to drive this game object.
1364
+ ///
1365
+ /// Return true and output a null mesh if you don't want the
1366
+ /// exporter to output anything.
1357
1367
/// </summary>
1358
- private MeshInfo GetMeshInfo ( GameObject gameObject , bool requireRenderer = true )
1368
+ public delegate bool GetMeshForComponent < T > ( T component , out Mesh mesh ) where T : MonoBehaviour ;
1369
+ public delegate bool GetMeshForComponent ( MonoBehaviour component , out Mesh mesh ) ;
1370
+
1371
+ /// <summary>
1372
+ /// Map from type (must be a MonoBehaviour) to callback.
1373
+ /// The type safety is lost; the caller must ensure it at run-time.
1374
+ /// </summary>
1375
+ static Dictionary < System . Type , GetMeshForComponent > MeshForComponentCallbacks
1376
+ = new Dictionary < System . Type , GetMeshForComponent > ( ) ;
1377
+
1378
+ /// <summary>
1379
+ /// Register a callback to invoke if the object has a component of type T.
1380
+ ///
1381
+ /// This function is prefered over the other mesh callback
1382
+ /// registration methods because it's type-safe, efficient, and
1383
+ /// invocation order between types can be controlled in the UI by
1384
+ /// reordering the components.
1385
+ ///
1386
+ /// It's an error to register a callback for a component that
1387
+ /// already has one, unless 'replace' is set to true.
1388
+ /// </summary>
1389
+ public static void RegisterMeshCallback < T > ( GetMeshForComponent < T > callback , bool replace = false )
1390
+ where T : UnityEngine . MonoBehaviour
1359
1391
{
1360
- // Two possibilities: it's a skinned mesh, or we have a mesh filter.
1361
- Mesh mesh ;
1362
- var meshFilter = gameObject . GetComponent < MeshFilter > ( ) ;
1363
- if ( meshFilter ) {
1364
- mesh = meshFilter . sharedMesh ;
1365
- } else {
1366
- var renderer = gameObject . GetComponent < SkinnedMeshRenderer > ( ) ;
1367
- if ( ! renderer ) {
1368
- mesh = null ;
1392
+ // Under the hood we lose type safety, but don't let the user notice!
1393
+ RegisterMeshCallback ( typeof ( T ) ,
1394
+ ( MonoBehaviour component , out Mesh mesh ) => callback ( ( T ) component , out mesh ) ,
1395
+ replace ) ;
1396
+ }
1397
+
1398
+ /// <summary>
1399
+ /// Register a callback to invoke if the object has a component of type T.
1400
+ ///
1401
+ /// The callback will be invoked with an argument of type T, it's
1402
+ /// safe to downcast.
1403
+ ///
1404
+ /// Normally you'll want to use the generic form, but this one is
1405
+ /// easier to use with reflection.
1406
+ /// </summary>
1407
+ public static void RegisterMeshCallback ( System . Type t ,
1408
+ GetMeshForComponent callback ,
1409
+ bool replace = false )
1410
+ {
1411
+ if ( ! t . IsSubclassOf ( typeof ( MonoBehaviour ) ) ) {
1412
+ throw new System . Exception ( "Registering a callback for a type that isn't derived from MonoBehaviour: " + t ) ;
1413
+ }
1414
+ if ( ! replace && MeshForComponentCallbacks . ContainsKey ( t ) ) {
1415
+ throw new System . Exception ( "Replacing a callback for type " + t ) ;
1416
+ }
1417
+ MeshForComponentCallbacks [ t ] = callback ;
1418
+ }
1419
+
1420
+ /// <summary>
1421
+ /// Delegate used to convert a GameObject into a mesh.
1422
+ ///
1423
+ /// This is useful if you want to have broader control over
1424
+ /// the export process than the GetMeshForComponent callbacks
1425
+ /// provide. But it's less efficient because you'll get a callback
1426
+ /// on every single GameObject.
1427
+ /// </summary>
1428
+ public delegate bool GetMeshForObject ( GameObject gameObject , out Mesh mesh ) ;
1429
+
1430
+ static List < GetMeshForObject > MeshForObjectCallbacks = new List < GetMeshForObject > ( ) ;
1431
+
1432
+ /// <summary>
1433
+ /// Register a callback to invoke on every GameObject we export.
1434
+ ///
1435
+ /// Avoid doing this if you can use a callback that depends on type.
1436
+ ///
1437
+ /// The GameObject-based callbacks are checked before the
1438
+ /// component-based ones.
1439
+ ///
1440
+ /// Multiple GameObject-based callbacks can be registered; they are
1441
+ /// checked in order of registration.
1442
+ /// </summary>
1443
+ public static void RegisterMeshObjectCallback ( GetMeshForObject callback )
1444
+ {
1445
+ MeshForObjectCallbacks . Add ( callback ) ;
1446
+ }
1447
+
1448
+ /// <summary>
1449
+ /// Forget the callback linked to a component of type T.
1450
+ /// </summary>
1451
+ public static void UnRegisterMeshCallback < T > ( )
1452
+ {
1453
+ MeshForComponentCallbacks . Remove ( typeof ( T ) ) ;
1454
+ }
1455
+
1456
+ /// <summary>
1457
+ /// Forget the callback linked to a component of type T.
1458
+ /// </summary>
1459
+ public static void UnRegisterMeshCallback ( System . Type t )
1460
+ {
1461
+ MeshForComponentCallbacks . Remove ( t ) ;
1462
+ }
1463
+
1464
+ /// <summary>
1465
+ /// Forget a GameObject-based callback.
1466
+ /// </summary>
1467
+ public static void UnRegisterMeshCallback ( GetMeshForObject callback )
1468
+ {
1469
+ MeshForObjectCallbacks . Remove ( callback ) ;
1470
+ }
1471
+
1472
+ /// <summary>
1473
+ /// Get the mesh we want to use for a GameObject.
1474
+ /// May be null.
1475
+ /// </summary>
1476
+ Mesh ChooseMeshForObject ( GameObject gameObject )
1477
+ {
1478
+ // First allow the object-based callbacks to have a hack at it.
1479
+ foreach ( var callback in MeshForObjectCallbacks ) {
1480
+ Mesh mesh ;
1481
+ if ( callback ( gameObject , out mesh ) ) {
1482
+ return mesh ;
1483
+ }
1484
+ }
1485
+
1486
+ // Next iterate over components and allow the component-based
1487
+ // callbacks to have a hack at it. This is complicated by the
1488
+ // potential of subclassing.
1489
+ Mesh defaultMesh = null ;
1490
+ foreach ( var component in gameObject . GetComponents < Component > ( ) ) {
1491
+ if ( ! component ) {
1492
+ continue ;
1493
+ }
1494
+ var monoBehaviour = component as MonoBehaviour ;
1495
+ if ( ! monoBehaviour ) {
1496
+ // Check for default handling. But don't commit yet.
1497
+ if ( defaultMesh ) {
1498
+ continue ;
1499
+ }
1500
+ var meshFilter = component as MeshFilter ;
1501
+ if ( meshFilter ) {
1502
+ defaultMesh = ( component as MeshFilter ) . sharedMesh ;
1503
+ continue ;
1504
+ }
1505
+ var smr = component as SkinnedMeshRenderer ;
1506
+ if ( smr ) {
1507
+ defaultMesh = new Mesh ( ) ;
1508
+ smr . BakeMesh ( defaultMesh ) ;
1509
+ continue ;
1510
+ }
1369
1511
} else {
1370
- mesh = new Mesh ( ) ;
1371
- renderer . BakeMesh ( mesh ) ;
1512
+ // Check if we have custom behaviour for this component type, or
1513
+ // one of its base classes.
1514
+ if ( ! monoBehaviour . enabled ) {
1515
+ continue ;
1516
+ }
1517
+ var componentType = monoBehaviour . GetType ( ) ;
1518
+ do {
1519
+ GetMeshForComponent callback ;
1520
+ if ( MeshForComponentCallbacks . TryGetValue ( componentType , out callback ) ) {
1521
+ Mesh mesh ;
1522
+ if ( callback ( monoBehaviour , out mesh ) ) {
1523
+ return mesh ;
1524
+ }
1525
+ }
1526
+ componentType = componentType . BaseType ;
1527
+ } while ( componentType . IsSubclassOf ( typeof ( MonoBehaviour ) ) ) ;
1372
1528
}
1373
1529
}
1530
+
1531
+ // If we're here, custom handling didn't work.
1532
+ // Revert to default handling.
1533
+ return defaultMesh ;
1534
+ }
1535
+
1536
+ /// <summary>
1537
+ /// Get the mesh for an object in an easy-to-use format.
1538
+ /// </summary>
1539
+ private MeshInfo GetMeshInfo ( GameObject gameObject )
1540
+ {
1541
+ var mesh = ChooseMeshForObject ( gameObject ) ;
1374
1542
if ( ! mesh ) {
1375
1543
return new MeshInfo ( null ) ;
1376
1544
}
0 commit comments