1
1
import { getAsArray } from "./utils" ;
2
2
3
+ /**
4
+ * @typedef {Object } TextureCollectionItem
5
+ * @property {string } id
6
+ * @property {string } name
7
+ * @property {string } directory
8
+ * @property {string } [fullDirectory]
9
+ * @property {string } [thumbnail]
10
+ */
11
+
12
+ /**
13
+ * @typedef {Object } TextureCollection
14
+ * @property {string } trait
15
+ * @property {string } type
16
+ * @property {TextureCollectionItem[] } collection
17
+ *
18
+ */
19
+
3
20
export class CharacterManifestData {
4
21
constructor ( manifest ) {
5
22
const {
@@ -23,6 +40,7 @@ export class CharacterManifestData{
23
40
vrmMeta,
24
41
traits,
25
42
textureCollections,
43
+ decalCollections,
26
44
colorCollections,
27
45
canDownload = true ,
28
46
downloadOptions = { }
@@ -105,6 +123,11 @@ export class CharacterManifestData{
105
123
this . textureTraitsMap = null ;
106
124
this . createTextureTraits ( textureCollections ) ;
107
125
126
+ this . decalTraits = [ ] ;
127
+ this . decalTraitsMap = null ;
128
+
129
+ this . createDecalTraits ( decalCollections ) ;
130
+
108
131
this . colorTraits = [ ] ;
109
132
this . colorTraitsMap = null ;
110
133
this . createColorTraits ( colorCollections ) ;
@@ -324,6 +347,14 @@ export class CharacterManifestData{
324
347
return this . textureTraitsMap . get ( groupTraitID ) ;
325
348
}
326
349
350
+ // decals
351
+ getDecalTrait ( groupTraitID , traitID ) {
352
+ return this . getDecalGroup ( groupTraitID ) ?. getTrait ( traitID ) ;
353
+ }
354
+ getDecalGroup ( decalGroupTraitId ) {
355
+ return this . decalTraitsMap . get ( decalGroupTraitId ) ;
356
+ }
357
+
327
358
// colors
328
359
getColorTrait ( groupTraitID , traitID ) {
329
360
return this . getColorGroup ( groupTraitID ) ?. getTrait ( traitID ) ;
@@ -360,17 +391,35 @@ export class CharacterManifestData{
360
391
return result ;
361
392
}
362
393
394
+ getDecalsDirectory ( ) {
395
+ let result = ( this . assetsLocation || "" ) + ( this . decalDirectory || "" ) ;
396
+ if ( ! result . endsWith ( "/" ) && ! result . endsWith ( "\\" ) )
397
+ result += "/" ;
398
+ return result ;
399
+ }
363
400
364
401
365
402
366
403
// Given an array of traits, saves an array of TraitModels
367
404
createModelTraits ( modelTraits , replaceExisting = false ) {
368
405
if ( replaceExisting ) this . modelTraits = [ ] ;
369
406
407
+ let hasTraitWithDecals = false
370
408
getAsArray ( modelTraits ) . forEach ( traitObject => {
371
- this . modelTraits . push ( new TraitModelsGroup ( this , traitObject ) )
409
+ const group = new TraitModelsGroup ( this , traitObject )
410
+ this . modelTraits . push ( group )
411
+
412
+ /**
413
+ * We only support one group with decals at the moment; if there are multiple groups with decals, we will log a warning
414
+ */
415
+ if ( hasTraitWithDecals && group . getAllDecals ( ) ?. length ) {
416
+ console . warn ( "Detected multiple traits with decals; only one trait with decals is supported at the moment" )
417
+ } else if ( ! group . getAllDecals ( ) ?. length ) {
418
+ hasTraitWithDecals = true
419
+ }
372
420
} ) ;
373
421
422
+
374
423
this . modelTraitsMap = new Map ( this . modelTraits . map ( item => [ item . trait , item ] ) ) ;
375
424
376
425
// Updates all restricted traits for each group models
@@ -394,6 +443,19 @@ export class CharacterManifestData{
394
443
395
444
this . textureTraitsMap = new Map ( this . textureTraits . map ( item => [ item . trait , item ] ) ) ;
396
445
}
446
+ /**
447
+ * @param {TextureCollection[] } decalTraitGroups
448
+ * @param {boolean } [replaceExisting]
449
+ */
450
+ createDecalTraits ( decalTraitGroups , replaceExisting = false ) {
451
+ if ( replaceExisting ) this . decalTraits = [ ] ;
452
+
453
+ getAsArray ( decalTraitGroups ) . forEach ( traitObject => {
454
+ this . decalTraits . push ( new DecalTextureGroup ( this , traitObject ) )
455
+ } ) ;
456
+
457
+ this . decalTraitsMap = new Map ( this . decalTraits . map ( item => [ item . trait , item ] ) ) ;
458
+ }
397
459
398
460
createColorTraits ( colorTraits , replaceExisting = false ) {
399
461
if ( replaceExisting ) this . colorTraits = [ ] ;
@@ -409,7 +471,16 @@ export class CharacterManifestData{
409
471
410
472
411
473
// Must be created AFTER color collections and texture collections have been created
412
- class TraitModelsGroup {
474
+ export class TraitModelsGroup {
475
+ /**
476
+ * @type {ModelTrait[] }
477
+ */
478
+ collection
479
+ /**
480
+ * @type {CharacterManifestData }
481
+ */
482
+ manifestData
483
+
413
484
constructor ( manifestData , options ) {
414
485
const {
415
486
trait,
@@ -487,6 +558,15 @@ class TraitModelsGroup{
487
558
return this . collectionMap . get ( traitID ) ;
488
559
}
489
560
561
+ /**
562
+ *
563
+ * @returns {DecalTrait[] }
564
+ */
565
+ getAllDecals ( ) {
566
+ const decalGroup = this . collection . map ( trait => trait . targetDecalCollection ) . flat ( ) ;
567
+ return decalGroup . map ( ( c ) => c ?. collection ) . flat ( ) . filter ( ( c ) => ! ! c ) ;
568
+ }
569
+
490
570
getTraitByIndex ( index ) {
491
571
return this . collection [ index ] ;
492
572
}
@@ -509,13 +589,23 @@ class TraitModelsGroup{
509
589
510
590
}
511
591
class TraitTexturesGroup {
592
+ /**
593
+ *
594
+ * @param {CharacterManifestData } manifestData
595
+ * @param {TextureCollection } options
596
+ */
512
597
constructor ( manifestData , options ) {
513
598
const {
514
599
trait,
515
600
collection
516
601
} = options ;
602
+ if ( ! trait ) {
603
+ console . warn ( "TraitTexturesGroup is missing property trait" )
604
+ this . trait = "undefined" + Math . floor ( Math . random ( ) * 10 )
605
+ } else {
606
+ this . trait = trait ;
607
+ }
517
608
this . manifestData = manifestData ;
518
- this . trait = trait ;
519
609
520
610
this . collection = [ ] ;
521
611
this . collectionMap = null ;
@@ -569,6 +659,86 @@ class TraitTexturesGroup{
569
659
null ;
570
660
}
571
661
}
662
+ export class DecalTextureGroup {
663
+ /**
664
+ * @type {string }
665
+ */
666
+ trait
667
+ /**
668
+ * @type {DecalTrait[] }
669
+ */
670
+ collection
671
+ /**
672
+ * @type {Map<string,DecalTrait> }
673
+ */
674
+ collectionMap
675
+ /**
676
+ *
677
+ * @param {CharacterManifestData } manifestData
678
+ * @param {TextureCollection } options
679
+ */
680
+ constructor ( manifestData , options ) {
681
+ const {
682
+ trait,
683
+ collection
684
+ } = options ;
685
+ this . manifestData = manifestData ;
686
+ if ( ! trait ) {
687
+ console . warn ( "DecalTextureGroup is missing property trait" )
688
+ this . trait = "undefined" + Math . floor ( Math . random ( ) * 10 )
689
+ } else {
690
+ this . trait = trait ;
691
+ }
692
+ this . collection = [ ] ;
693
+ this . collectionMap = null ;
694
+ this . createCollection ( collection ) ;
695
+ }
696
+
697
+ appendCollection ( decalTraitGroup , replaceExisting = false ) {
698
+ decalTraitGroup . collection . forEach ( newTextureTrait => {
699
+ const textureTrait = this . getTrait ( newTextureTrait . id )
700
+ if ( textureTrait != null ) {
701
+ // replace only if requested ro replace
702
+ if ( replaceExisting ) {
703
+ console . log ( `Texture with id ${ newTextureTrait . id } exists and will be replaced with new one` )
704
+ this . collectionMap . set ( newTextureTrait . id , newTextureTrait )
705
+ const ind = this . collection . indexOf ( textureTrait )
706
+ this . collection [ ind ] = newTextureTrait ;
707
+ }
708
+ else {
709
+ console . log ( `Texture with id ${ newTextureTrait . id } exists, skipping` )
710
+ }
711
+ }
712
+ else {
713
+ // create
714
+ this . collection . push ( newTextureTrait )
715
+ this . collectionMap . set ( newTextureTrait . id , newTextureTrait ) ;
716
+ }
717
+ } ) ;
718
+ }
719
+
720
+ createCollection ( itemCollection , replaceExisting = false ) {
721
+ if ( replaceExisting ) this . collection = [ ] ;
722
+ getAsArray ( itemCollection ) . forEach ( item => {
723
+ this . collection . push ( new DecalTrait ( this , item ) )
724
+ } ) ;
725
+ this . collectionMap = new Map ( this . collection . map ( item => [ item . id , item ] ) ) ;
726
+ }
727
+
728
+ getTrait ( traitID ) {
729
+ return this . collectionMap . get ( traitID ) ;
730
+ }
731
+
732
+ getTraitByIndex ( index ) {
733
+ return this . collection [ index ] ;
734
+ }
735
+
736
+ getRandomTrait ( ) {
737
+ return this . collection . length > 0 ?
738
+ this . collection [ Math . floor ( Math . random ( ) * this . collection . length ) ] :
739
+ null ;
740
+ }
741
+ }
572
742
class TraitColorsGroup {
573
743
constructor ( manifestData , options ) {
574
744
const {
@@ -628,8 +798,20 @@ class TraitColorsGroup{
628
798
null ;
629
799
}
630
800
}
631
- class ModelTrait {
632
- blendshapeTraits = [ ] ;
801
+ export class ModelTrait {
802
+ blendshapeTraits = [ ] ;
803
+ /**
804
+ * @type {string[] }
805
+ * */
806
+ decalMeshNameTargets = [ ]
807
+ /**
808
+ * @type {DecalTextureGroup | null }
809
+ */
810
+ targetDecalCollection = null
811
+ /**
812
+ * @type {TraitModelsGroup }
813
+ */
814
+ traitGroup
633
815
blendshapeTraitsMap = new Map ( ) ;
634
816
constructor ( traitGroup , options ) {
635
817
const {
@@ -643,11 +825,14 @@ class ModelTrait{
643
825
textureCollection,
644
826
blendshapeTraits,
645
827
colorCollection,
828
+ decalCollection,
829
+ decalMeshNameTargets,
646
830
fullDirectory,
647
831
fullThumbnail,
648
832
} = options ;
649
833
this . manifestData = traitGroup . manifestData ;
650
834
this . traitGroup = traitGroup ;
835
+ this . decalMeshNameTargets = getAsArray ( decalMeshNameTargets ) ;
651
836
652
837
this . id = id ;
653
838
this . directory = directory ;
@@ -683,6 +868,7 @@ class ModelTrait{
683
868
684
869
this . targetTextureCollection = textureCollection ? traitGroup . manifestData . getTextureGroup ( textureCollection ) : null ;
685
870
this . targetColorCollection = colorCollection ? traitGroup . manifestData . getColorGroup ( colorCollection ) : null ;
871
+ this . targetDecalCollection = decalCollection ? traitGroup . manifestData . getDecalGroup ( decalCollection ) : null ;
686
872
687
873
if ( blendshapeTraits && Array . isArray ( blendshapeTraits ) ) {
688
874
@@ -838,6 +1024,10 @@ export class BlendShapeTrait{
838
1024
}
839
1025
840
1026
class TextureTrait {
1027
+ /**
1028
+ * @param {TraitTexturesGroup } traitGroup
1029
+ * @param {TextureCollectionItem } options
1030
+ */
841
1031
constructor ( traitGroup , options ) {
842
1032
const {
843
1033
id,
@@ -872,6 +1062,67 @@ class TextureTrait{
872
1062
this . fullThumbnail = traitGroup . manifestData . getThumbnailsDirectory ( ) + thumbnail ;
873
1063
}
874
1064
}
1065
+ export class DecalTrait extends TextureTrait {
1066
+ /**
1067
+ * @type {string }
1068
+ */
1069
+ id
1070
+ /**
1071
+ * @type {string }
1072
+ */
1073
+ directory
1074
+ /**
1075
+ * @type {string | undefined }
1076
+ * */
1077
+ fullDirectory
1078
+ name
1079
+ thumbnail
1080
+ /**
1081
+ * @type {string|undefined }
1082
+ */
1083
+ fullThumbnail
1084
+ /**
1085
+ * @type {TraitTexturesGroup }
1086
+ */
1087
+ traitGroup
1088
+ /**
1089
+ * @param {TraitTexturesGroup } traitGroup
1090
+ * @param {TextureCollectionItem } options
1091
+ */
1092
+ constructor ( traitGroup , options ) {
1093
+ super ( traitGroup , options ) ;
1094
+ const {
1095
+ id,
1096
+ directory,
1097
+ fullDirectory,
1098
+ name,
1099
+ thumbnail,
1100
+ } = options ;
1101
+ this . traitGroup = traitGroup ;
1102
+ this . id = id ;
1103
+ this . directory = directory ;
1104
+ if ( fullDirectory ) {
1105
+ this . fullDirectory = fullDirectory
1106
+ }
1107
+ else {
1108
+ if ( Array . isArray ( directory ) )
1109
+ {
1110
+ this . fullDirectory = [ ] ;
1111
+ for ( let i = 0 ; i < directory . length ; i ++ ) {
1112
+ this . fullDirectory [ i ] = traitGroup . manifestData . getTraitsDirectory ( ) + directory [ i ]
1113
+ }
1114
+ }
1115
+ else
1116
+ {
1117
+ this . fullDirectory = traitGroup . manifestData . getTraitsDirectory ( ) + thumbnail ;
1118
+ }
1119
+ }
1120
+
1121
+ this . name = name ;
1122
+ this . thumbnail = thumbnail ;
1123
+ this . fullThumbnail = traitGroup . manifestData . getThumbnailsDirectory ( ) + thumbnail ;
1124
+ }
1125
+ }
875
1126
class ColorTrait {
876
1127
constructor ( traitGroup , options ) {
877
1128
const {
0 commit comments