2010-02-05 13:10:32 -07:00
|
|
|
// mesh.cpp
|
|
|
|
//
|
|
|
|
// Copyright (C) 2004-2010, the Celestia Development Team
|
|
|
|
// Original version by Chris Laurel <claurel@gmail.com>
|
|
|
|
//
|
|
|
|
// This program is free software; you can redistribute it and/or
|
|
|
|
// modify it under the terms of the GNU General Public License
|
|
|
|
// as published by the Free Software Foundation; either version 2
|
|
|
|
// of the License, or (at your option) any later version.
|
|
|
|
|
2021-11-27 10:41:51 -07:00
|
|
|
#include <algorithm>
|
2021-11-27 07:29:08 -07:00
|
|
|
#include <array>
|
|
|
|
#include <cstring>
|
2021-11-27 10:41:51 -07:00
|
|
|
#include <iterator>
|
|
|
|
#include <tuple>
|
|
|
|
#include <utility>
|
2022-01-25 11:08:25 -07:00
|
|
|
#include <celutil/logger.h>
|
2010-02-05 13:10:32 -07:00
|
|
|
|
2021-11-27 07:29:08 -07:00
|
|
|
#include "mesh.h"
|
2010-02-05 13:10:32 -07:00
|
|
|
|
2022-01-25 11:08:25 -07:00
|
|
|
using celestia::util::GetLogger;
|
2010-02-05 13:10:32 -07:00
|
|
|
|
2021-11-27 07:29:08 -07:00
|
|
|
namespace cmod
|
2010-02-05 13:10:32 -07:00
|
|
|
{
|
2021-11-27 10:41:51 -07:00
|
|
|
namespace
|
|
|
|
{
|
2021-11-27 07:29:08 -07:00
|
|
|
|
2021-11-27 10:41:51 -07:00
|
|
|
template<typename It>
|
|
|
|
VertexDescription appendingAttributes(const VertexDescription& desc, It begin, It end)
|
2010-02-05 13:10:32 -07:00
|
|
|
{
|
2021-11-27 10:41:51 -07:00
|
|
|
std::vector<VertexAttribute> allAttributes;
|
|
|
|
allAttributes.reserve(desc.attributes.size() + (end - begin));
|
|
|
|
|
|
|
|
std::copy(desc.attributes.cbegin(), desc.attributes.cend(), std::back_inserter(allAttributes));
|
|
|
|
for (auto it = begin; it != end; ++it)
|
2010-02-05 13:10:32 -07:00
|
|
|
{
|
2021-11-27 10:41:51 -07:00
|
|
|
allAttributes.push_back(*it);
|
2010-02-05 13:10:32 -07:00
|
|
|
}
|
2021-11-27 10:41:51 -07:00
|
|
|
|
2021-11-27 16:03:42 -07:00
|
|
|
return VertexDescription(std::move(allAttributes));
|
2010-02-05 13:10:32 -07:00
|
|
|
}
|
|
|
|
|
2022-01-27 13:28:49 -07:00
|
|
|
bool
|
|
|
|
isOpaqueMaterial(const Material &material)
|
|
|
|
{
|
|
|
|
return (!(material.opacity > 0.01f && material.opacity < 1.0f)) &&
|
|
|
|
material.blend != BlendMode::AdditiveBlend;
|
|
|
|
}
|
|
|
|
|
2021-11-27 10:41:51 -07:00
|
|
|
} // end unnamed namespace
|
2010-02-05 13:10:32 -07:00
|
|
|
|
2021-11-27 10:41:51 -07:00
|
|
|
|
|
|
|
bool operator==(const VertexAttribute& a, const VertexAttribute& b)
|
2010-02-05 13:10:32 -07:00
|
|
|
{
|
2021-11-27 16:03:42 -07:00
|
|
|
return std::tie(a.semantic, a.format, a.offsetWords) == std::tie(b.semantic, b.format, b.offsetWords);
|
2010-02-05 13:10:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-27 10:41:51 -07:00
|
|
|
bool operator<(const VertexAttribute& a, const VertexAttribute& b)
|
2010-02-05 13:10:32 -07:00
|
|
|
{
|
2021-11-27 16:03:42 -07:00
|
|
|
return std::tie(a.semantic, a.format, a.offsetWords) < std::tie(b.semantic, b.format, b.offsetWords);
|
2021-11-27 10:41:51 -07:00
|
|
|
}
|
2018-08-09 09:25:54 -06:00
|
|
|
|
2021-11-27 10:41:51 -07:00
|
|
|
|
2021-11-27 16:03:42 -07:00
|
|
|
VertexDescription::VertexDescription(std::vector<VertexAttribute>&& _attributes) :
|
|
|
|
strideBytes(0),
|
2021-11-27 10:41:51 -07:00
|
|
|
attributes(std::move(_attributes))
|
|
|
|
{
|
2021-11-27 16:03:42 -07:00
|
|
|
for (const auto& attr : attributes)
|
|
|
|
{
|
|
|
|
strideBytes += VertexAttribute::getFormatSizeWords(attr.format) * sizeof(VWord);
|
|
|
|
}
|
|
|
|
|
2021-11-27 10:41:51 -07:00
|
|
|
if (!attributes.empty())
|
2010-02-05 13:10:32 -07:00
|
|
|
{
|
2021-11-27 10:41:51 -07:00
|
|
|
buildSemanticMap();
|
2010-02-05 13:10:32 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: This should be called in the constructor; we should start using
|
|
|
|
// exceptions in Celestia.
|
|
|
|
bool
|
2021-11-27 07:29:08 -07:00
|
|
|
VertexDescription::validate() const
|
2010-02-05 13:10:32 -07:00
|
|
|
{
|
2021-11-27 16:03:42 -07:00
|
|
|
unsigned int stride = strideBytes / sizeof(VWord);
|
2021-11-27 10:41:51 -07:00
|
|
|
for (const VertexAttribute& attr : attributes)
|
2010-02-05 13:10:32 -07:00
|
|
|
{
|
|
|
|
// Validate the attribute
|
2021-11-27 16:03:42 -07:00
|
|
|
if (attr.offsetWords + VertexAttribute::getFormatSizeWords(attr.format) > stride)
|
2010-02-05 13:10:32 -07:00
|
|
|
return false;
|
|
|
|
// TODO: check for repetition of attributes
|
|
|
|
// if (vertexAttributeMap[attr->semantic].format != InvalidFormat)
|
|
|
|
// return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-11-27 10:41:51 -07:00
|
|
|
|
|
|
|
void
|
|
|
|
VertexDescription::buildSemanticMap()
|
2020-12-11 06:38:12 -07:00
|
|
|
{
|
2021-11-27 10:41:51 -07:00
|
|
|
for (const VertexAttribute& attr : attributes)
|
2020-12-11 06:38:12 -07:00
|
|
|
{
|
2021-11-27 10:41:51 -07:00
|
|
|
semanticMap[static_cast<std::size_t>(attr.semantic)] = attr;
|
2020-12-11 06:38:12 -07:00
|
|
|
}
|
|
|
|
}
|
2010-02-05 13:10:32 -07:00
|
|
|
|
2021-11-27 10:41:51 -07:00
|
|
|
|
|
|
|
void
|
|
|
|
VertexDescription::clearSemanticMap()
|
2010-02-05 13:10:32 -07:00
|
|
|
{
|
2021-11-27 10:41:51 -07:00
|
|
|
for (auto& i : semanticMap)
|
|
|
|
i = VertexAttribute();
|
2010-02-05 13:10:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-27 10:41:51 -07:00
|
|
|
bool operator==(const VertexDescription& a, const VertexDescription& b)
|
2010-02-05 13:10:32 -07:00
|
|
|
{
|
2021-11-27 16:03:42 -07:00
|
|
|
return std::tie(a.strideBytes, a.attributes) == std::tie(b.strideBytes, b.attributes);
|
2010-02-05 13:10:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-27 10:41:51 -07:00
|
|
|
bool operator<(const VertexDescription& a, const VertexDescription& b)
|
2010-02-05 13:10:32 -07:00
|
|
|
{
|
2021-11-27 16:03:42 -07:00
|
|
|
return std::tie(a.strideBytes, a.attributes) < std::tie(b.strideBytes, b.attributes);
|
2010-02-05 13:10:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-28 03:30:53 -07:00
|
|
|
PrimitiveGroup
|
|
|
|
PrimitiveGroup::clone() const
|
|
|
|
{
|
|
|
|
PrimitiveGroup newGroup;
|
|
|
|
newGroup.prim = prim;
|
|
|
|
newGroup.materialIndex = materialIndex;
|
|
|
|
newGroup.indices = indices;
|
|
|
|
newGroup.primOverride = primOverride;
|
|
|
|
newGroup.vertexOverride = vertexOverride;
|
|
|
|
newGroup.vertexCountOverride = vertexCountOverride;
|
|
|
|
newGroup.indicesOverride = indicesOverride;
|
|
|
|
newGroup.vertexDescriptionOverride = vertexDescriptionOverride.clone();
|
|
|
|
return newGroup;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-02-05 13:10:32 -07:00
|
|
|
unsigned int
|
2021-11-27 07:29:08 -07:00
|
|
|
PrimitiveGroup::getPrimitiveCount() const
|
2010-02-05 13:10:32 -07:00
|
|
|
{
|
|
|
|
switch (prim)
|
|
|
|
{
|
2021-11-27 07:29:08 -07:00
|
|
|
case PrimitiveGroupType::TriList:
|
2021-11-27 10:41:51 -07:00
|
|
|
return indices.size() / 3;
|
2021-11-27 07:29:08 -07:00
|
|
|
case PrimitiveGroupType::TriStrip:
|
|
|
|
case PrimitiveGroupType::TriFan:
|
2021-11-27 10:41:51 -07:00
|
|
|
return indices.size() - 2;
|
2021-11-27 07:29:08 -07:00
|
|
|
case PrimitiveGroupType::LineList:
|
2021-11-27 10:41:51 -07:00
|
|
|
return indices.size() / 2;
|
2021-11-27 07:29:08 -07:00
|
|
|
case PrimitiveGroupType::LineStrip:
|
2021-11-27 10:41:51 -07:00
|
|
|
return indices.size() - 2;
|
2021-11-27 07:29:08 -07:00
|
|
|
case PrimitiveGroupType::PointList:
|
|
|
|
case PrimitiveGroupType::SpriteList:
|
2021-11-27 10:41:51 -07:00
|
|
|
return indices.size();
|
2010-02-05 13:10:32 -07:00
|
|
|
default:
|
|
|
|
// Invalid value
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-28 03:30:53 -07:00
|
|
|
Mesh
|
|
|
|
Mesh::clone() const
|
2010-02-05 13:10:32 -07:00
|
|
|
{
|
2021-11-28 03:30:53 -07:00
|
|
|
Mesh newMesh;
|
|
|
|
newMesh.vertexDesc = vertexDesc.clone();
|
|
|
|
newMesh.nVertices = nVertices;
|
|
|
|
newMesh.vertices = vertices;
|
|
|
|
newMesh.groups.reserve(groups.size());
|
|
|
|
std::transform(groups.cbegin(), groups.cend(), std::back_inserter(newMesh.groups),
|
|
|
|
[](const PrimitiveGroup& group) { return group.clone(); });
|
|
|
|
newMesh.name = name;
|
|
|
|
return newMesh;
|
2010-02-05 13:10:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2021-11-27 16:03:42 -07:00
|
|
|
Mesh::setVertices(unsigned int _nVertices, std::vector<VWord>&& vertexData)
|
2010-02-05 13:10:32 -07:00
|
|
|
{
|
|
|
|
nVertices = _nVertices;
|
2021-11-27 16:03:42 -07:00
|
|
|
vertices = std::move(vertexData);
|
2010-02-05 13:10:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
2021-11-27 10:41:51 -07:00
|
|
|
Mesh::setVertexDescription(VertexDescription&& desc)
|
2010-02-05 13:10:32 -07:00
|
|
|
{
|
|
|
|
if (!desc.validate())
|
|
|
|
return false;
|
|
|
|
|
2021-11-27 10:41:51 -07:00
|
|
|
vertexDesc = std::move(desc);
|
2010-02-05 13:10:32 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-27 07:29:08 -07:00
|
|
|
const VertexDescription& Mesh::getVertexDescription() const
|
2010-02-05 13:10:32 -07:00
|
|
|
{
|
|
|
|
return vertexDesc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-27 07:29:08 -07:00
|
|
|
const PrimitiveGroup*
|
2010-02-05 13:10:32 -07:00
|
|
|
Mesh::getGroup(unsigned int index) const
|
|
|
|
{
|
|
|
|
if (index >= groups.size())
|
2018-09-22 07:13:49 -06:00
|
|
|
return nullptr;
|
|
|
|
|
2021-11-28 03:30:53 -07:00
|
|
|
return &groups[index];
|
2010-02-05 13:10:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-27 07:29:08 -07:00
|
|
|
PrimitiveGroup*
|
2010-02-10 13:52:43 -07:00
|
|
|
Mesh::getGroup(unsigned int index)
|
|
|
|
{
|
|
|
|
if (index >= groups.size())
|
2018-09-22 07:13:49 -06:00
|
|
|
return nullptr;
|
|
|
|
|
2021-11-28 03:30:53 -07:00
|
|
|
return &groups[index];
|
2010-02-10 13:52:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-02-05 13:10:32 -07:00
|
|
|
unsigned int
|
2021-11-28 03:30:53 -07:00
|
|
|
Mesh::addGroup(PrimitiveGroup&& group)
|
2010-02-05 13:10:32 -07:00
|
|
|
{
|
2021-11-28 03:30:53 -07:00
|
|
|
groups.push_back(std::move(group));
|
2010-02-05 13:10:32 -07:00
|
|
|
return groups.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-28 03:30:53 -07:00
|
|
|
PrimitiveGroup
|
2021-11-27 16:03:42 -07:00
|
|
|
Mesh::createLinePrimitiveGroup(bool lineStrip, const std::vector<Index32>& indices)
|
2020-12-11 06:38:12 -07:00
|
|
|
{
|
|
|
|
// Transform LINE_STRIP/LINES to triangle vertices
|
|
|
|
// Get information of the position attributes
|
2021-11-27 07:29:08 -07:00
|
|
|
auto positionAttributes = vertexDesc.getAttribute(VertexAttributeSemantic::Position);
|
2021-11-27 16:03:42 -07:00
|
|
|
int positionSize = VertexAttribute::getFormatSizeWords(positionAttributes.format);
|
|
|
|
int positionSizeBytes = positionSize * sizeof(VWord);
|
|
|
|
int positionOffset = positionAttributes.offsetWords;
|
2020-12-11 06:38:12 -07:00
|
|
|
|
2021-11-27 16:03:42 -07:00
|
|
|
int originalStrideBytes = vertexDesc.strideBytes;
|
|
|
|
int originalStride = originalStrideBytes / sizeof(VWord);
|
2020-12-11 06:38:12 -07:00
|
|
|
// Add another position (next line end), and scale factor
|
|
|
|
// ORIGINAL ATTRIBUTES | NextVCoordAttributeIndex | ScaleFactorAttributeIndex
|
2021-11-27 16:03:42 -07:00
|
|
|
int stride = originalStride + positionSize + 1;
|
2020-12-11 06:38:12 -07:00
|
|
|
|
|
|
|
// Get line count
|
2021-11-27 10:41:51 -07:00
|
|
|
int lineCount = lineStrip ? indices.size() - 1 : indices.size() / 2;
|
2020-12-11 06:38:12 -07:00
|
|
|
int lineIndexCount = 6 * lineCount;
|
|
|
|
int lineVertexCount = 4 * lineCount;
|
|
|
|
|
|
|
|
// Create buffer to hold the transformed vertices/indices
|
2021-11-27 16:03:42 -07:00
|
|
|
std::vector<VWord> data(stride * lineVertexCount);
|
|
|
|
std::vector<Index32> newIndices;
|
2021-11-27 10:41:51 -07:00
|
|
|
newIndices.reserve(lineIndexCount);
|
2020-12-11 06:38:12 -07:00
|
|
|
|
2021-11-27 16:03:42 -07:00
|
|
|
VWord* ptr = data.data();
|
2020-12-11 06:38:12 -07:00
|
|
|
for (int i = 0; i < lineCount; ++i)
|
|
|
|
{
|
|
|
|
int thisIndex = indices[lineStrip ? i : i * 2];
|
|
|
|
int nextIndex = indices[lineStrip ? i + 1 : i * 2 + 1];
|
|
|
|
|
2021-11-27 16:03:42 -07:00
|
|
|
const VWord* origThisVertLoc = vertices.data() + thisIndex * originalStride;
|
|
|
|
const VWord* origNextVertLoc = vertices.data() + nextIndex * originalStride;
|
2020-12-11 06:38:12 -07:00
|
|
|
|
|
|
|
// Fill the info for the 4 vertices
|
2021-11-27 16:03:42 -07:00
|
|
|
std::memcpy(ptr, origThisVertLoc, originalStrideBytes);
|
|
|
|
std::memcpy(ptr + originalStride, origNextVertLoc + positionOffset, positionSizeBytes);
|
|
|
|
float scaleFactor = -0.5f;
|
|
|
|
std::memcpy(ptr + originalStride + positionSize, &scaleFactor, sizeof(float));
|
2020-12-11 06:38:12 -07:00
|
|
|
ptr += stride;
|
|
|
|
|
2021-11-27 16:03:42 -07:00
|
|
|
std::memcpy(ptr, origThisVertLoc, originalStrideBytes);
|
|
|
|
std::memcpy(ptr + originalStride, origNextVertLoc + positionOffset, positionSizeBytes);
|
|
|
|
scaleFactor = 0.5f;
|
|
|
|
std::memcpy(ptr + originalStride + positionSize, &scaleFactor, sizeof(float));
|
2020-12-11 06:38:12 -07:00
|
|
|
ptr += stride;
|
|
|
|
|
2021-11-27 16:03:42 -07:00
|
|
|
std::memcpy(ptr, origNextVertLoc, originalStrideBytes);
|
|
|
|
std::memcpy(ptr + originalStride, origThisVertLoc + positionOffset, positionSizeBytes);
|
|
|
|
scaleFactor = -0.5f;
|
|
|
|
std::memcpy(ptr + originalStride + positionSize, &scaleFactor, sizeof(float));
|
2020-12-11 06:38:12 -07:00
|
|
|
ptr += stride;
|
|
|
|
|
2021-11-27 16:03:42 -07:00
|
|
|
std::memcpy(ptr, origNextVertLoc, originalStrideBytes);
|
|
|
|
std::memcpy(ptr + originalStride, origThisVertLoc + positionOffset, positionSizeBytes);
|
|
|
|
scaleFactor = 0.5f;
|
|
|
|
std::memcpy(ptr + originalStride + positionSize, &scaleFactor, sizeof(float));
|
2020-12-11 06:38:12 -07:00
|
|
|
ptr += stride;
|
|
|
|
|
2021-11-27 16:03:42 -07:00
|
|
|
Index32 newIndex = 4 * i;
|
2020-12-11 06:38:12 -07:00
|
|
|
|
|
|
|
// Fill info for the 6 indices
|
2021-11-27 10:41:51 -07:00
|
|
|
newIndices.push_back(newIndex);
|
|
|
|
newIndices.push_back(newIndex + 1);
|
|
|
|
newIndices.push_back(newIndex + 2);
|
|
|
|
newIndices.push_back(newIndex + 2);
|
|
|
|
newIndices.push_back(newIndex + 3);
|
|
|
|
newIndices.push_back(newIndex);
|
2020-12-11 06:38:12 -07:00
|
|
|
}
|
|
|
|
|
2021-11-27 07:29:08 -07:00
|
|
|
std::array<VertexAttribute, 2> newAttributes = {
|
|
|
|
VertexAttribute(VertexAttributeSemantic::NextPosition, positionAttributes.format, originalStride),
|
|
|
|
VertexAttribute(VertexAttributeSemantic::ScaleFactor, VertexAttributeFormat::Float1, originalStride + positionSize),
|
2020-12-11 06:38:12 -07:00
|
|
|
};
|
2021-11-28 03:30:53 -07:00
|
|
|
|
|
|
|
PrimitiveGroup g;
|
|
|
|
g.vertexOverride = std::move(data);
|
|
|
|
g.vertexCountOverride = lineVertexCount;
|
|
|
|
g.vertexDescriptionOverride = appendingAttributes(vertexDesc, newAttributes.cbegin(), newAttributes.cend());
|
|
|
|
g.indicesOverride = std::move(newIndices);
|
|
|
|
g.primOverride = PrimitiveGroupType::TriList;
|
2020-12-11 06:38:12 -07:00
|
|
|
return g;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-02-05 13:10:32 -07:00
|
|
|
unsigned int
|
|
|
|
Mesh::addGroup(PrimitiveGroupType prim,
|
|
|
|
unsigned int materialIndex,
|
2021-11-27 16:03:42 -07:00
|
|
|
std::vector<Index32>&& indices)
|
2010-02-05 13:10:32 -07:00
|
|
|
{
|
2021-11-28 03:30:53 -07:00
|
|
|
PrimitiveGroup g;
|
2021-11-27 07:29:08 -07:00
|
|
|
if (prim == PrimitiveGroupType::LineStrip || prim == PrimitiveGroupType::LineList)
|
2021-11-27 10:41:51 -07:00
|
|
|
g = createLinePrimitiveGroup(prim == PrimitiveGroupType::LineStrip, indices);
|
2010-02-05 13:10:32 -07:00
|
|
|
|
2021-11-28 03:30:53 -07:00
|
|
|
g.indices = std::move(indices);
|
|
|
|
g.prim = prim;
|
|
|
|
g.materialIndex = materialIndex;
|
|
|
|
|
|
|
|
return addGroup(std::move(g));
|
2010-02-05 13:10:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
unsigned int
|
|
|
|
Mesh::getGroupCount() const
|
|
|
|
{
|
|
|
|
return groups.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
Mesh::clearGroups()
|
|
|
|
{
|
|
|
|
groups.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-27 07:29:08 -07:00
|
|
|
const std::string&
|
2010-02-05 13:10:32 -07:00
|
|
|
Mesh::getName() const
|
|
|
|
{
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2021-11-27 10:41:51 -07:00
|
|
|
Mesh::setName(std::string&& _name)
|
2010-02-05 13:10:32 -07:00
|
|
|
{
|
2021-11-27 10:41:51 -07:00
|
|
|
name = std::move(_name);
|
2010-02-05 13:10:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2021-11-27 16:03:42 -07:00
|
|
|
Mesh::remapIndices(const std::vector<Index32>& indexMap)
|
2010-02-05 13:10:32 -07:00
|
|
|
{
|
2021-11-28 03:30:53 -07:00
|
|
|
for (auto& group : groups)
|
2010-02-05 13:10:32 -07:00
|
|
|
{
|
2021-11-28 03:30:53 -07:00
|
|
|
for (auto& index : group.indices)
|
2010-02-05 13:10:32 -07:00
|
|
|
{
|
2021-11-27 10:41:51 -07:00
|
|
|
index = indexMap[index];
|
2010-02-05 13:10:32 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2021-11-27 07:29:08 -07:00
|
|
|
Mesh::remapMaterials(const std::vector<unsigned int>& materialMap)
|
2010-02-05 13:10:32 -07:00
|
|
|
{
|
2021-11-28 03:30:53 -07:00
|
|
|
for (auto& group : groups)
|
|
|
|
group.materialIndex = materialMap[group.materialIndex];
|
2010-02-05 13:10:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
Mesh::aggregateByMaterial()
|
|
|
|
{
|
2021-11-27 07:29:08 -07:00
|
|
|
std::sort(groups.begin(), groups.end(),
|
2021-11-28 03:30:53 -07:00
|
|
|
[](const PrimitiveGroup& g0, const PrimitiveGroup& g1)
|
2021-11-27 07:29:08 -07:00
|
|
|
{
|
2021-11-28 03:30:53 -07:00
|
|
|
return g0.materialIndex < g1.materialIndex;
|
2021-11-27 07:29:08 -07:00
|
|
|
});
|
2022-01-25 11:08:25 -07:00
|
|
|
mergePrimitiveGroups();
|
2010-02-05 13:10:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-01-25 11:08:25 -07:00
|
|
|
void
|
|
|
|
Mesh::mergePrimitiveGroups()
|
|
|
|
{
|
|
|
|
if (groups.size() < 2)
|
|
|
|
return;
|
|
|
|
|
|
|
|
std::vector<PrimitiveGroup> newGroups;
|
|
|
|
for (size_t i = 0; i < groups.size(); i++)
|
|
|
|
{
|
|
|
|
auto &g = groups[i];
|
|
|
|
|
|
|
|
if (g.vertexCountOverride == 0 && g.prim == PrimitiveGroupType::TriStrip)
|
|
|
|
{
|
|
|
|
std::vector<Index32> newIndices;
|
|
|
|
newIndices.reserve(g.indices.size() * 2);
|
|
|
|
for (size_t j = 0, e = g.indices.size() - 2; j < e; j++)
|
|
|
|
{
|
|
|
|
auto x = g.indices[j + 0];
|
|
|
|
auto y = g.indices[j + 1];
|
|
|
|
auto z = g.indices[j + 2];
|
|
|
|
// skip degenerated triangles
|
|
|
|
if (x == y || y == z || z == x)
|
|
|
|
continue;
|
|
|
|
if ((j & 1) != 0) // FIXME: CCW hardcoded
|
|
|
|
std::swap(y, z);
|
|
|
|
newIndices.push_back(x);
|
|
|
|
newIndices.push_back(y);
|
|
|
|
newIndices.push_back(z);
|
|
|
|
}
|
|
|
|
g.indices = std::move(newIndices);
|
|
|
|
g.prim = PrimitiveGroupType::TriList;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == 0 || g.vertexCountOverride != 0 || g.prim != PrimitiveGroupType::TriList)
|
|
|
|
{
|
|
|
|
newGroups.push_back(std::move(g));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
auto &p = newGroups.back();
|
|
|
|
if (p.prim != g.prim || p.materialIndex != g.materialIndex)
|
|
|
|
{
|
|
|
|
newGroups.push_back(std::move(g));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
p.indices.reserve(p.indices.size() + g.indices.size());
|
|
|
|
p.indices.insert(p.indices.end(), g.indices.begin(), g.indices.end());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
GetLogger()->info("Optimized mesh groups: had {} groups, now: {} of them.\n", groups.size(), newGroups.size());
|
|
|
|
groups = std::move(newGroups);
|
|
|
|
}
|
|
|
|
|
2010-02-05 13:10:32 -07:00
|
|
|
bool
|
2021-11-27 07:29:08 -07:00
|
|
|
Mesh::pick(const Eigen::Vector3d& rayOrigin, const Eigen::Vector3d& rayDirection, PickResult* result) const
|
2010-02-05 13:10:32 -07:00
|
|
|
{
|
|
|
|
double maxDistance = 1.0e30;
|
|
|
|
double closest = maxDistance;
|
|
|
|
|
|
|
|
// Pick will automatically fail without vertex positions--no reasonable
|
|
|
|
// mesh should lack these.
|
2021-11-27 07:29:08 -07:00
|
|
|
if (vertexDesc.getAttribute(VertexAttributeSemantic::Position).semantic != VertexAttributeSemantic::Position ||
|
|
|
|
vertexDesc.getAttribute(VertexAttributeSemantic::Position).format != VertexAttributeFormat::Float3)
|
2010-02-05 13:10:32 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-11-27 16:03:42 -07:00
|
|
|
unsigned int stride = vertexDesc.strideBytes / sizeof(VWord);
|
|
|
|
unsigned int posOffset = vertexDesc.getAttribute(VertexAttributeSemantic::Position).offsetWords;
|
|
|
|
const VWord* vdata = vertices.data();
|
2010-02-05 13:10:32 -07:00
|
|
|
|
|
|
|
// Iterate over all primitive groups in the mesh
|
2021-11-28 03:30:53 -07:00
|
|
|
for (const auto& group : groups)
|
2010-02-05 13:10:32 -07:00
|
|
|
{
|
2021-11-28 03:30:53 -07:00
|
|
|
PrimitiveGroupType primType = group.prim;
|
|
|
|
Index32 nIndices = group.indices.size();
|
2010-02-05 13:10:32 -07:00
|
|
|
|
|
|
|
// Only attempt to compute the intersection of the ray with triangle
|
|
|
|
// groups.
|
2021-11-27 07:29:08 -07:00
|
|
|
if ((primType == PrimitiveGroupType::TriList
|
|
|
|
|| primType == PrimitiveGroupType::TriStrip
|
|
|
|
|| primType == PrimitiveGroupType::TriFan) &&
|
2010-02-05 13:10:32 -07:00
|
|
|
(nIndices >= 3) &&
|
2021-11-27 07:29:08 -07:00
|
|
|
!(primType == PrimitiveGroupType::TriList && nIndices % 3 != 0))
|
2010-02-05 13:10:32 -07:00
|
|
|
{
|
2010-02-10 13:52:43 -07:00
|
|
|
unsigned int primitiveIndex = 0;
|
2021-11-27 16:03:42 -07:00
|
|
|
Index32 index = 0;
|
2021-11-28 03:30:53 -07:00
|
|
|
Index32 i0 = group.indices[0];
|
|
|
|
Index32 i1 = group.indices[1];
|
|
|
|
Index32 i2 = group.indices[2];
|
2010-02-05 13:10:32 -07:00
|
|
|
|
|
|
|
// Iterate over the triangles in the primitive group
|
|
|
|
do
|
|
|
|
{
|
|
|
|
// Get the triangle vertices v0, v1, and v2
|
2021-11-27 16:03:42 -07:00
|
|
|
float fv[3];
|
|
|
|
std::memcpy(fv, vdata + i0 * stride + posOffset, sizeof(float) * 3);
|
|
|
|
Eigen::Vector3d v0 = Eigen::Map<Eigen::Vector3f>(fv).cast<double>();
|
|
|
|
std::memcpy(fv, vdata + i1 * stride + posOffset, sizeof(float) * 3);
|
|
|
|
Eigen::Vector3d v1 = Eigen::Map<Eigen::Vector3f>(fv).cast<double>();
|
|
|
|
std::memcpy(fv, vdata + i2 * stride + posOffset, sizeof(float) * 3);
|
|
|
|
Eigen::Vector3d v2 = Eigen::Map<Eigen::Vector3f>(fv).cast<double>();
|
2010-02-05 13:10:32 -07:00
|
|
|
|
|
|
|
// Compute the edge vectors e0 and e1, and the normal n
|
2021-11-27 07:29:08 -07:00
|
|
|
Eigen::Vector3d e0 = v1 - v0;
|
|
|
|
Eigen::Vector3d e1 = v2 - v0;
|
|
|
|
Eigen::Vector3d n = e0.cross(e1);
|
2010-02-05 13:10:32 -07:00
|
|
|
|
|
|
|
// c is the cosine of the angle between the ray and triangle normal
|
|
|
|
double c = n.dot(rayDirection);
|
|
|
|
|
|
|
|
// If the ray is parallel to the triangle, it either misses the
|
|
|
|
// triangle completely, or is contained in the triangle's plane.
|
|
|
|
// If it's contained in the plane, we'll still call it a miss.
|
|
|
|
if (c != 0.0)
|
|
|
|
{
|
|
|
|
double t = (n.dot(v0 - rayOrigin)) / c;
|
|
|
|
if (t < closest && t > 0.0)
|
|
|
|
{
|
|
|
|
double m00 = e0.dot(e0);
|
|
|
|
double m01 = e0.dot(e1);
|
|
|
|
double m10 = e1.dot(e0);
|
|
|
|
double m11 = e1.dot(e1);
|
|
|
|
double det = m00 * m11 - m01 * m10;
|
|
|
|
if (det != 0.0)
|
|
|
|
{
|
2021-11-27 07:29:08 -07:00
|
|
|
Eigen::Vector3d p = rayOrigin + rayDirection * t;
|
|
|
|
Eigen::Vector3d q = p - v0;
|
2010-02-05 13:10:32 -07:00
|
|
|
double q0 = e0.dot(q);
|
|
|
|
double q1 = e1.dot(q);
|
|
|
|
double d = 1.0 / det;
|
|
|
|
double s0 = (m11 * q0 - m01 * q1) * d;
|
|
|
|
double s1 = (m00 * q1 - m10 * q0) * d;
|
|
|
|
if (s0 >= 0.0 && s1 >= 0.0 && s0 + s1 <= 1.0)
|
2010-02-10 13:52:43 -07:00
|
|
|
{
|
2010-02-05 13:10:32 -07:00
|
|
|
closest = t;
|
2010-02-10 13:52:43 -07:00
|
|
|
if (result)
|
|
|
|
{
|
2021-11-28 03:30:53 -07:00
|
|
|
result->group = &group;
|
2010-02-10 13:52:43 -07:00
|
|
|
result->primitiveIndex = primitiveIndex;
|
|
|
|
result->distance = closest;
|
|
|
|
}
|
|
|
|
}
|
2010-02-05 13:10:32 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the indices for the next triangle
|
2021-11-27 07:29:08 -07:00
|
|
|
if (primType == PrimitiveGroupType::TriList)
|
2010-02-05 13:10:32 -07:00
|
|
|
{
|
|
|
|
index += 3;
|
|
|
|
if (index < nIndices)
|
|
|
|
{
|
2021-11-28 03:30:53 -07:00
|
|
|
i0 = group.indices[index + 0];
|
|
|
|
i1 = group.indices[index + 1];
|
|
|
|
i2 = group.indices[index + 2];
|
2010-02-05 13:10:32 -07:00
|
|
|
}
|
|
|
|
}
|
2021-11-27 07:29:08 -07:00
|
|
|
else if (primType == PrimitiveGroupType::TriStrip)
|
2010-02-05 13:10:32 -07:00
|
|
|
{
|
|
|
|
index += 1;
|
|
|
|
if (index < nIndices)
|
|
|
|
{
|
|
|
|
i0 = i1;
|
|
|
|
i1 = i2;
|
2021-11-28 03:30:53 -07:00
|
|
|
i2 = group.indices[index];
|
2010-02-05 13:10:32 -07:00
|
|
|
// TODO: alternate orientation of triangles in a strip
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else // primType == TriFan
|
|
|
|
{
|
|
|
|
index += 1;
|
|
|
|
if (index < nIndices)
|
|
|
|
{
|
|
|
|
index += 1;
|
|
|
|
i1 = i2;
|
2021-11-28 03:30:53 -07:00
|
|
|
i2 = group.indices[index];
|
2010-02-05 13:10:32 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-02-10 13:52:43 -07:00
|
|
|
primitiveIndex++;
|
|
|
|
|
2010-02-05 13:10:32 -07:00
|
|
|
} while (index < nIndices);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-02-10 13:52:43 -07:00
|
|
|
return closest != maxDistance;
|
2010-02-05 13:10:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-02-10 13:52:43 -07:00
|
|
|
bool
|
2021-11-27 07:29:08 -07:00
|
|
|
Mesh::pick(const Eigen::Vector3d& rayOrigin, const Eigen::Vector3d& rayDirection, double& distance) const
|
2010-02-10 13:52:43 -07:00
|
|
|
{
|
|
|
|
PickResult result;
|
|
|
|
bool hit = pick(rayOrigin, rayDirection, &result);
|
|
|
|
if (hit)
|
2010-02-05 13:10:32 -07:00
|
|
|
{
|
2010-02-10 13:52:43 -07:00
|
|
|
distance = result.distance;
|
2010-02-05 13:10:32 -07:00
|
|
|
}
|
|
|
|
|
2010-02-10 13:52:43 -07:00
|
|
|
return hit;
|
2010-02-05 13:10:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-27 07:29:08 -07:00
|
|
|
Eigen::AlignedBox<float, 3>
|
2010-02-05 13:10:32 -07:00
|
|
|
Mesh::getBoundingBox() const
|
|
|
|
{
|
2021-11-27 07:29:08 -07:00
|
|
|
Eigen::AlignedBox<float, 3> bbox;
|
2010-02-05 13:10:32 -07:00
|
|
|
|
|
|
|
// Return an empty box if there's no position info
|
2021-11-27 07:29:08 -07:00
|
|
|
if (vertexDesc.getAttribute(VertexAttributeSemantic::Position).format != VertexAttributeFormat::Float3)
|
2010-02-05 13:10:32 -07:00
|
|
|
return bbox;
|
|
|
|
|
2021-11-27 16:03:42 -07:00
|
|
|
const VWord* vdata = vertices.data() + vertexDesc.getAttribute(VertexAttributeSemantic::Position).offsetWords;
|
2010-02-05 13:10:32 -07:00
|
|
|
|
2021-11-27 16:03:42 -07:00
|
|
|
unsigned int stride = vertexDesc.strideBytes / sizeof(VWord);
|
2021-11-27 07:29:08 -07:00
|
|
|
if (vertexDesc.getAttribute(VertexAttributeSemantic::PointSize).format == VertexAttributeFormat::Float1)
|
2010-02-05 13:10:32 -07:00
|
|
|
{
|
|
|
|
// Handle bounding box calculation for point sprites. Unlike other
|
|
|
|
// primitives, point sprite vertices have a non-zero size.
|
2021-11-27 16:03:42 -07:00
|
|
|
int pointSizeOffset = (int) vertexDesc.getAttribute(VertexAttributeSemantic::PointSize).offsetWords -
|
|
|
|
(int) vertexDesc.getAttribute(VertexAttributeSemantic::Position).offsetWords;
|
2018-03-11 07:12:58 -06:00
|
|
|
|
2021-11-27 16:03:42 -07:00
|
|
|
for (unsigned int i = 0; i < nVertices; i++, vdata += stride)
|
2010-02-05 13:10:32 -07:00
|
|
|
{
|
2021-11-27 16:03:42 -07:00
|
|
|
float fv[3];
|
|
|
|
std::memcpy(fv, vdata, sizeof(float) * 3);
|
|
|
|
Eigen::Vector3f center = Eigen::Map<Eigen::Vector3f>(fv);
|
|
|
|
float pointSize;
|
|
|
|
std::memcpy(&pointSize, vdata + pointSizeOffset, sizeof(float));
|
2021-11-27 07:29:08 -07:00
|
|
|
Eigen::Vector3f offsetVec = Eigen::Vector3f::Constant(pointSize);
|
2010-02-05 13:10:32 -07:00
|
|
|
|
2021-11-27 07:29:08 -07:00
|
|
|
Eigen::AlignedBox<float, 3> pointbox(center - offsetVec, center + offsetVec);
|
2010-02-05 13:10:32 -07:00
|
|
|
bbox.extend(pointbox);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-11-27 16:03:42 -07:00
|
|
|
for (unsigned int i = 0; i < nVertices; i++, vdata += stride)
|
|
|
|
{
|
|
|
|
float fv[3];
|
|
|
|
std::memcpy(fv, vdata, sizeof(float) * 3);
|
|
|
|
bbox.extend(Eigen::Map<Eigen::Vector3f>(fv));
|
|
|
|
}
|
2010-02-05 13:10:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return bbox;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2021-11-27 07:29:08 -07:00
|
|
|
Mesh::transform(const Eigen::Vector3f& translation, float scale)
|
2010-02-05 13:10:32 -07:00
|
|
|
{
|
2021-11-27 07:29:08 -07:00
|
|
|
if (vertexDesc.getAttribute(VertexAttributeSemantic::Position).format != VertexAttributeFormat::Float3)
|
2010-02-05 13:10:32 -07:00
|
|
|
return;
|
|
|
|
|
2021-11-27 16:03:42 -07:00
|
|
|
VWord* vdata = vertices.data() + vertexDesc.getAttribute(VertexAttributeSemantic::Position).offsetWords;
|
2010-02-05 13:10:32 -07:00
|
|
|
unsigned int i;
|
|
|
|
|
2021-11-27 16:03:42 -07:00
|
|
|
unsigned int stride = vertexDesc.strideBytes / sizeof(VWord);
|
|
|
|
|
2010-02-05 13:10:32 -07:00
|
|
|
// Scale and translate the vertex positions
|
2021-11-27 16:03:42 -07:00
|
|
|
for (i = 0; i < nVertices; i++, vdata += stride)
|
2010-02-05 13:10:32 -07:00
|
|
|
{
|
2021-11-27 16:03:42 -07:00
|
|
|
float fv[3];
|
|
|
|
std::memcpy(fv, vdata, sizeof(float) * 3);
|
|
|
|
const Eigen::Vector3f tv = (Eigen::Map<Eigen::Vector3f>(fv) + translation) * scale;
|
|
|
|
std::memcpy(vdata, tv.data(), sizeof(float) * 3);
|
2010-02-05 13:10:32 -07:00
|
|
|
}
|
|
|
|
|
2020-12-11 06:38:12 -07:00
|
|
|
// Scale and translate the overriden vertex values
|
|
|
|
for (i = 0; i < getGroupCount(); i++)
|
|
|
|
{
|
|
|
|
PrimitiveGroup* group = getGroup(i);
|
2021-11-27 16:03:42 -07:00
|
|
|
if (group->vertexOverride.empty()) { continue; }
|
|
|
|
VWord* vdataOverride = group->vertexOverride.data();
|
|
|
|
|
|
|
|
const auto& vertexDescOverride = group->vertexDescriptionOverride;
|
|
|
|
unsigned int strideOverride = vertexDescOverride.strideBytes / sizeof(VWord);
|
|
|
|
int positionOffset = vertexDescOverride.getAttribute(VertexAttributeSemantic::Position).offsetWords;
|
|
|
|
int nextPositionOffset = vertexDescOverride.getAttribute(VertexAttributeSemantic::NextPosition).offsetWords;
|
|
|
|
for (unsigned int j = 0; j < group->vertexCountOverride; j++, vdataOverride += strideOverride)
|
2020-12-11 06:38:12 -07:00
|
|
|
{
|
2021-11-27 16:03:42 -07:00
|
|
|
float fv[3];
|
|
|
|
std::memcpy(fv, vdataOverride + positionOffset, sizeof(float) * 3);
|
|
|
|
Eigen::Vector3f tv = (Eigen::Map<Eigen::Vector3f>(fv) + translation) * scale;
|
|
|
|
std::memcpy(vdataOverride + positionOffset, tv.data(), sizeof(float) * 3);
|
|
|
|
|
|
|
|
std::memcpy(fv, vdataOverride + nextPositionOffset, sizeof(float) * 3);
|
|
|
|
tv = (Eigen::Map<Eigen::Vector3f>(fv) + translation) * scale;
|
|
|
|
std::memcpy(vdataOverride + nextPositionOffset, tv.data(), sizeof(float) * 3);
|
2020-12-11 06:38:12 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-02-05 13:10:32 -07:00
|
|
|
// Point sizes need to be scaled as well
|
2021-11-27 07:29:08 -07:00
|
|
|
if (vertexDesc.getAttribute(VertexAttributeSemantic::PointSize).format == VertexAttributeFormat::Float1)
|
2010-02-05 13:10:32 -07:00
|
|
|
{
|
2021-11-27 16:03:42 -07:00
|
|
|
vdata = vertices.data() + vertexDesc.getAttribute(VertexAttributeSemantic::PointSize).offsetWords;
|
|
|
|
for (i = 0; i < nVertices; i++, vdata += stride)
|
|
|
|
{
|
|
|
|
float f;
|
|
|
|
std::memcpy(&f, vdata, sizeof(float));
|
|
|
|
f *= scale;
|
|
|
|
std::memcpy(vdata, &f, sizeof(float));
|
|
|
|
}
|
2010-02-05 13:10:32 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
unsigned int
|
|
|
|
Mesh::getPrimitiveCount() const
|
|
|
|
{
|
|
|
|
unsigned int count = 0;
|
|
|
|
|
2021-11-28 03:30:53 -07:00
|
|
|
for (const auto& group : groups)
|
|
|
|
count += group.getPrimitiveCount();
|
2010-02-05 13:10:32 -07:00
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2022-01-27 13:28:49 -07:00
|
|
|
void
|
|
|
|
Mesh::merge(const Mesh &other)
|
|
|
|
{
|
|
|
|
auto &ti = groups.front().indices;
|
|
|
|
const auto &oi = other.groups.front().indices;
|
|
|
|
|
|
|
|
ti.reserve(ti.size() + oi.size());
|
|
|
|
for (auto i : oi)
|
|
|
|
ti.push_back(i + nVertices);
|
|
|
|
|
|
|
|
vertices.reserve(vertices.size() + other.vertices.size());
|
|
|
|
vertices.insert(vertices.end(), other.vertices.begin(), other.vertices.end());
|
|
|
|
|
|
|
|
nVertices += other.nVertices;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
Mesh::canMerge(const Mesh &other, const std::vector<Material> &materials) const
|
|
|
|
{
|
|
|
|
if (getGroupCount() != 1 || other.getGroupCount() != 1)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
const auto &tg = groups.front();
|
|
|
|
const auto &og = other.groups.front();
|
|
|
|
|
|
|
|
if (tg.vertexCountOverride != 0 || og.vertexCountOverride != 0 || tg.prim != PrimitiveGroupType::TriList)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (std::tie(tg.materialIndex, tg.prim, vertexDesc.strideBytes) !=
|
|
|
|
std::tie(og.materialIndex, og.prim, other.vertexDesc.strideBytes))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!isOpaqueMaterial(materials[tg.materialIndex]) || !isOpaqueMaterial(materials[og.materialIndex]))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
for (auto i = VertexAttributeSemantic::Position;
|
|
|
|
i < VertexAttributeSemantic::SemanticMax;
|
|
|
|
i = static_cast<VertexAttributeSemantic>(1 + static_cast<uint16_t>(i)))
|
|
|
|
{
|
|
|
|
auto &ta = vertexDesc.getAttribute(i);
|
|
|
|
auto &oa = other.vertexDesc.getAttribute(i);
|
|
|
|
if (ta.format != oa.format || ta.offsetWords != oa.offsetWords)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2021-11-27 07:29:08 -07:00
|
|
|
} // end namespace cmod
|