diff --git a/examples/glview/glview.cc b/examples/glview/glview.cc index 4a300a4..a551bc2 100644 --- a/examples/glview/glview.cc +++ b/examples/glview/glview.cc @@ -529,15 +529,15 @@ static void DrawMesh(tinygltf::Model &model, const tinygltf::Mesh &mesh) { const tinygltf::Accessor &accessor = model.accessors[it->second]; glBindBuffer(GL_ARRAY_BUFFER, gBufferState[accessor.bufferView].vb); CheckErrors("bind buffer"); - int count = 1; + int size = 1; if (accessor.type == TINYGLTF_TYPE_SCALAR) { - count = 1; + size = 1; } else if (accessor.type == TINYGLTF_TYPE_VEC2) { - count = 2; + size = 2; } else if (accessor.type == TINYGLTF_TYPE_VEC3) { - count = 3; + size = 3; } else if (accessor.type == TINYGLTF_TYPE_VEC4) { - count = 4; + size = 4; } else { assert(0); } @@ -546,9 +546,9 @@ static void DrawMesh(tinygltf::Model &model, const tinygltf::Mesh &mesh) { (it->first.compare("NORMAL") == 0) || (it->first.compare("TEXCOORD_0") == 0)) { if (gGLProgramState.attribs[it->first] >= 0) { - glVertexAttribPointer(gGLProgramState.attribs[it->first], count, - accessor.componentType, GL_FALSE, - 0, + glVertexAttribPointer(gGLProgramState.attribs[it->first], size, + accessor.componentType, accessor.normalized ? GL_TRUE : GL_FALSE, + model.bufferViews[accessor.bufferView].byteStride, BUFFER_OFFSET(accessor.byteOffset)); CheckErrors("vertex attrib pointer"); glEnableVertexAttribArray(gGLProgramState.attribs[it->first]); diff --git a/loader_example.cc b/loader_example.cc index 55f57ec..c575c76 100644 --- a/loader_example.cc +++ b/loader_example.cc @@ -409,6 +409,8 @@ static void Dump(const tinygltf::Model &model) { << std::endl; std::cout << Indent(2) << "byteOffset : " << bufferView.byteOffset << std::endl; + std::cout << Indent(2) << "byteStride : " << bufferView.byteStride + << std::endl; std::cout << Indent(2) << "target : " << PrintTarget(bufferView.target) << std::endl; diff --git a/tiny_gltf.h b/tiny_gltf.h index 87f215a..1cc2c9b 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h @@ -386,12 +386,13 @@ struct BufferView { int buffer; // Required size_t byteOffset; // minimum 0, default 0 size_t byteLength; // required, minimum 1 - size_t byteStride; // minimum 4, maximum 252 (multiple of 4) + size_t byteStride; // minimum 4, maximum 252 (multiple of 4), default 0 = + // understood to be tightly packed int target; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"] int pad0; Value extras; - BufferView() : byteOffset(0), byteStride(4) {} + BufferView() : byteOffset(0), byteStride(0) {} }; struct Accessor { @@ -399,13 +400,16 @@ struct Accessor { // are not supported std::string name; size_t byteOffset; + bool normalized; // optinal. int componentType; // (required) One of TINYGLTF_COMPONENT_TYPE_*** size_t count; // required int type; // (required) One of TINYGLTF_TYPE_*** .. Value extras; - std::vector minValues; // required - std::vector maxValues; // required + std::vector minValues; // optional + std::vector maxValues; // optional + + // TODO(syoyo): "sparse" Accessor() { bufferView = -1; } }; @@ -1122,12 +1126,18 @@ static bool ParseExtrasProperty(Value *ret, const picojson::object &o) { static bool ParseBooleanProperty(bool *ret, std::string *err, const picojson::object &o, - const std::string &property, bool required) { + const std::string &property, + const bool required, + const std::string &parent_node = "") { picojson::object::const_iterator it = o.find(property); if (it == o.end()) { if (required) { if (err) { - (*err) += "'" + property + "' property is missing.\n"; + (*err) += "'" + property + "' property is missing"; + if (!parent_node.empty()) { + (*err) += " in " + parent_node; + } + (*err) += ".\n"; } } return false; @@ -1579,8 +1589,30 @@ static bool ParseBufferView(BufferView *bufferView, std::string *err, return false; } - double byteStride = 4.0; - ParseNumberProperty(&byteStride, err, o, "byteStride", false); + size_t byteStride = 0; + double byteStrideValue = 0.0; + if (!ParseNumberProperty(&byteStrideValue, err, o, "byteStride", false)) { + // Spec says: When byteStride of referenced bufferView is not defined, it + // means that accessor elements are tightly packed, i.e., effective stride + // equals the size of the element. + // We cannot determine the actual byteStride until Accessor are parsed, thus + // set 0(= tightly packed) here(as done in OpenGL's VertexAttribPoiner) + byteStride = 0; + } else { + byteStride = static_cast(byteStrideValue); + } + + if ((byteStride > 252) || ((byteStride % 4) != 0)) { + if (err) { + std::stringstream ss; + ss << "Invalid `byteStride' value. `byteStride' must be the multiple of " + "4 : " + << byteStride << std::endl; + + (*err) += ss.str(); + } + return false; + } double target = 0.0; ParseNumberProperty(&target, err, o, "target", false); @@ -1614,6 +1646,9 @@ static bool ParseAccessor(Accessor *accessor, std::string *err, double byteOffset = 0.0; ParseNumberProperty(&byteOffset, err, o, "byteOffset", false, "Accessor"); + bool normalized = false; + ParseBooleanProperty(&normalized, err, o, "normalized", false, "Accessor"); + double componentType = 0.0; if (!ParseNumberProperty(&componentType, err, o, "componentType", true, "Accessor")) { @@ -1657,15 +1692,11 @@ static bool ParseAccessor(Accessor *accessor, std::string *err, accessor->minValues.clear(); accessor->maxValues.clear(); - if (!ParseNumberArrayProperty(&accessor->minValues, err, o, "min", true, - "Accessor")) { - return false; - } + ParseNumberArrayProperty(&accessor->minValues, err, o, "min", false, + "Accessor"); - if (!ParseNumberArrayProperty(&accessor->maxValues, err, o, "max", true, - "Accessor")) { - return false; - } + ParseNumberArrayProperty(&accessor->maxValues, err, o, "max", false, + "Accessor"); accessor->count = static_cast(count); accessor->bufferView = static_cast(bufferView);