Cesium 源码解析 Model(六)

该章节详细介绍了如何使用Draco压缩技术来减小3D模型的体积,通过gltf的KHR_draco_mesh_compression扩展在WebGL环境中进行解码。DracoLoader.js负责检测和处理gltf中的Draco压缩数据,利用WebAssembly和Web Worker进行后台解码,然后将解码后的数据转换回gltf规范的顶点属性。整个过程包括数据收集、解码任务调度和解码结果的格式化,确保模型数据在WebGL中正确渲染。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        上一章是关于压缩数据的一种方式,本章讲解第二种使用draco压缩过的几何数据,这部分分两个阶段,第一个阶段是使用wasm+worker的过程,这个过程解析完成后还会在vs中在进行数据处理才能变成普通的顶点属性数据。

        对于draco压缩数据使用的是gltf的extensionsRequired.KHR_draco_mesh_compression扩展,在model.js解析gltf的json中检测到这个标志就会使用wasm+worker处理。

        在model.js中关于draco压缩的数据引用DracoLoader.js类来处理。在model.js中使用了

  1. DracoLoader.hasExtension

  2. DracoLoader.parse

  3. DracoLoader.decodeModel

  4. DracoLoader.cacheDataForModel

  5. DracoLoader.destroyCachedDataForModel

1、对于DracoLoader.hasExtension,主要是用来检测gltf中是否使用了KHR_draco_mesh_compression扩展。

// 使用了Draco压缩扩展
DracoLoader.hasExtension = function (model) {
  return (
    defined(model.extensionsRequired.KHR_draco_mesh_compression) ||
    defined(model.extensionsUsed.KHR_draco_mesh_compression)
  );
};

2、DracoLoader.parse用来收集gltf中使用draco压缩过的数据到primitivesToDecode,将这些数据保存在一个队列中,等收集完成后,将所有的数据一个一个的进行解压缩,收集数据的代码如下:

// 解析数据
DracoLoader.parse = function (model, context) {
  // 是否存在扩展
  if (!DracoLoader.hasExtension(model)) {
    return;
  }

  // 加载资源
  var loadResources = model._loadResources;
  // 缓存key
  var cacheKey = model.cacheKey;
  // 缓存
  if (defined(cacheKey)) {
    // 解码缓存中没有数据
    if (!defined(DracoLoader._decodedModelResourceCache)) {
      // 上下文的缓存中也没有这个数据
      if (!defined(context.cache.modelDecodingCache)) {
        // 上下文中创建解码缓存
        context.cache.modelDecodingCache = {};
      }

      // 指向上下文解码缓存
      DracoLoader._decodedModelResourceCache = context.cache.modelDecodingCache;
    }

    // Decoded data for model will be loaded from cache
    // 缓存中加载
    var cachedData = DracoLoader._decodedModelResourceCache[cacheKey];
    if (defined(cachedData)) {
      // 引用计数增加
      cachedData.count++;
      // 正在处理解码缓存
      loadResources.pendingDecodingCache = true;
      return;
    }
  }

  // 着色器中解析
  var dequantizeInShader = model._dequantizeInShader;
  var gltf = model.gltf;

  // 遍历mesh
  ForEach.mesh(gltf, function (mesh, meshId) {
    // 遍历primitive
    ForEach.meshPrimitive(mesh, function (primitive, primitiveId) {

      // 没有扩展
      if (!defined(primitive.extensions)) {
        return;
      }

      // 不是压缩过的数据就不用处理了
      var compressionData = primitive.extensions.KHR_draco_mesh_compression;
      if (!defined(compressionData)) {
        return;
      }

      // 获取bufferView数据
      var bufferView = gltf.bufferViews[compressionData.bufferView];
      // 获取数据块
      var typedArray = arraySlice(
        gltf.buffers[bufferView.buffer].extras._pipeline.source,
        bufferView.byteOffset,
        bufferView.byteOffset + bufferView.byteLength
      );

      // 存储待解压的draco数据
      loadResources.primitivesToDecode.enqueue({
        mesh: meshId,                // 数据属于哪一个mesh
        primitive: primitiveId,      // 数据是哪一个primitive的
        array: typedArray,           // 数据
        bufferView: bufferView,      // 数据的归属     
        compressedAttributes: compressionData.attributes,       // 压缩过的属性,这些属性对应的数据
        dequantizeInShader: dequantizeInShader,                 // 是否在着色器中解码
      });

    });
  });
};

