ModelAnimationCache.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. import Cartesian3 from "../Core/Cartesian3.js";
  2. import ComponentDatatype from "../Core/ComponentDatatype.js";
  3. import ConstantSpline from "../Core/ConstantSpline.js";
  4. import defaultValue from "../Core/defaultValue.js";
  5. import defined from "../Core/defined.js";
  6. import LinearSpline from "../Core/LinearSpline.js";
  7. import Matrix4 from "../Core/Matrix4.js";
  8. import MorphWeightSpline from "../Core/MorphWeightSpline.js";
  9. import Quaternion from "../Core/Quaternion.js";
  10. import QuaternionSpline from "../Core/QuaternionSpline.js";
  11. import Spline from "../Core/Spline.js";
  12. import WebGLConstants from "../Core/WebGLConstants.js";
  13. import getAccessorByteStride from "./GltfPipeline/getAccessorByteStride.js";
  14. import numberOfComponentsForType from "./GltfPipeline/numberOfComponentsForType.js";
  15. import AttributeType from "./AttributeType.js";
  16. /**
  17. * @private
  18. */
  19. function ModelAnimationCache() {}
  20. const dataUriRegex = /^data\:/i;
  21. function getAccessorKey(model, accessor) {
  22. const gltf = model.gltf;
  23. const buffers = gltf.buffers;
  24. const bufferViews = gltf.bufferViews;
  25. const bufferView = bufferViews[accessor.bufferView];
  26. const buffer = buffers[bufferView.buffer];
  27. const byteOffset = bufferView.byteOffset + accessor.byteOffset;
  28. const byteLength = accessor.count * numberOfComponentsForType(accessor.type);
  29. const uriKey = dataUriRegex.test(buffer.uri) ? "" : buffer.uri;
  30. return `${model.cacheKey}//${uriKey}/${byteOffset}/${byteLength}`;
  31. }
  32. const cachedAnimationParameters = {};
  33. ModelAnimationCache.getAnimationParameterValues = function (model, accessor) {
  34. const key = getAccessorKey(model, accessor);
  35. let values = cachedAnimationParameters[key];
  36. if (!defined(values)) {
  37. // Cache miss
  38. const gltf = model.gltf;
  39. const buffers = gltf.buffers;
  40. const bufferViews = gltf.bufferViews;
  41. const bufferView = bufferViews[accessor.bufferView];
  42. const bufferId = bufferView.buffer;
  43. const buffer = buffers[bufferId];
  44. const source = buffer.extras._pipeline.source;
  45. const componentType = accessor.componentType;
  46. const type = accessor.type;
  47. const numberOfComponents = numberOfComponentsForType(type);
  48. const count = accessor.count;
  49. const byteStride = getAccessorByteStride(gltf, accessor);
  50. values = new Array(count);
  51. const accessorByteOffset = defaultValue(accessor.byteOffset, 0);
  52. let byteOffset = bufferView.byteOffset + accessorByteOffset;
  53. for (let i = 0; i < count; i++) {
  54. const typedArrayView = ComponentDatatype.createArrayBufferView(
  55. componentType,
  56. source.buffer,
  57. source.byteOffset + byteOffset,
  58. numberOfComponents
  59. );
  60. if (type === "SCALAR") {
  61. values[i] = typedArrayView[0];
  62. } else if (type === "VEC3") {
  63. values[i] = Cartesian3.fromArray(typedArrayView);
  64. } else if (type === "VEC4") {
  65. values[i] = Quaternion.unpack(typedArrayView);
  66. }
  67. byteOffset += byteStride;
  68. }
  69. // GLTF_SPEC: Support more parameter types when glTF supports targeting materials. https://github.com/KhronosGroup/glTF/issues/142
  70. if (defined(model.cacheKey)) {
  71. // Only cache when we can create a unique id
  72. cachedAnimationParameters[key] = values;
  73. }
  74. }
  75. return values;
  76. };
  77. const cachedAnimationSplines = {};
  78. function getAnimationSplineKey(model, animationName, samplerName) {
  79. return `${model.cacheKey}//${animationName}/${samplerName}`;
  80. }
  81. function SteppedSpline(backingSpline) {
  82. this._spline = backingSpline;
  83. this._lastTimeIndex = 0;
  84. }
  85. SteppedSpline.prototype.findTimeInterval = Spline.prototype.findTimeInterval;
  86. SteppedSpline.prototype.evaluate = function (time, result) {
  87. const i = (this._lastTimeIndex = this.findTimeInterval(
  88. time,
  89. this._lastTimeIndex
  90. ));
  91. const times = this._spline.times;
  92. const steppedTime = time >= times[i + 1] ? times[i + 1] : times[i];
  93. return this._spline.evaluate(steppedTime, result);
  94. };
  95. Object.defineProperties(SteppedSpline.prototype, {
  96. times: {
  97. get: function () {
  98. return this._spline.times;
  99. },
  100. },
  101. });
  102. SteppedSpline.prototype.wrapTime = function (time) {
  103. return this._spline.wrapTime(time);
  104. };
  105. SteppedSpline.prototype.clampTime = function (time) {
  106. return this._spline.clampTime(time);
  107. };
  108. ModelAnimationCache.getAnimationSpline = function (
  109. model,
  110. animationName,
  111. animation,
  112. samplerName,
  113. sampler,
  114. input,
  115. path,
  116. output
  117. ) {
  118. const key = getAnimationSplineKey(model, animationName, samplerName);
  119. let spline = cachedAnimationSplines[key];
  120. if (!defined(spline)) {
  121. const times = input;
  122. const controlPoints = output;
  123. if (times.length === 1 && controlPoints.length === 1) {
  124. spline = new ConstantSpline(controlPoints[0]);
  125. } else if (
  126. sampler.interpolation === "LINEAR" ||
  127. sampler.interpolation === "STEP"
  128. ) {
  129. if (path === "translation" || path === "scale") {
  130. spline = new LinearSpline({
  131. times: times,
  132. points: controlPoints,
  133. });
  134. } else if (path === "rotation") {
  135. spline = new QuaternionSpline({
  136. times: times,
  137. points: controlPoints,
  138. });
  139. } else if (path === "weights") {
  140. spline = new MorphWeightSpline({
  141. times: times,
  142. weights: controlPoints,
  143. });
  144. }
  145. if (defined(spline) && sampler.interpolation === "STEP") {
  146. spline = new SteppedSpline(spline);
  147. }
  148. }
  149. if (defined(model.cacheKey)) {
  150. // Only cache when we can create a unique id
  151. cachedAnimationSplines[key] = spline;
  152. }
  153. }
  154. return spline;
  155. };
  156. const cachedSkinInverseBindMatrices = {};
  157. ModelAnimationCache.getSkinInverseBindMatrices = function (model, accessor) {
  158. const key = getAccessorKey(model, accessor);
  159. let matrices = cachedSkinInverseBindMatrices[key];
  160. if (!defined(matrices)) {
  161. // Cache miss
  162. const gltf = model.gltf;
  163. const buffers = gltf.buffers;
  164. const bufferViews = gltf.bufferViews;
  165. const bufferViewId = accessor.bufferView;
  166. const bufferView = bufferViews[bufferViewId];
  167. const bufferId = bufferView.buffer;
  168. const buffer = buffers[bufferId];
  169. const source = buffer.extras._pipeline.source;
  170. const componentType = accessor.componentType;
  171. const type = accessor.type;
  172. const count = accessor.count;
  173. const byteStride = getAccessorByteStride(gltf, accessor);
  174. let byteOffset = bufferView.byteOffset + accessor.byteOffset;
  175. const numberOfComponents = numberOfComponentsForType(type);
  176. matrices = new Array(count);
  177. if (componentType === WebGLConstants.FLOAT && type === AttributeType.MAT4) {
  178. for (let i = 0; i < count; ++i) {
  179. const typedArrayView = ComponentDatatype.createArrayBufferView(
  180. componentType,
  181. source.buffer,
  182. source.byteOffset + byteOffset,
  183. numberOfComponents
  184. );
  185. matrices[i] = Matrix4.fromArray(typedArrayView);
  186. byteOffset += byteStride;
  187. }
  188. }
  189. cachedSkinInverseBindMatrices[key] = matrices;
  190. }
  191. return matrices;
  192. };
  193. export default ModelAnimationCache;