summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/bgfx/src/vertexlayout.cpp
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/bgfx/src/vertexlayout.cpp')
-rw-r--r--3rdparty/bgfx/src/vertexlayout.cpp828
1 files changed, 828 insertions, 0 deletions
diff --git a/3rdparty/bgfx/src/vertexlayout.cpp b/3rdparty/bgfx/src/vertexlayout.cpp
new file mode 100644
index 00000000000..4cebdb4a2b6
--- /dev/null
+++ b/3rdparty/bgfx/src/vertexlayout.cpp
@@ -0,0 +1,828 @@
+/*
+ * Copyright 2011-2022 Branimir Karadzic. All rights reserved.
+ * License: https://github.com/bkaradzic/bgfx/blob/master/LICENSE
+ */
+
+#include <bx/debug.h>
+#include <bx/hash.h>
+#include <bx/readerwriter.h>
+#include <bx/sort.h>
+#include <bx/string.h>
+#include <bx/uint32_t.h>
+
+#include "vertexlayout.h"
+
+namespace bgfx
+{
+ static const uint8_t s_attribTypeSizeD3D9[AttribType::Count][4] =
+ {
+ { 4, 4, 4, 4 }, // Uint8
+ { 4, 4, 4, 4 }, // Uint10
+ { 4, 4, 8, 8 }, // Int16
+ { 4, 4, 8, 8 }, // Half
+ { 4, 8, 12, 16 }, // Float
+ };
+
+ static const uint8_t s_attribTypeSizeD3D1x[AttribType::Count][4] =
+ {
+ { 1, 2, 4, 4 }, // Uint8
+ { 4, 4, 4, 4 }, // Uint10
+ { 2, 4, 8, 8 }, // Int16
+ { 2, 4, 8, 8 }, // Half
+ { 4, 8, 12, 16 }, // Float
+ };
+
+ static const uint8_t s_attribTypeSizeGl[AttribType::Count][4] =
+ {
+ { 1, 2, 4, 4 }, // Uint8
+ { 4, 4, 4, 4 }, // Uint10
+ { 2, 4, 6, 8 }, // Int16
+ { 2, 4, 6, 8 }, // Half
+ { 4, 8, 12, 16 }, // Float
+ };
+
+ static const uint8_t (*s_attribTypeSize[])[AttribType::Count][4] =
+ {
+ &s_attribTypeSizeD3D9, // Noop
+ &s_attribTypeSizeD3D1x, // Agc
+ &s_attribTypeSizeD3D9, // Direct3D9
+ &s_attribTypeSizeD3D1x, // Direct3D11
+ &s_attribTypeSizeD3D1x, // Direct3D12
+ &s_attribTypeSizeD3D1x, // Gnm
+ &s_attribTypeSizeGl, // Metal
+ &s_attribTypeSizeGl, // Nvn
+ &s_attribTypeSizeGl, // OpenGLES
+ &s_attribTypeSizeGl, // OpenGL
+ &s_attribTypeSizeD3D1x, // Vulkan
+ &s_attribTypeSizeD3D1x, // WebGPU
+ &s_attribTypeSizeD3D9, // Count
+ };
+ BX_STATIC_ASSERT(BX_COUNTOF(s_attribTypeSize) == RendererType::Count+1);
+
+ void initAttribTypeSizeTable(RendererType::Enum _type)
+ {
+ s_attribTypeSize[0] = s_attribTypeSize[_type];
+ s_attribTypeSize[RendererType::Count] = s_attribTypeSize[_type];
+ }
+
+ VertexLayout::VertexLayout()
+ : m_stride(0)
+ {
+ // BK - struct need to have ctor to qualify as non-POD data.
+ // Need this to catch programming errors when serializing struct.
+ }
+
+ VertexLayout& VertexLayout::begin(RendererType::Enum _renderer)
+ {
+ m_hash = _renderer; // use hash to store renderer type while building VertexLayout.
+ m_stride = 0;
+ bx::memSet(m_attributes, 0xff, sizeof(m_attributes) );
+ bx::memSet(m_offset, 0, sizeof(m_offset) );
+
+ return *this;
+ }
+
+ void VertexLayout::end()
+ {
+ bx::HashMurmur2A murmur;
+ murmur.begin();
+ murmur.add(m_attributes, sizeof(m_attributes) );
+ murmur.add(m_offset, sizeof(m_offset) );
+ murmur.add(m_stride);
+ m_hash = murmur.end();
+ }
+
+ VertexLayout& VertexLayout::add(Attrib::Enum _attrib, uint8_t _num, AttribType::Enum _type, bool _normalized, bool _asInt)
+ {
+ const uint16_t encodedNorm = (_normalized&1)<<7;
+ const uint16_t encodedType = (_type&7)<<3;
+ const uint16_t encodedNum = (_num-1)&3;
+ const uint16_t encodeAsInt = (_asInt&(!!"\x1\x1\x1\x0\x0"[_type]) )<<8;
+ m_attributes[_attrib] = encodedNorm|encodedType|encodedNum|encodeAsInt;
+
+ m_offset[_attrib] = m_stride;
+ m_stride += (*s_attribTypeSize[m_hash])[_type][_num-1];
+
+ return *this;
+ }
+
+ VertexLayout& VertexLayout::skip(uint8_t _num)
+ {
+ m_stride += _num;
+
+ return *this;
+ }
+
+ void VertexLayout::decode(Attrib::Enum _attrib, uint8_t& _num, AttribType::Enum& _type, bool& _normalized, bool& _asInt) const
+ {
+ uint16_t val = m_attributes[_attrib];
+ _num = (val&3)+1;
+ _type = AttribType::Enum( (val>>3)&7);
+ _normalized = !!(val&(1<<7) );
+ _asInt = !!(val&(1<<8) );
+ }
+
+ static const bool s_attribTypeIsFloat[] =
+ {
+ false, // Uint8
+ false, // Uint10
+ false, // Int16
+ true, // Half
+ true, // Float
+ };
+ BX_STATIC_ASSERT(BX_COUNTOF(s_attribTypeIsFloat) == AttribType::Count);
+
+ bool isFloat(AttribType::Enum _type)
+ {
+ return s_attribTypeIsFloat[_type];
+ }
+
+ static const char* s_attrName[] =
+ {
+ "P", "Attrib::Position",
+ "N", "Attrib::Normal",
+ "T", "Attrib::Tangent",
+ "B", "Attrib::Bitangent",
+ "C0", "Attrib::Color0",
+ "C1", "Attrib::Color1",
+ "C2", "Attrib::Color2",
+ "C3", "Attrib::Color3",
+ "I", "Attrib::Indices",
+ "W", "Attrib::Weights",
+ "T0", "Attrib::TexCoord0",
+ "T1", "Attrib::TexCoord1",
+ "T2", "Attrib::TexCoord2",
+ "T3", "Attrib::TexCoord3",
+ "T4", "Attrib::TexCoord4",
+ "T5", "Attrib::TexCoord5",
+ "T6", "Attrib::TexCoord6",
+ "T7", "Attrib::TexCoord7",
+ };
+ BX_STATIC_ASSERT(BX_COUNTOF(s_attrName) == Attrib::Count*2);
+
+ const char* getAttribNameShort(Attrib::Enum _attr)
+ {
+ return s_attrName[_attr*2+0];
+ }
+
+ const char* getAttribName(Attrib::Enum _attr)
+ {
+ return s_attrName[_attr*2+1];
+ }
+
+ struct AttribToId
+ {
+ Attrib::Enum attr;
+ uint16_t id;
+ };
+
+ static AttribToId s_attribToId[] =
+ {
+ // NOTICE:
+ // Attrib must be in order how it appears in Attrib::Enum! id is
+ // unique and should not be changed if new Attribs are added.
+ { Attrib::Position, 0x0001 },
+ { Attrib::Normal, 0x0002 },
+ { Attrib::Tangent, 0x0003 },
+ { Attrib::Bitangent, 0x0004 },
+ { Attrib::Color0, 0x0005 },
+ { Attrib::Color1, 0x0006 },
+ { Attrib::Color2, 0x0018 },
+ { Attrib::Color3, 0x0019 },
+ { Attrib::Indices, 0x000e },
+ { Attrib::Weight, 0x000f },
+ { Attrib::TexCoord0, 0x0010 },
+ { Attrib::TexCoord1, 0x0011 },
+ { Attrib::TexCoord2, 0x0012 },
+ { Attrib::TexCoord3, 0x0013 },
+ { Attrib::TexCoord4, 0x0014 },
+ { Attrib::TexCoord5, 0x0015 },
+ { Attrib::TexCoord6, 0x0016 },
+ { Attrib::TexCoord7, 0x0017 },
+ };
+ BX_STATIC_ASSERT(BX_COUNTOF(s_attribToId) == Attrib::Count);
+
+ Attrib::Enum idToAttrib(uint16_t id)
+ {
+ for (uint32_t ii = 0; ii < BX_COUNTOF(s_attribToId); ++ii)
+ {
+ if (s_attribToId[ii].id == id)
+ {
+ return s_attribToId[ii].attr;
+ }
+ }
+
+ return Attrib::Count;
+ }
+
+ uint16_t attribToId(Attrib::Enum _attr)
+ {
+ return s_attribToId[_attr].id;
+ }
+
+ struct AttribTypeToId
+ {
+ AttribType::Enum type;
+ uint16_t id;
+ };
+
+ static AttribTypeToId s_attribTypeToId[] =
+ {
+ // NOTICE:
+ // AttribType must be in order how it appears in AttribType::Enum!
+ // id is unique and should not be changed if new AttribTypes are
+ // added.
+ { AttribType::Uint8, 0x0001 },
+ { AttribType::Uint10, 0x0005 },
+ { AttribType::Int16, 0x0002 },
+ { AttribType::Half, 0x0003 },
+ { AttribType::Float, 0x0004 },
+ };
+ BX_STATIC_ASSERT(BX_COUNTOF(s_attribTypeToId) == AttribType::Count);
+
+ AttribType::Enum idToAttribType(uint16_t id)
+ {
+ for (uint32_t ii = 0; ii < BX_COUNTOF(s_attribTypeToId); ++ii)
+ {
+ if (s_attribTypeToId[ii].id == id)
+ {
+ return s_attribTypeToId[ii].type;
+ }
+ }
+
+ return AttribType::Count;
+ }
+
+ uint16_t attribTypeToId(AttribType::Enum _attr)
+ {
+ return s_attribTypeToId[_attr].id;
+ }
+
+ int32_t write(bx::WriterI* _writer, const VertexLayout& _layout, bx::Error* _err)
+ {
+ BX_ERROR_SCOPE(_err);
+
+ int32_t total = 0;
+ uint8_t numAttrs = 0;
+
+ for (uint32_t attr = 0; attr < Attrib::Count; ++attr)
+ {
+ numAttrs += UINT16_MAX == _layout.m_attributes[attr] ? 0 : 1;
+ }
+
+ total += bx::write(_writer, numAttrs, _err);
+ total += bx::write(_writer, _layout.m_stride, _err);
+
+ for (uint32_t attr = 0; attr < Attrib::Count; ++attr)
+ {
+ if (UINT16_MAX != _layout.m_attributes[attr])
+ {
+ uint8_t num;
+ AttribType::Enum type;
+ bool normalized;
+ bool asInt;
+ _layout.decode(Attrib::Enum(attr), num, type, normalized, asInt);
+ total += bx::write(_writer, _layout.m_offset[attr], _err);
+ total += bx::write(_writer, s_attribToId[attr].id, _err);
+ total += bx::write(_writer, num, _err);
+ total += bx::write(_writer, s_attribTypeToId[type].id, _err);
+ total += bx::write(_writer, normalized, _err);
+ total += bx::write(_writer, asInt, _err);
+ }
+ }
+
+ return total;
+ }
+
+ int32_t read(bx::ReaderI* _reader, VertexLayout& _layout, bx::Error* _err)
+ {
+ BX_ERROR_SCOPE(_err);
+
+ int32_t total = 0;
+
+ uint8_t numAttrs;
+ total += bx::read(_reader, numAttrs, _err);
+
+ uint16_t stride;
+ total += bx::read(_reader, stride, _err);
+
+ if (!_err->isOk() )
+ {
+ return total;
+ }
+
+ _layout.begin();
+
+ for (uint32_t ii = 0; ii < numAttrs; ++ii)
+ {
+ uint16_t offset;
+ total += bx::read(_reader, offset, _err);
+
+ uint16_t attribId = 0;
+ total += bx::read(_reader, attribId, _err);
+
+ uint8_t num;
+ total += bx::read(_reader, num, _err);
+
+ uint16_t attribTypeId;
+ total += bx::read(_reader, attribTypeId, _err);
+
+ bool normalized;
+ total += bx::read(_reader, normalized, _err);
+
+ bool asInt;
+ total += bx::read(_reader, asInt, _err);
+
+ if (!_err->isOk() )
+ {
+ return total;
+ }
+
+ Attrib::Enum attr = idToAttrib(attribId);
+ AttribType::Enum type = idToAttribType(attribTypeId);
+ if (Attrib::Count != attr
+ && AttribType::Count != type)
+ {
+ _layout.add(attr, num, type, normalized, asInt);
+ _layout.m_offset[attr] = offset;
+ }
+ }
+
+ _layout.end();
+ _layout.m_stride = stride;
+
+ return total;
+ }
+
+ void vertexPack(const float _input[4], bool _inputNormalized, Attrib::Enum _attr, const VertexLayout& _layout, void* _data, uint32_t _index)
+ {
+ if (!_layout.has(_attr) )
+ {
+ return;
+ }
+
+ uint32_t stride = _layout.getStride();
+ uint8_t* data = (uint8_t*)_data + _index*stride + _layout.getOffset(_attr);
+
+ uint8_t num;
+ AttribType::Enum type;
+ bool normalized;
+ bool asInt;
+ _layout.decode(_attr, num, type, normalized, asInt);
+
+ switch (type)
+ {
+ default:
+ case AttribType::Uint8:
+ {
+ uint8_t* packed = (uint8_t*)data;
+ if (_inputNormalized)
+ {
+ if (asInt)
+ {
+ switch (num)
+ {
+ default: *packed++ = uint8_t(*_input++ * 127.0f + 128.0f); BX_FALLTHROUGH;
+ case 3: *packed++ = uint8_t(*_input++ * 127.0f + 128.0f); BX_FALLTHROUGH;
+ case 2: *packed++ = uint8_t(*_input++ * 127.0f + 128.0f); BX_FALLTHROUGH;
+ case 1: *packed++ = uint8_t(*_input++ * 127.0f + 128.0f);
+ }
+ }
+ else
+ {
+ switch (num)
+ {
+ default: *packed++ = uint8_t(*_input++ * 255.0f); BX_FALLTHROUGH;
+ case 3: *packed++ = uint8_t(*_input++ * 255.0f); BX_FALLTHROUGH;
+ case 2: *packed++ = uint8_t(*_input++ * 255.0f); BX_FALLTHROUGH;
+ case 1: *packed++ = uint8_t(*_input++ * 255.0f);
+ }
+ }
+ }
+ else
+ {
+ switch (num)
+ {
+ default: *packed++ = uint8_t(*_input++); BX_FALLTHROUGH;
+ case 3: *packed++ = uint8_t(*_input++); BX_FALLTHROUGH;
+ case 2: *packed++ = uint8_t(*_input++); BX_FALLTHROUGH;
+ case 1: *packed++ = uint8_t(*_input++);
+ }
+ }
+ }
+ break;
+
+ case AttribType::Uint10:
+ {
+ uint32_t packed = 0;
+ if (_inputNormalized)
+ {
+ if (asInt)
+ {
+ switch (num)
+ {
+ default: BX_FALLTHROUGH;
+ case 3: packed |= uint32_t(*_input++ * 511.0f + 512.0f); BX_FALLTHROUGH;
+ case 2: packed <<= 10; packed |= uint32_t(*_input++ * 511.0f + 512.0f); BX_FALLTHROUGH;
+ case 1: packed <<= 10; packed |= uint32_t(*_input++ * 511.0f + 512.0f);
+ }
+ }
+ else
+ {
+ switch (num)
+ {
+ default: BX_FALLTHROUGH;
+ case 3: packed |= uint32_t(*_input++ * 1023.0f); BX_FALLTHROUGH;
+ case 2: packed <<= 10; packed |= uint32_t(*_input++ * 1023.0f); BX_FALLTHROUGH;
+ case 1: packed <<= 10; packed |= uint32_t(*_input++ * 1023.0f);
+ }
+ }
+ }
+ else
+ {
+ switch (num)
+ {
+ default: BX_FALLTHROUGH;
+ case 3: packed |= uint32_t(*_input++); BX_FALLTHROUGH;
+ case 2: packed <<= 10; packed |= uint32_t(*_input++); BX_FALLTHROUGH;
+ case 1: packed <<= 10; packed |= uint32_t(*_input++);
+ }
+ }
+ *(uint32_t*)data = packed;
+ }
+ break;
+
+ case AttribType::Int16:
+ {
+ int16_t* packed = (int16_t*)data;
+ if (_inputNormalized)
+ {
+ if (asInt)
+ {
+ switch (num)
+ {
+ default: *packed++ = int16_t(*_input++ * 32767.0f); BX_FALLTHROUGH;
+ case 3: *packed++ = int16_t(*_input++ * 32767.0f); BX_FALLTHROUGH;
+ case 2: *packed++ = int16_t(*_input++ * 32767.0f); BX_FALLTHROUGH;
+ case 1: *packed++ = int16_t(*_input++ * 32767.0f);
+ }
+ }
+ else
+ {
+ switch (num)
+ {
+ default: *packed++ = int16_t(*_input++ * 65535.0f - 32768.0f); BX_FALLTHROUGH;
+ case 3: *packed++ = int16_t(*_input++ * 65535.0f - 32768.0f); BX_FALLTHROUGH;
+ case 2: *packed++ = int16_t(*_input++ * 65535.0f - 32768.0f); BX_FALLTHROUGH;
+ case 1: *packed++ = int16_t(*_input++ * 65535.0f - 32768.0f);
+ }
+ }
+ }
+ else
+ {
+ switch (num)
+ {
+ default: *packed++ = int16_t(*_input++); BX_FALLTHROUGH;
+ case 3: *packed++ = int16_t(*_input++); BX_FALLTHROUGH;
+ case 2: *packed++ = int16_t(*_input++); BX_FALLTHROUGH;
+ case 1: *packed++ = int16_t(*_input++);
+ }
+ }
+ }
+ break;
+
+ case AttribType::Half:
+ {
+ uint16_t* packed = (uint16_t*)data;
+ switch (num)
+ {
+ default: *packed++ = bx::halfFromFloat(*_input++); BX_FALLTHROUGH;
+ case 3: *packed++ = bx::halfFromFloat(*_input++); BX_FALLTHROUGH;
+ case 2: *packed++ = bx::halfFromFloat(*_input++); BX_FALLTHROUGH;
+ case 1: *packed++ = bx::halfFromFloat(*_input++);
+ }
+ }
+ break;
+
+ case AttribType::Float:
+ bx::memCopy(data, _input, num*sizeof(float) );
+ break;
+ }
+ }
+
+ void vertexUnpack(float _output[4], Attrib::Enum _attr, const VertexLayout& _layout, const void* _data, uint32_t _index)
+ {
+ if (!_layout.has(_attr) )
+ {
+ bx::memSet(_output, 0, 4*sizeof(float) );
+ return;
+ }
+
+ uint32_t stride = _layout.getStride();
+ uint8_t* data = (uint8_t*)_data + _index*stride + _layout.getOffset(_attr);
+
+ uint8_t num;
+ AttribType::Enum type;
+ bool normalized;
+ bool asInt;
+ _layout.decode(_attr, num, type, normalized, asInt);
+
+ switch (type)
+ {
+ default:
+ case AttribType::Uint8:
+ {
+ uint8_t* packed = (uint8_t*)data;
+ if (asInt)
+ {
+ switch (num)
+ {
+ default: *_output++ = (float(*packed++) - 128.0f)*1.0f/127.0f; BX_FALLTHROUGH;
+ case 3: *_output++ = (float(*packed++) - 128.0f)*1.0f/127.0f; BX_FALLTHROUGH;
+ case 2: *_output++ = (float(*packed++) - 128.0f)*1.0f/127.0f; BX_FALLTHROUGH;
+ case 1: *_output++ = (float(*packed++) - 128.0f)*1.0f/127.0f;
+ }
+ }
+ else
+ {
+ switch (num)
+ {
+ default: *_output++ = float(*packed++)*1.0f/255.0f; BX_FALLTHROUGH;
+ case 3: *_output++ = float(*packed++)*1.0f/255.0f; BX_FALLTHROUGH;
+ case 2: *_output++ = float(*packed++)*1.0f/255.0f; BX_FALLTHROUGH;
+ case 1: *_output++ = float(*packed++)*1.0f/255.0f;
+ }
+ }
+ }
+ break;
+
+ case AttribType::Uint10:
+ {
+ uint32_t packed = *(uint32_t*)data;
+ if (asInt)
+ {
+ switch (num)
+ {
+ default: BX_FALLTHROUGH;
+ case 3: *_output++ = (float(packed & 0x3ff) - 512.0f)*1.0f/511.0f; packed >>= 10; BX_FALLTHROUGH;
+ case 2: *_output++ = (float(packed & 0x3ff) - 512.0f)*1.0f/511.0f; packed >>= 10; BX_FALLTHROUGH;
+ case 1: *_output++ = (float(packed & 0x3ff) - 512.0f)*1.0f/511.0f;
+ }
+ }
+ else
+ {
+ switch (num)
+ {
+ default: BX_FALLTHROUGH;
+ case 3: *_output++ = float(packed & 0x3ff)*1.0f/1023.0f; packed >>= 10; BX_FALLTHROUGH;
+ case 2: *_output++ = float(packed & 0x3ff)*1.0f/1023.0f; packed >>= 10; BX_FALLTHROUGH;
+ case 1: *_output++ = float(packed & 0x3ff)*1.0f/1023.0f;
+ }
+ }
+ }
+ break;
+
+ case AttribType::Int16:
+ {
+ int16_t* packed = (int16_t*)data;
+ if (asInt)
+ {
+ switch (num)
+ {
+ default: *_output++ = float(*packed++)*1.0f/32767.0f; BX_FALLTHROUGH;
+ case 3: *_output++ = float(*packed++)*1.0f/32767.0f; BX_FALLTHROUGH;
+ case 2: *_output++ = float(*packed++)*1.0f/32767.0f; BX_FALLTHROUGH;
+ case 1: *_output++ = float(*packed++)*1.0f/32767.0f;
+ }
+ }
+ else
+ {
+ switch (num)
+ {
+ default: *_output++ = (float(*packed++) + 32768.0f)*1.0f/65535.0f; BX_FALLTHROUGH;
+ case 3: *_output++ = (float(*packed++) + 32768.0f)*1.0f/65535.0f; BX_FALLTHROUGH;
+ case 2: *_output++ = (float(*packed++) + 32768.0f)*1.0f/65535.0f; BX_FALLTHROUGH;
+ case 1: *_output++ = (float(*packed++) + 32768.0f)*1.0f/65535.0f;
+ }
+ }
+ }
+ break;
+
+ case AttribType::Half:
+ {
+ uint16_t* packed = (uint16_t*)data;
+ switch (num)
+ {
+ default: *_output++ = bx::halfToFloat(*packed++); BX_FALLTHROUGH;
+ case 3: *_output++ = bx::halfToFloat(*packed++); BX_FALLTHROUGH;
+ case 2: *_output++ = bx::halfToFloat(*packed++); BX_FALLTHROUGH;
+ case 1: *_output++ = bx::halfToFloat(*packed++);
+ }
+ }
+ break;
+
+ case AttribType::Float:
+ bx::memCopy(_output, data, num*sizeof(float) );
+ _output += num;
+ break;
+ }
+
+ switch (num)
+ {
+ case 1: *_output++ = 0.0f; BX_FALLTHROUGH;
+ case 2: *_output++ = 0.0f; BX_FALLTHROUGH;
+ case 3: *_output++ = 0.0f; BX_FALLTHROUGH;
+ default: break;
+ }
+ }
+
+ void vertexConvert(const VertexLayout& _destLayout, void* _destData, const VertexLayout& _srcLayout, const void* _srcData, uint32_t _num)
+ {
+ if (_destLayout.m_hash == _srcLayout.m_hash)
+ {
+ bx::memCopy(_destData, _srcData, _srcLayout.getSize(_num) );
+ return;
+ }
+
+ struct ConvertOp
+ {
+ Attrib::Enum attr;
+ uint32_t src;
+ uint32_t dest;
+ uint32_t size;
+ };
+
+ ConvertOp convertOp[Attrib::Count];
+ uint32_t numOps = 0;
+
+ const uint8_t* src = (const uint8_t*)_srcData;
+ uint32_t srcStride = _srcLayout.getStride();
+
+ uint8_t* dest = (uint8_t*)_destData;
+ uint32_t destStride = _destLayout.getStride();
+
+ for (uint32_t ii = 0; ii < Attrib::Count; ++ii)
+ {
+ Attrib::Enum attr = (Attrib::Enum)ii;
+
+ if (_destLayout.has(attr) )
+ {
+ ConvertOp& cop = convertOp[numOps];
+ cop.attr = attr;
+ cop.dest = _destLayout.getOffset(attr);
+
+ uint8_t num;
+ AttribType::Enum type;
+ bool normalized;
+ bool asInt;
+ _destLayout.decode(attr, num, type, normalized, asInt);
+ cop.size = (*s_attribTypeSize[0])[type][num-1];
+
+ if (_srcLayout.has(attr) )
+ {
+ cop.src = _srcLayout.getOffset(attr);
+
+ if (_destLayout.m_attributes[attr] == _srcLayout.m_attributes[attr])
+ {
+ bx::memCopy(dest + cop.dest, destStride, src + cop.src, srcStride, cop.size, _num);
+ }
+ else
+ {
+ ++numOps;
+ }
+ }
+ else
+ {
+ bx::memSet(dest + cop.dest, destStride, 0, cop.size, _num);
+ }
+ }
+ }
+
+ if (0 < numOps)
+ {
+ float unpacked[4];
+
+ for (uint32_t ii = 0; ii < _num; ++ii)
+ {
+ for (uint32_t jj = 0; jj < numOps; ++jj)
+ {
+ const ConvertOp& cop = convertOp[jj];
+ vertexUnpack(unpacked, cop.attr, _srcLayout, src);
+ vertexPack(unpacked, true, cop.attr, _destLayout, dest);
+ }
+
+ src += srcStride;
+ dest += destStride;
+ }
+ }
+ }
+
+ inline float sqLength(const float _a[3], const float _b[3])
+ {
+ const float xx = _a[0] - _b[0];
+ const float yy = _a[1] - _b[1];
+ const float zz = _a[2] - _b[2];
+ return xx*xx + yy*yy + zz*zz;
+ }
+
+ template<typename IndexT>
+ static IndexT weldVerticesRef(IndexT* _output, const VertexLayout& _layout, const void* _data, uint32_t _num, float _epsilon)
+ {
+ // Brute force slow vertex welding...
+ const float epsilonSq = _epsilon*_epsilon;
+
+ uint32_t numVertices = 0;
+ bx::memSet(_output, 0xff, _num*sizeof(IndexT) );
+
+ for (uint32_t ii = 0; ii < _num; ++ii)
+ {
+ if (IndexT(-1) != _output[ii])
+ {
+ continue;
+ }
+
+ _output[ii] = (IndexT)ii;
+ ++numVertices;
+
+ float pos[4];
+ vertexUnpack(pos, Attrib::Position, _layout, _data, ii);
+
+ for (uint32_t jj = 0; jj < _num; ++jj)
+ {
+ if (IndexT(-1) != _output[jj])
+ {
+ continue;
+ }
+
+ float test[4];
+ vertexUnpack(test, Attrib::Position, _layout, _data, jj);
+
+ if (sqLength(test, pos) < epsilonSq)
+ {
+ _output[jj] = IndexT(ii);
+ }
+ }
+ }
+
+ return IndexT(numVertices);
+ }
+
+ template<typename IndexT>
+ static IndexT weldVertices(IndexT* _output, const VertexLayout& _layout, const void* _data, uint32_t _num, float _epsilon, bx::AllocatorI* _allocator)
+ {
+ const uint32_t hashSize = bx::uint32_nextpow2(_num);
+ const uint32_t hashMask = hashSize-1;
+ const float epsilonSq = _epsilon*_epsilon;
+
+ uint32_t numVertices = 0;
+
+ const uint32_t size = sizeof(IndexT)*(hashSize + _num);
+ IndexT* hashTable = (IndexT*)BX_ALLOC(_allocator, size);
+ bx::memSet(hashTable, 0xff, size);
+
+ IndexT* next = hashTable + hashSize;
+
+ for (uint32_t ii = 0; ii < _num; ++ii)
+ {
+ float pos[4];
+ vertexUnpack(pos, Attrib::Position, _layout, _data, ii);
+ uint32_t hashValue = bx::hash<bx::HashMurmur2A>(pos, 3*sizeof(float) ) & hashMask;
+
+ IndexT offset = hashTable[hashValue];
+ for (; IndexT(-1) != offset; offset = next[offset])
+ {
+ float test[4];
+ vertexUnpack(test, Attrib::Position, _layout, _data, _output[offset]);
+
+ if (sqLength(test, pos) < epsilonSq)
+ {
+ _output[ii] = _output[offset];
+ break;
+ }
+ }
+
+ if (IndexT(-1) == offset)
+ {
+ _output[ii] = IndexT(ii);
+ next[ii] = hashTable[hashValue];
+ hashTable[hashValue] = IndexT(ii);
+ numVertices++;
+ }
+ }
+
+ BX_FREE(_allocator, hashTable);
+
+ return IndexT(numVertices);
+ }
+
+ uint32_t weldVertices(void* _output, const VertexLayout& _layout, const void* _data, uint32_t _num, bool _index32, float _epsilon, bx::AllocatorI* _allocator)
+ {
+ if (_index32)
+ {
+ return weldVertices( (uint32_t*)_output, _layout, _data, _num, _epsilon, _allocator);
+ }
+
+ return weldVertices( (uint16_t*)_output, _layout, _data, _num, _epsilon, _allocator);
+ }
+
+} // namespace bgfx