3、DracoLoader.decodeModel解码draco数据

// 解码模型
DracoLoader.decodeModel = function (model, context) {
  // 数据是否压缩过
  if (!DracoLoader.hasExtension(model)) {
    return when.resolve();
  }

  // 加载资源
  var loadResources = model._loadResources;
  // 缓存key
  var cacheKey = model.cacheKey;
  if (defined(cacheKey) && defined(DracoLoader._decodedModelResourceCache)) {
    // 缓存过的数据,不用解码
    var cachedData = DracoLoader._decodedModelResourceCache[cacheKey];
    // Load decoded data for model when cache is ready
    if (defined(cachedData) && loadResources.pendingDecodingCache) {
      return when(cachedData.ready, function () {
        model._decodedData = cachedData.data;
        loadResources.pendingDecodingCache = false;
      });
    }

    // Decoded data for model should be cached when ready
    // 还没有准备好
    DracoLoader._decodedModelResourceCache[cacheKey] = {
      ready: false,
      count: 1,
      data: undefined,
    };
  }

  // 压缩过的数据不存在就返回
  if (loadResources.primitivesToDecode.length === 0) {
    // No more tasks to schedule
    return when.resolve();
  }

  // 任务处理器
  var decoderTaskProcessor = DracoLoader._getDecoderTaskProcessor();
  var decodingPromises = [];

  // 调度
  var promise = scheduleDecodingTask(
    decoderTaskProcessor,       // 调度任务
    model,                      // 模型类
    loadResources,              // 正在加载的资源
    context                     // webgl上下文
  );

  // 为什么第一个在循环之外??
  while (defined(promise)) {
    decodingPromises.push(promise);
    promise = scheduleDecodingTask(
      decoderTaskProcessor,   // 解码
      model,
      loadResources,
      context
    );
  }

  // 所有任务都处理完成
  return when.all(decodingPromises);
};

可以看到数据一个个从primitivesToDecode中取出来,调用scheduleDecodingTask进行处理,返回的是一个promise,最后将所有的promise通过when.all()都处理完成就返回。

        对于scheduleDecodingTask个过程,

// draco解码
function DracoLoader() {}

// Maximum concurrency to use when decoding draco models
// 解码draco压缩过的模型的并发数量
DracoLoader._maxDecodingConcurrency = Math.max(
  FeatureDetection.hardwareConcurrency - 1,
  1
);

// Exposed for testing purposes

// 任务处理器(封装线程)
DracoLoader._decoderTaskProcessor = undefined;
DracoLoader._taskProcessorReady = false;

// 创建任务处理器
DracoLoader._getDecoderTaskProcessor = function () {
  // 没有创建处理程序
  if (!defined(DracoLoader._decoderTaskProcessor)) {
    // 创建任务处理程序
    var processor = new TaskProcessor(
      "decodeDraco",                        // 业务处理的js脚本文件
      DracoLoader._maxDecodingConcurrency   // 最大并发数量
    );

    processor
      .initWebAssemblyModule({    // 初始化wasm
        modulePath: "ThirdParty/Workers/draco_decoder_nodejs.js",
        wasmBinaryFile: "ThirdParty/draco_decoder.wasm",
      })
      .then(function () {
        // 准备好了
        DracoLoader._taskProcessorReady = true;
      });
    // 任务处理程序
    DracoLoader._decoderTaskProcessor = processor;
  }

  // 返回任务处理程序
  return DracoLoader._decoderTaskProcessor;
};

