Blendshape.hx 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. package h3d.prim;
  2. @:access(h3d.prim.HMDModel)
  3. class Blendshape {
  4. var hmdModel : HMDModel;
  5. var shapes : Array<hxd.fmt.hmd.Data.BlendShape>;
  6. var weights : Array<Float>;
  7. var inputMapping : Array<Map<String, Int>> = [];
  8. var shapesBytes = [];
  9. public function new(hmdModel) {
  10. this.hmdModel = hmdModel;
  11. if ( hmdModel.data.vertexFormat.hasLowPrecision )
  12. throw "Blend shape doesn't support low precision";
  13. // Cache data for blendshapes
  14. var is32 = hmdModel.data.vertexCount > 0x10000;
  15. var vertexFormat = hmdModel.data.vertexFormat;
  16. var size = hmdModel.data.vertexCount * vertexFormat.strideBytes;
  17. var geoId = 0;
  18. for (gIdx => g in hmdModel.lib.header.geometries)
  19. if (g == hmdModel.data)
  20. geoId = gIdx;
  21. shapes = [ for(s in hmdModel.lib.header.shapes) if (s.geom == geoId) s];
  22. weights = [];
  23. for ( s in 0...shapes.length ) {
  24. var s = shapes[s];
  25. var size = s.vertexCount * s.vertexFormat.strideBytes;
  26. var vertexBytes = haxe.io.Bytes.alloc(size);
  27. hmdModel.lib.resource.entry.readBytes(vertexBytes, 0, hmdModel.dataPosition + s.vertexPosition, size);
  28. size = hmdModel.data.vertexCount << 2;
  29. var remapBytes = haxe.io.Bytes.alloc(size);
  30. hmdModel.lib.resource.entry.readBytes(remapBytes, 0, hmdModel.dataPosition + s.remapPosition, size);
  31. shapesBytes.push({ vertexBytes : vertexBytes, remapBytes : remapBytes});
  32. inputMapping.push(new Map());
  33. weights.push(0.0);
  34. }
  35. // We want to remap inputs since inputs can be not exactly in the same
  36. for ( input in vertexFormat.getInputs() ) {
  37. for ( s in 0...shapes.length ) {
  38. var offset = 0;
  39. for ( i in shapes[s].vertexFormat.getInputs() ) {
  40. if ( i.name == input.name )
  41. inputMapping[s].set(i.name, offset);
  42. offset += i.type.getSize();
  43. }
  44. }
  45. }
  46. }
  47. public function setBlendshapeAmount(blendshapeIdx: Int, amount: Float) {
  48. if (blendshapeIdx >= this.weights.length)
  49. throw 'Blendshape at index ${blendshapeIdx} doesn\'t exist (there is only ${this.weights.length} blendshapes).';
  50. this.weights[blendshapeIdx] = amount;
  51. uploadBlendshapeBytes();
  52. }
  53. function getBlendshapeCount() {
  54. if (hmdModel.lib.header.shapes == null)
  55. return 0;
  56. return shapes.length;
  57. }
  58. function uploadBlendshapeBytes() {
  59. if (hmdModel.buffer == null || hmdModel.buffer.isDisposed())
  60. hmdModel.alloc(Engine.getCurrent());
  61. var is32 = hmdModel.data.vertexCount > 0x10000;
  62. var vertexFormat = hmdModel.data.vertexFormat;
  63. var size = hmdModel.data.vertexCount * vertexFormat.strideBytes;
  64. var originalBytes = haxe.io.Bytes.alloc(size);
  65. hmdModel.lib.resource.entry.readBytes(originalBytes, 0, hmdModel.dataPosition + hmdModel.data.vertexPosition, size);
  66. var flagOffset = 31;
  67. var bytesOffset = haxe.io.Bytes.alloc(originalBytes.length);
  68. bytesOffset.fill(0, originalBytes.length, 0);
  69. // Apply blendshapes offsets to original vertex
  70. for (sIdx in 0...shapes.length) {
  71. var sp = shapesBytes[sIdx];
  72. var offsetIdx = 0;
  73. var idx = 0;
  74. while (offsetIdx < shapes[sIdx].indexCount) {
  75. var affectedVId = sp.remapBytes.getInt32(idx << 2);
  76. var reachEnd = false;
  77. while (!reachEnd) {
  78. reachEnd = affectedVId >> flagOffset != 0;
  79. if (reachEnd)
  80. affectedVId = affectedVId ^ (1 << flagOffset);
  81. var inputIdx = 0;
  82. var offsetInput = 0;
  83. for (input in shapes[sIdx].vertexFormat.getInputs()) {
  84. for (sizeIdx in 0...input.type.getSize()) {
  85. // if (input.name == "normal")
  86. // continue;
  87. var original = originalBytes.getFloat(affectedVId * vertexFormat.stride + inputMapping[sIdx][input.name] + sizeIdx << 2);
  88. var offset = sp.vertexBytes.getFloat(offsetIdx * shapes[sIdx].vertexFormat.stride + offsetInput + sizeIdx << 2);
  89. var res = hxd.Math.lerp(original, original + offset, weights[sIdx]) - original;
  90. var bytePos = affectedVId * vertexFormat.stride + inputMapping[sIdx][input.name] + sizeIdx << 2;
  91. bytesOffset.setFloat(bytePos, bytesOffset.getFloat(bytePos) + res);
  92. }
  93. offsetInput += input.type.getSize();
  94. inputIdx++;
  95. }
  96. idx++;
  97. if (idx < hmdModel.data.vertexCount)
  98. affectedVId = sp.remapBytes.getInt32(idx << 2);
  99. }
  100. offsetIdx++;
  101. }
  102. }
  103. var bytes = haxe.io.Bytes.alloc(originalBytes.length);
  104. bytes.blit(0, originalBytes, 0, originalBytes.length);
  105. for (i in 0...(Std.int(bytesOffset.length / 4.0)))
  106. bytes.setFloat(i << 2, bytes.getFloat(i << 2) + bytesOffset.getFloat(i << 2));
  107. // Send bytes to buffer for rendering
  108. hmdModel.buffer.uploadBytes(bytes, 0, hmdModel.data.vertexCount);
  109. hmdModel.indexCount = 0;
  110. hmdModel.indexesTriPos = [];
  111. for( n in hmdModel.data.indexCounts ) {
  112. hmdModel.indexesTriPos.push(Std.int(hmdModel.indexCount/3));
  113. hmdModel.indexCount += n;
  114. }
  115. var size = (is32 ? 4 : 2) * hmdModel.indexCount;
  116. var bytes = hmdModel.lib.resource.entry.fetchBytes(hmdModel.dataPosition + hmdModel.data.indexPosition, size);
  117. hmdModel.indexes.uploadBytes(bytes, 0, hmdModel.indexCount);
  118. }
  119. }