/**
 * Returns true if the model uses or requires KHR_draco_mesh_compression.
 *
 * @private
 */
// 使用了Draco压缩扩展
DracoLoader.hasExtension = function (model) {
  return (
    defined(model.extensionsRequired.KHR_draco_mesh_compression) ||
    defined(model.extensionsUsed.KHR_draco_mesh_compression)
  );
};

// 将解压后的数据重新格式化成gltf规范,并存储到loadResources中
function addBufferToLoadResources(loadResources, typedArray) {
  // Create a new id to differentiate from original glTF bufferViews
  // 重新创建一个bufferViewid
  var bufferViewId =
    "runtime." + Object.keys(loadResources.createdBufferViews).length;

  // 原始的gltf中的buffer数组
  var loadResourceBuffers = loadResources.buffers;
  // 原来数据的长度作为新的id
  var id = Object.keys(loadResourceBuffers).length;
  // 存储解压完的数据到loadResources中
  loadResourceBuffers[id] = typedArray;
  // 在BufferViews中添加新的数据索引,符合gltf规范
  loadResources.createdBufferViews[bufferViewId] = {
    buffer: id,
    byteOffset: 0,
    byteLength: typedArray.byteLength,
  };

  // 返回bufferViewid
  return bufferViewId;
}

function addNewVertexBuffer(typedArray, model, context) {
  var loadResources = model._loadResources;
  // 依据解压后的数据重新创建buffer、bufferview规范,并将数据通过索引连接起来
  var id = addBufferToLoadResources(loadResources, typedArray);
  // 待创建的顶点缓存
  loadResources.vertexBuffersToCreate.enqueue(id);
  // 返回bufferView的所以你
  return id;
}

function addNewIndexBuffer(indexArray, model, context) {
  // 数据
  var typedArray = indexArray.typedArray;
  var loadResources = model._loadResources;
  // 
  var id = addBufferToLoadResources(loadResources, typedArray);
  loadResources.indexBuffersToCreate.enqueue({
    id: id,
    componentType: ComponentDatatype.fromTypedArray(typedArray),
  });

  // 位置
  return {
    bufferViewId: id,
    numberOfIndices: indexArray.numberOfIndices,
  };
}

function scheduleDecodingTask(
  decoderTaskProcessor,
  model,
  loadResources,
  context
) {
  // 解码器程序还没有准备好就
  if (!DracoLoader._taskProcessorReady) {
    // The task processor is not ready to schedule tasks
    return;
  }

  // 获取最早的解码任务
  var taskData = loadResources.primitivesToDecode.peek();
  // 没有待处理的任务
  if (!defined(taskData)) {
    // All primitives are processing
    return;
  }

  // 调度任务,处理buffer数据
  var promise = decoderTaskProcessor.scheduleTask(taskData, [
    taskData.array.buffer,
  ]);

  // 调度失败返回
  if (!defined(promise)) {
    // Cannot schedule another task this frame
    return;
  }

  // 正在解码的任务数量
  loadResources.activeDecodingTasks++;
  // 删除解码完成的一个数据
  loadResources.primitivesToDecode.dequeue();
  // 等待任务处理完成
  return promise.then(function (result) {
    // 正在处理的任务数量减少
    loadResources.activeDecodingTasks--;

    // 解码后的索引数据
    var decodedIndexBuffer = addNewIndexBuffer(
      result.indexArray,
      model,
      context
    );

    var attributes = {};
    // 获取解码后的属性数据
    var decodedAttributeData = result.attributeData;
    for (var attributeName in decodedAttributeData) {
      if (decodedAttributeData.hasOwnProperty(attributeName)) {
        var attribute = decodedAttributeData[attributeName];
        // 属性的数据
        var vertexArray = attribute.array;
        // 创建顶点缓存
        var vertexBufferView = addNewVertexBuffer(vertexArray, model, context);

        // 属性的bufferView索引
        var data = attribute.data;
        data.bufferView = vertexBufferView;
        // 类似于gltf的primitive.attributes.position等
        attributes[attributeName] = data;
      }
    }

    // 解码后的数据
    model._decodedData[taskData.mesh + ".primitive." + taskData.primitive] = {
      bufferView: decodedIndexBuffer.bufferViewId,          // bufferViewId的索引
      numberOfIndices: decodedIndexBuffer.numberOfIndices,  // 数量??
      attributes: attributes,
    };

  });
}

DracoLoader._decodedModelResourceCache = undefined;

/**
 * Parses draco extension on model primitives and
 * adds the decoding data to the model's load resources.
 *
 * @private
 */
// 解析数据
DracoLoader.parse = function (model, context) {
  // 是否存在扩展
  if (!DracoLoader.hasExtension(model)) {
    return;
  }

  // 加载资源
  var loadResources = model._loadResources;
  // 缓存key
  var cacheKey = model.cacheKey;
  // 缓存
  if (defined(cacheKey)) {
    // 解码缓存中没有数据
    if (!defined(DracoLoader._decodedModelResourceCache)) {
      // 上下文的缓存中也没有这个数据
      if (!defined(context.cache.modelDecodingCache)) {
        // 上下文中创建解码缓存
        context.cache.modelDecodingCache = {};
      }

      // 指向上下文解码缓存
      DracoLoader._decodedModelResourceCache = context.cache.modelDecodingCache;
    }

    // Decoded data for model will be loaded from cache
    // 缓存中加载
    var cachedData = DracoLoader._decodedModelResourceCache[cacheKey];
    if (defined(cachedData)) {
      // 引用计数增加
      cachedData.count++;
      // 正在处理解码缓存
      loadResources.pendingDecodingCache = true;
      return;
    }
  }

  // 着色器中解析
  var dequantizeInShader = model._dequantizeInShader;
  var gltf = model.gltf;

  // 遍历mesh
  ForEach.mesh(gltf, function (mesh, meshId) {
    // 遍历primitive
    ForEach.meshPrimitive(mesh, function (primitive, primitiveId) {

      // 没有扩展
      if (!defined(primitive.extensions)) {
        return;
      }

      // 不是压缩过的数据就不用处理了
      var compressionData = primitive.extensions.KHR_draco_mesh_compression;
      if (!defined(compressionData)) {
        return;
      }

      // 获取bufferView数据
      var bufferView = gltf.bufferViews[compressionData.bufferView];
      // 获取数据块
      var typedArray = arraySlice(
        gltf.buffers[bufferView.buffer].extras._pipeline.source,
        bufferView.byteOffset,
        bufferView.byteOffset + bufferView.byteLength
      );

      // 存储待解压的draco数据
      loadResources.primitivesToDecode.enqueue({
        mesh: meshId,                // 数据属于哪一个mesh
        primitive: primitiveId,      // 数据是哪一个primitive的
        array: typedArray,           // 数据
        bufferView: bufferView,      // 数据的归属     
        compressedAttributes: compressionData.attributes,       // 压缩过的属性,这些属性对应的数据
        dequantizeInShader: dequantizeInShader,                 // 是否在着色器中解码
      });

    });
  });
};

/**
 * Schedules decoding tasks available this frame.
 * 调度此帧可用的解码任务
 * 
 * @private
 */
// 解码模型
DracoLoader.decodeModel = function (model, context) {
  // 数据是否压缩过
  if (!DracoLoader.hasExtension(model)) {
    return when.resolve();
  }

  // 加载资源
  var loadResources = model._loadResources;
  // 缓存key
  var cacheKey = model.cacheKey;
  if (defined(cacheKey) && defined(DracoLoader._decodedModelResourceCache)) {
    // 缓存过的数据,不用解码
    var cachedData = DracoLoader._decodedModelResourceCache[cacheKey];
    // Load decoded data for model when cache is ready
    if (defined(cachedData) && loadResources.pendingDecodingCache) {
      return when(cachedData.ready, function () {
        model._decodedData = cachedData.data;
        loadResources.pendingDecodingCache = false;
      });
    }

    // Decoded data for model should be cached when ready
    // 还没有准备好
    DracoLoader._decodedModelResourceCache[cacheKey] = {
      ready: false,
      count: 1,
      data: undefined,
    };
  }

  // 压缩过的数据不存在就返回
  if (loadResources.primitivesToDecode.length === 0) {
    // No more tasks to schedule
    return when.resolve();
  }

  // 任务处理器
  var decoderTaskProcessor = DracoLoader._getDecoderTaskProcessor();
  var decodingPromises = [];

  // 调度
  var promise = scheduleDecodingTask(
    decoderTaskProcessor,       // 调度任务
    model,                      // 模型类
    loadResources,              // 正在加载的资源
    context                     // webgl上下文
  );

  // 为什么第一个在循环之外??
  while (defined(promise)) {
    decodingPromises.push(promise);
    promise = scheduleDecodingTask(
      decoderTaskProcessor,   // 解码
      model,
      loadResources,
      context
    );
  }

  // 所有任务都处理完成
  return when.all(decodingPromises);
};

/**
 * Decodes a compressed point cloud. Returns undefined if the task cannot be scheduled.
 * 解码压缩的点云,任务不能调度就返回undefined
 * @private
 */
DracoLoader.decodePointCloud = function (parameters) {
  // 获取任务处理程序
  var decoderTaskProcessor = DracoLoader._getDecoderTaskProcessor();
  // 没有准备好就返回
  if (!DracoLoader._taskProcessorReady) {
    // The task processor is not ready to schedule tasks
    return;
  }
  // 调度任务
  return decoderTaskProcessor.scheduleTask(parameters, [
    parameters.buffer.buffer,
  ]);
};

/**
 * Decodes a buffer view. Returns undefined if the task cannot be scheduled.
 * 解码bufferView数据,如果没有调度任务,则返回undefined
 *
 * @param {Object} options Object with the following properties:
 * @param {Uint8Array} options.array The typed array containing the buffer view data.
 * @param {Object} options.bufferView The glTF buffer view object.
 * @param {Object.<String, Number>} options.compressedAttributes The compressed attributes.
 * @param {Boolean} options.dequantizeInShader Whether POSITION and NORMAL attributes should be dequantized on the GPU.
 *
 * @returns {Promise} A promise that resolves to the decoded indices and attributes.
 * @private
 */
DracoLoader.decodeBufferView = function (options) {
  var decoderTaskProcessor = DracoLoader._getDecoderTaskProcessor();
  if (!DracoLoader._taskProcessorReady) {
    // The task processor is not ready to schedule tasks
    return;
  }

  return decoderTaskProcessor.scheduleTask(options, [options.array.buffer]);
};

/**
 * Caches a models decoded data so it doesn't need to decode more than once.
 * 如果设置了cacheKey,缓存解码后的数据
 * @private
 */
DracoLoader.cacheDataForModel = function (model) {
  var cacheKey = model.cacheKey;
  if (defined(cacheKey) && defined(DracoLoader._decodedModelResourceCache)) {
    var cachedData = DracoLoader._decodedModelResourceCache[cacheKey];
    if (defined(cachedData)) {
      cachedData.ready = true;
      cachedData.data = model._decodedData;
    }
  }
};

/**
 * Destroys the cached data that this model references if it is no longer in use.
 * 删除不在使用的缓存数据
 * @private
 */
DracoLoader.destroyCachedDataForModel = function (model) {
  var cacheKey = model.cacheKey;
  if (defined(cacheKey) && defined(DracoLoader._decodedModelResourceCache)) {
    var cachedData = DracoLoader._decodedModelResourceCache[cacheKey];
    if (defined(cachedData) && --cachedData.count === 0) {
      delete DracoLoader._decodedModelResourceCache[cacheKey];
    }
  }
};

 上面是创建了一个draco的处理器,这个处理会创建线程,真正解码的是decodeDraco.js,这个会调用wasm的接口

// 解码
function decodePrimitive(parameters) {
  // 创建解码器
  var dracoDecoder = new draco.Decoder();

  // Skip all parameter types except generic
  // Note: As a temporary work-around until GetAttributeByUniqueId() works after
  // calling SkipAttributeTransform(), we will not skip attributes with multiple
  // sets of data in the glTF.
  // 数据默认必须有位置和法线
  var attributesToSkip = ["POSITION", "NORMAL"];
  // Primitive中的属性列表属性,属性列表对应的语义数据
  var compressedAttributes = parameters.compressedAttributes;
  if (!defined(compressedAttributes["COLOR_1"])) {
    // 颜色
    attributesToSkip.push("COLOR");
  }
  if (!defined(compressedAttributes["TEXCOORD_1"])) {
    // 纹理
    attributesToSkip.push("TEX_COORD");
  }
  // 后续是否还需要再vs中解码数据
  if (parameters.dequantizeInShader) {
    for (var i = 0; i < attributesToSkip.length; ++i) {
      // 跳过的属性??
      dracoDecoder.SkipAttributeTransform(draco[attributesToSkip[i]]);
    }
  }

  // 数据
  var bufferView = parameters.bufferView;
  // 创建解码后的缓存
  var buffer = new draco.DecoderBuffer();
  // 设置数据,传递给c++解码
  buffer.Init(parameters.array, bufferView.byteLength);
  // 获取解码类型
  var geometryType = dracoDecoder.GetEncodedGeometryType(buffer);
  // 几何类型??
  if (geometryType !== draco.TRIANGULAR_MESH) {
    throw new RuntimeError("Unsupported draco mesh geometry type.");
  }

  // 创建几何
  var dracoGeometry = new draco.Mesh();
  // 将buffer解码到mesh??
  var decodingStatus = dracoDecoder.DecodeBufferToMesh(buffer, dracoGeometry);
  if (!decodingStatus.ok() || dracoGeometry.ptr === 0) {
    // 解码错误
    throw new RuntimeError(
      "Error decoding draco mesh geometry: " + decodingStatus.error_msg()
    );
  }

  // 删除buffer
  draco.destroy(buffer);

  var attributeData = {};
  // 遍历属性
  for (var attributeName in compressedAttributes) {
    if (compressedAttributes.hasOwnProperty(attributeName)) {
      // Since GetAttributeByUniqueId() only works on attributes that we have not called
      // SkipAttributeTransform() on, we must first store a `dracoAttributeName` in case
      // we call GetAttributeId() instead.
      var dracoAttributeName = attributeName;
      if (attributeName === "TEXCOORD_0") {
        dracoAttributeName = "TEX_COORD";
      }
      if (attributeName === "COLOR_0") {
        dracoAttributeName = "COLOR";
      }

      var dracoAttribute;
      if (attributesToSkip.includes(dracoAttributeName)) {
        // 获取数据对应的id
        var dracoAttributeId = dracoDecoder.GetAttributeId(
          dracoGeometry,
          draco[dracoAttributeName]
        );
        // 获取数据偏移
        dracoAttribute = dracoDecoder.GetAttribute(
          dracoGeometry,
          dracoAttributeId
        );
      } else {
        var compressedAttribute = compressedAttributes[attributeName];
        dracoAttribute = dracoDecoder.GetAttributeByUniqueId(
          dracoGeometry,
          compressedAttribute
        );
      }

      // 属性对应的数据
      attributeData[attributeName] = decodeAttribute(
        dracoGeometry,     // 解码后的几何数据
        dracoDecoder,      // 解码器  
        dracoAttribute     // 解码数据对应的属性数据偏移
      );
    }
  }

  var result = {
    indexArray: decodeIndexArray(dracoGeometry, dracoDecoder),    // 处理索引数据
    attributeData: attributeData,   // 解码后的数据
  };

  // 销毁句柄
  draco.destroy(dracoGeometry);
  draco.destroy(dracoDecoder);

  return result;
}

// 解码数据
function decode(parameters) {
  // 解码bufferView
  if (defined(parameters.bufferView)) {
    return decodePrimitive(parameters);
  }
  return decodePointCloud(parameters);
}

// 初始化
function initWorker(dracoModule) {
  draco = dracoModule;
  self.onmessage = createTaskProcessorWorker(decode);
  self.postMessage(true);
}

// 解码数据
function decodeDraco(event) {
  var data = event.data;

  // Expect the first message to be to load a web assembly module
  // 配置信息
  var wasmConfig = data.webAssemblyConfig;
  if (defined(wasmConfig)) {
    // Require and compile WebAssembly module, or use fallback if not supported
    // 下载wasm模块
    return require([wasmConfig.modulePath], function (dracoModule) {
      if (defined(wasmConfig.wasmBinaryFile)) {
        if (!defined(dracoModule)) {
          dracoModule = self.DracoDecoderModule;
        }

        // 初始化
        dracoModule(wasmConfig).then(function (compiledModule) {
          // 开启线程中的线程
          initWorker(compiledModule);
        });
      } else {
        initWorker(dracoModule());
      }
    });
  }
}

通过primitive中的语义属性为索引,直接找到bufferview数据索引,根据索引解码buffer数据,buffer解析完成后会根据face创建索引,最终返回解码后的数据

 var result = {
    indexArray: decodeIndexArray(dracoGeometry, dracoDecoder),    // 处理索引数据
    attributeData: attributeData,   // 解码后的数据
  };

解码完成后的数据被存储在了model._decodedData中

function scheduleDecodingTask(
  decoderTaskProcessor,
  model,
  loadResources,
  context
) {
  // 解码器程序还没有准备好就
  if (!DracoLoader._taskProcessorReady) {
    // The task processor is not ready to schedule tasks
    return;
  }

  // 获取最早的解码任务
  var taskData = loadResources.primitivesToDecode.peek();
  // 没有待处理的任务
  if (!defined(taskData)) {
    // All primitives are processing
    return;
  }

  // 调度任务,处理buffer数据
  var promise = decoderTaskProcessor.scheduleTask(taskData, [
    taskData.array.buffer,
  ]);

  // 调度失败返回
  if (!defined(promise)) {
    // Cannot schedule another task this frame
    return;
  }

  // 正在解码的任务数量
  loadResources.activeDecodingTasks++;
  // 删除解码完成的一个数据
  loadResources.primitivesToDecode.dequeue();
  // 等待任务处理完成
  return promise.then(function (result) {
    // 正在处理的任务数量减少
    loadResources.activeDecodingTasks--;

    // 解码后的索引数据
    var decodedIndexBuffer = addNewIndexBuffer(
      result.indexArray,
      model,
      context
    );

    var attributes = {};
    // 获取解码后的属性数据
    var decodedAttributeData = result.attributeData;
    for (var attributeName in decodedAttributeData) {
      if (decodedAttributeData.hasOwnProperty(attributeName)) {
        var attribute = decodedAttributeData[attributeName];
        // 属性的数据
        var vertexArray = attribute.array;
        // 创建顶点缓存
        var vertexBufferView = addNewVertexBuffer(vertexArray, model, context);

        // 属性的bufferView索引
        var data = attribute.data;
        data.bufferView = vertexBufferView;
        // 类似于gltf的primitive.attributes.position等
        attributes[attributeName] = data;
      }
    }

    // 解码后的数据
    model._decodedData[taskData.mesh + ".primitive." + taskData.primitive] = {
      bufferView: decodedIndexBuffer.bufferViewId,          // bufferViewId的索引
      numberOfIndices: decodedIndexBuffer.numberOfIndices,  // 数量??
      attributes: attributes,
    };

  });
}

这里的model._decodedData的结构是比照gltf中bufferView引用buffer的结构进行组织的,后续的使用能够和流程匹配上。对于DracoLoader.cacheDataForModel、DracoLoader.destroyCachedDataForModel主要是用来缓存数据的,目前没有用到。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值