Refactor VertexOffsets
- Extract from Mesh - Rename to VertexAttributes - Move stride on top
This commit is contained in:
parent
ebee8f36c6
commit
0e2ffbee9d
7 changed files with 105 additions and 77 deletions
|
@ -195,6 +195,7 @@ set(GRAPHICS_HEADERS
|
|||
src/engine/graphics/textureutil.h
|
||||
src/engine/graphics/textutil.h
|
||||
src/engine/graphics/types.h
|
||||
src/engine/graphics/vertexattributes.h
|
||||
src/engine/graphics/walkmesh/bwmreader.h
|
||||
src/engine/graphics/walkmesh/walkmesh.h
|
||||
src/engine/graphics/walkmesh/walkmeshes.h
|
||||
|
|
|
@ -30,11 +30,11 @@ namespace reone {
|
|||
|
||||
namespace graphics {
|
||||
|
||||
Mesh::Mesh(int vertexCount, vector<float> vertices, vector<uint16_t> indices, VertexOffsets offsets, DrawMode mode) :
|
||||
Mesh::Mesh(int vertexCount, vector<float> vertices, vector<uint16_t> indices, VertexAttributes attributes, DrawMode mode) :
|
||||
_vertexCount(vertexCount),
|
||||
_vertices(move(vertices)),
|
||||
_indices(move(indices)),
|
||||
_offsets(move(offsets)),
|
||||
_attributes(move(attributes)),
|
||||
_mode(mode) {
|
||||
}
|
||||
|
||||
|
@ -51,37 +51,37 @@ void Mesh::init() {
|
|||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ibo);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, _indices.size() * sizeof(uint16_t), &_indices[0], GL_STATIC_DRAW);
|
||||
|
||||
if (_offsets.vertexCoords != -1) {
|
||||
if (_attributes.offCoords != -1) {
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, _offsets.stride, reinterpret_cast<void *>(_offsets.vertexCoords));
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, _attributes.stride, reinterpret_cast<void *>(_attributes.offCoords));
|
||||
}
|
||||
if (_offsets.normals != -1) {
|
||||
if (_attributes.offNormals != -1) {
|
||||
glEnableVertexAttribArray(1);
|
||||
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, _offsets.stride, reinterpret_cast<void *>(_offsets.normals));
|
||||
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, _attributes.stride, reinterpret_cast<void *>(_attributes.offNormals));
|
||||
}
|
||||
if (_offsets.texCoords1 != -1) {
|
||||
if (_attributes.offTexCoords1 != -1) {
|
||||
glEnableVertexAttribArray(2);
|
||||
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, _offsets.stride, reinterpret_cast<void *>(_offsets.texCoords1));
|
||||
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, _attributes.stride, reinterpret_cast<void *>(_attributes.offTexCoords1));
|
||||
}
|
||||
if (_offsets.texCoords2 != -1) {
|
||||
if (_attributes.offTexCoords2 != -1) {
|
||||
glEnableVertexAttribArray(3);
|
||||
glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, _offsets.stride, reinterpret_cast<void *>(_offsets.texCoords2));
|
||||
glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, _attributes.stride, reinterpret_cast<void *>(_attributes.offTexCoords2));
|
||||
}
|
||||
if (_offsets.tangents != -1) {
|
||||
if (_attributes.offTangents != -1) {
|
||||
glEnableVertexAttribArray(4);
|
||||
glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, _offsets.stride, reinterpret_cast<void *>(_offsets.tangents));
|
||||
glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, _attributes.stride, reinterpret_cast<void *>(_attributes.offTangents));
|
||||
}
|
||||
if (_offsets.bitangents != -1) {
|
||||
if (_attributes.offBitangents != -1) {
|
||||
glEnableVertexAttribArray(5);
|
||||
glVertexAttribPointer(5, 3, GL_FLOAT, GL_FALSE, _offsets.stride, reinterpret_cast<void *>(_offsets.bitangents));
|
||||
glVertexAttribPointer(5, 3, GL_FLOAT, GL_FALSE, _attributes.stride, reinterpret_cast<void *>(_attributes.offBitangents));
|
||||
}
|
||||
if (_offsets.boneWeights != -1) {
|
||||
if (_attributes.offBoneWeights != -1) {
|
||||
glEnableVertexAttribArray(6);
|
||||
glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, _offsets.stride, reinterpret_cast<void *>(_offsets.boneWeights));
|
||||
glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, _attributes.stride, reinterpret_cast<void *>(_attributes.offBoneWeights));
|
||||
}
|
||||
if (_offsets.boneIndices != -1) {
|
||||
if (_attributes.offBoneIndices != -1) {
|
||||
glEnableVertexAttribArray(7);
|
||||
glVertexAttribPointer(7, 4, GL_FLOAT, GL_FALSE, _offsets.stride, reinterpret_cast<void *>(_offsets.boneIndices));
|
||||
glVertexAttribPointer(7, 4, GL_FLOAT, GL_FALSE, _attributes.stride, reinterpret_cast<void *>(_attributes.offBoneIndices));
|
||||
}
|
||||
|
||||
glBindVertexArray(0);
|
||||
|
@ -126,11 +126,11 @@ void Mesh::drawInstanced(int count) {
|
|||
void Mesh::computeAABB() {
|
||||
_aabb.reset();
|
||||
|
||||
int stride = _offsets.stride;
|
||||
int stride = _attributes.stride;
|
||||
if (stride == 0) {
|
||||
stride = 3 * sizeof(float);
|
||||
}
|
||||
auto vertCoords = reinterpret_cast<uint8_t *>(&_vertices[0]) + _offsets.vertexCoords;
|
||||
auto vertCoords = reinterpret_cast<uint8_t *>(&_vertices[0]) + _attributes.offCoords;
|
||||
|
||||
for (size_t i = 0; i < _vertexCount; ++i) {
|
||||
_aabb.expand(glm::make_vec3(reinterpret_cast<const float *>(vertCoords)));
|
||||
|
@ -142,12 +142,12 @@ glm::vec2 Mesh::getFaceCenterUV(int faceIdx) const {
|
|||
if (faceIdx < 0 || faceIdx >= _indices.size() / 3) {
|
||||
throw out_of_range("faceIdx out of range");
|
||||
}
|
||||
if (_offsets.texCoords1 == -1) return glm::vec2(0.0f);
|
||||
if (_attributes.offTexCoords1 == -1) return glm::vec2(0.0f);
|
||||
|
||||
glm::vec2 result(0.0f);
|
||||
const uint16_t *indices = &_indices[3 * faceIdx];
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
const float *uv = &_vertices[(indices[i] * _offsets.stride + _offsets.texCoords1) / sizeof(float)];
|
||||
const float *uv = &_vertices[(indices[i] * _attributes.stride + _attributes.offTexCoords1) / sizeof(float)];
|
||||
result.x += uv[0];
|
||||
result.y += uv[1];
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <boost/noncopyable.hpp>
|
||||
|
||||
#include "aabb.h"
|
||||
#include "vertexattributes.h"
|
||||
|
||||
namespace reone {
|
||||
|
||||
|
@ -42,19 +43,7 @@ public:
|
|||
TriangleStrip
|
||||
};
|
||||
|
||||
struct VertexOffsets {
|
||||
int vertexCoords { 0 };
|
||||
int normals { -1 };
|
||||
int texCoords1 { -1 };
|
||||
int texCoords2 { -1 };
|
||||
int tangents { -1 };
|
||||
int bitangents { -1 };
|
||||
int boneWeights { -1 };
|
||||
int boneIndices { -1 };
|
||||
int stride { 0 };
|
||||
};
|
||||
|
||||
Mesh(int vertexCount, std::vector<float> vertices, std::vector<uint16_t> indices, VertexOffsets offsets, DrawMode mode = DrawMode::Triangles);
|
||||
Mesh(int vertexCount, std::vector<float> vertices, std::vector<uint16_t> indices, VertexAttributes attributes, DrawMode mode = DrawMode::Triangles);
|
||||
~Mesh();
|
||||
|
||||
void init();
|
||||
|
@ -74,14 +63,14 @@ public:
|
|||
|
||||
const std::vector<float> &vertices() const { return _vertices; }
|
||||
const std::vector<uint16_t> &indices() const { return _indices; }
|
||||
const VertexOffsets &offsets() const { return _offsets; }
|
||||
const VertexAttributes &attributes() const { return _attributes; }
|
||||
const AABB &aabb() const { return _aabb; }
|
||||
|
||||
protected:
|
||||
int _vertexCount;
|
||||
std::vector<float> _vertices;
|
||||
std::vector<uint16_t> _indices;
|
||||
VertexOffsets _offsets;
|
||||
VertexAttributes _attributes;
|
||||
DrawMode _mode;
|
||||
|
||||
bool _inited { false };
|
||||
|
|
|
@ -100,7 +100,7 @@ static const vector<float> g_grassVertices = {
|
|||
|
||||
static const vector<uint16_t> g_quadIndices = { 0, 1, 2, 2, 3, 0 };
|
||||
|
||||
static const Mesh::VertexOffsets g_quadOffsets = { 0, -1, 3 * sizeof(float), -1, -1, -1, -1, -1, 5 * sizeof(float) };
|
||||
static const VertexAttributes g_quadAttributes = { 5 * sizeof(float), 0, -1, 3 * sizeof(float) };
|
||||
|
||||
// END Quads
|
||||
|
||||
|
@ -126,7 +126,7 @@ static const vector<uint16_t> g_cubeIndices = {
|
|||
6, 0, 3, 3, 5, 6
|
||||
};
|
||||
|
||||
static const Mesh::VertexOffsets g_cubeOffsets = { 0, -1, -1, -1, -1, -1, -1, -1, 3 * sizeof(float) };
|
||||
static const VertexAttributes g_cubeAttributes = { 3 * sizeof(float), 0 };
|
||||
|
||||
// END Cube
|
||||
|
||||
|
@ -174,7 +174,7 @@ static const vector<uint16_t> g_cubemapIndices {
|
|||
20, 21, 22, 21, 20, 23
|
||||
};
|
||||
|
||||
static const Mesh::VertexOffsets g_cubemapOffsets { 0, 3 * sizeof(float), 6 * sizeof(float), -1, -1, -1, -1, -1, 8 * sizeof(float) };
|
||||
static const VertexAttributes g_cubemapAttributes { 8 * sizeof(float), 0, 3 * sizeof(float), 6 * sizeof(float) };
|
||||
|
||||
// END Cubemap
|
||||
|
||||
|
@ -200,7 +200,7 @@ static const vector<uint16_t> g_aabbIndices = {
|
|||
6, 0, 0, 3, 3, 5, 5, 6
|
||||
};
|
||||
|
||||
static const Mesh::VertexOffsets g_aabbOffsets = { 0, -1, -1, -1, -1, -1, -1, -1, 3 * sizeof(float) };
|
||||
static const VertexAttributes g_aabbAttributes = { 3 * sizeof(float), 0, -1, -1, -1, -1, -1, -1, -1 };
|
||||
|
||||
// END AABB
|
||||
|
||||
|
@ -213,10 +213,10 @@ static unique_ptr<Mesh> getMesh(
|
|||
int vertexCount,
|
||||
const vector<float> &vertices,
|
||||
const vector<uint16_t> &indices,
|
||||
const Mesh::VertexOffsets &offsets,
|
||||
const VertexAttributes &attributes,
|
||||
Mesh::DrawMode mode = Mesh::DrawMode::Triangles) {
|
||||
|
||||
auto mesh = make_unique<Mesh>(vertexCount, vertices, indices, offsets, mode);
|
||||
auto mesh = make_unique<Mesh>(vertexCount, vertices, indices, attributes, mode);
|
||||
mesh->init();
|
||||
return move(mesh);
|
||||
}
|
||||
|
@ -227,7 +227,7 @@ static unique_ptr<Mesh> getSphereMesh() {
|
|||
|
||||
vector<float> vertices;
|
||||
vector<uint16_t> indices;
|
||||
Mesh::VertexOffsets offsets { 0, 3 * sizeof(float), 5 * sizeof(float), -1, -1, -1, -1, -1, 8 * sizeof(float) };
|
||||
VertexAttributes attributes { 8 * sizeof(float), 0, 3 * sizeof(float), 5 * sizeof(float) };
|
||||
|
||||
for (int y = 0; y <= kNumSegmentsY; ++y) {
|
||||
for (int x = 0; x <= kNumSegmentsX; ++x) {
|
||||
|
@ -267,7 +267,7 @@ static unique_ptr<Mesh> getSphereMesh() {
|
|||
oddRow = !oddRow;
|
||||
}
|
||||
|
||||
auto mesh = make_unique<Mesh>(static_cast<int>(vertices.size()) / 8, move(vertices), move(indices), move(offsets), Mesh::DrawMode::TriangleStrip);
|
||||
auto mesh = make_unique<Mesh>(static_cast<int>(vertices.size()) / 8, move(vertices), move(indices), move(attributes), Mesh::DrawMode::TriangleStrip);
|
||||
mesh->init();
|
||||
|
||||
return move(mesh);
|
||||
|
@ -275,20 +275,20 @@ static unique_ptr<Mesh> getSphereMesh() {
|
|||
|
||||
void Meshes::init() {
|
||||
if (!_inited) {
|
||||
_quad = getMesh(4, g_quadVertices, g_quadIndices, g_quadOffsets);
|
||||
_quadFlipX = getMesh(4, g_quadFlipXVertices, g_quadIndices, g_quadOffsets);
|
||||
_quadFlipY = getMesh(4, g_quadFlipYVertices, g_quadIndices, g_quadOffsets);
|
||||
_quadFlipXY = getMesh(4, g_quadFlipXYVertices, g_quadIndices, g_quadOffsets);
|
||||
_quadSwap = getMesh(4, g_quadSwapVertices, g_quadIndices, g_quadOffsets);
|
||||
_quadSwapFlipX = getMesh(4, g_quadSwapFlipXVertices, g_quadIndices, g_quadOffsets);
|
||||
_quadNDC = getMesh(4, g_quadNDCVertices, g_quadIndices, g_quadOffsets);
|
||||
_quadNDCFlipY = getMesh(4, g_quadNDCFlipYVertices, g_quadIndices, g_quadOffsets);
|
||||
_billboard = getMesh(4, g_billboardVertices, g_quadIndices, g_quadOffsets);
|
||||
_grass = getMesh(4, g_grassVertices, g_quadIndices, g_quadOffsets);
|
||||
_cube = getMesh(8, g_cubeVertices, g_cubeIndices, g_cubeOffsets);
|
||||
_quad = getMesh(4, g_quadVertices, g_quadIndices, g_quadAttributes);
|
||||
_quadFlipX = getMesh(4, g_quadFlipXVertices, g_quadIndices, g_quadAttributes);
|
||||
_quadFlipY = getMesh(4, g_quadFlipYVertices, g_quadIndices, g_quadAttributes);
|
||||
_quadFlipXY = getMesh(4, g_quadFlipXYVertices, g_quadIndices, g_quadAttributes);
|
||||
_quadSwap = getMesh(4, g_quadSwapVertices, g_quadIndices, g_quadAttributes);
|
||||
_quadSwapFlipX = getMesh(4, g_quadSwapFlipXVertices, g_quadIndices, g_quadAttributes);
|
||||
_quadNDC = getMesh(4, g_quadNDCVertices, g_quadIndices, g_quadAttributes);
|
||||
_quadNDCFlipY = getMesh(4, g_quadNDCFlipYVertices, g_quadIndices, g_quadAttributes);
|
||||
_billboard = getMesh(4, g_billboardVertices, g_quadIndices, g_quadAttributes);
|
||||
_grass = getMesh(4, g_grassVertices, g_quadIndices, g_quadAttributes);
|
||||
_cube = getMesh(8, g_cubeVertices, g_cubeIndices, g_cubeAttributes);
|
||||
_sphere = getSphereMesh();
|
||||
_cubemap = getMesh(24, g_cubemapVertices, g_cubemapIndices, g_cubemapOffsets);
|
||||
_aabb = getMesh(8, g_aabbVertices, g_aabbIndices, g_aabbOffsets, Mesh::DrawMode::Lines);
|
||||
_cubemap = getMesh(24, g_cubemapVertices, g_cubemapIndices, g_cubemapAttributes);
|
||||
_aabb = getMesh(8, g_aabbVertices, g_aabbIndices, g_aabbAttributes, Mesh::DrawMode::Lines);
|
||||
|
||||
_inited = true;
|
||||
}
|
||||
|
|
|
@ -890,15 +890,15 @@ void MdlReader::readMesh(ModelNode &node) {
|
|||
_mdxReader->seek(offMdxData);
|
||||
vertices = _mdxReader->getFloatArray(header.numVertices * header.mdxVertexSize / sizeof(float));
|
||||
|
||||
Mesh::VertexOffsets offsets;
|
||||
offsets.stride = header.mdxVertexSize;
|
||||
offsets.vertexCoords = header.offMdxVertices;
|
||||
offsets.normals = header.offMdxNormals;
|
||||
offsets.texCoords1 = header.offMdxTexCoords1;
|
||||
offsets.texCoords2 = header.offMdxTexCoords2;
|
||||
VertexAttributes attributes;
|
||||
attributes.stride = header.mdxVertexSize;
|
||||
attributes.offCoords = header.offMdxVertices;
|
||||
attributes.offNormals = header.offMdxNormals;
|
||||
attributes.offTexCoords1 = header.offMdxTexCoords1;
|
||||
attributes.offTexCoords2 = header.offMdxTexCoords2;
|
||||
if (header.offMdxTanSpace != -1) {
|
||||
offsets.bitangents = header.offMdxTanSpace + 0 * sizeof(float);
|
||||
offsets.tangents = header.offMdxTanSpace + 3 * sizeof(float);
|
||||
attributes.offBitangents = header.offMdxTanSpace + 0 * sizeof(float);
|
||||
attributes.offTangents = header.offMdxTanSpace + 3 * sizeof(float);
|
||||
}
|
||||
|
||||
vector<uint16_t> indices;
|
||||
|
@ -909,7 +909,7 @@ void MdlReader::readMesh(ModelNode &node) {
|
|||
|
||||
seek(endPos);
|
||||
|
||||
loadMesh(header, header.numVertices, move(vertices), move(indices), move(offsets), node);
|
||||
loadMesh(header, header.numVertices, move(vertices), move(indices), move(attributes), node);
|
||||
}
|
||||
|
||||
MdlReader::MeshHeader MdlReader::readMeshHeader() {
|
||||
|
@ -983,7 +983,7 @@ MdlReader::MeshHeader MdlReader::readMeshHeader() {
|
|||
return move(result);
|
||||
}
|
||||
|
||||
void MdlReader::loadMesh(const MeshHeader &header, int numVertices, vector<float> &&vertices, vector<uint16_t> &&indices, Mesh::VertexOffsets &&offsets, ModelNode &node) {
|
||||
void MdlReader::loadMesh(const MeshHeader &header, int numVertices, vector<float> &&vertices, vector<uint16_t> &&indices, VertexAttributes &&offsets, ModelNode &node) {
|
||||
auto mesh = make_unique<Mesh>(numVertices, vertices, indices, offsets);
|
||||
mesh->computeAABB();
|
||||
|
||||
|
@ -1020,8 +1020,8 @@ void MdlReader::readSkin(ModelNode &node) {
|
|||
vector<uint16_t> boneIndices(readUint16Array(16));
|
||||
ignore(4); // padding
|
||||
|
||||
node._mesh->_mesh->_offsets.boneWeights = offMdxBoneWeights;
|
||||
node._mesh->_mesh->_offsets.boneIndices = offMdxBoneIndices;
|
||||
node._mesh->_mesh->_attributes.offBoneWeights = offMdxBoneWeights;
|
||||
node._mesh->_mesh->_attributes.offBoneIndices = offMdxBoneIndices;
|
||||
|
||||
unordered_map<uint16_t, uint16_t> nodeIdxByBoneIdx;
|
||||
seek(kMdlDataOffset + offBones);
|
||||
|
@ -1095,11 +1095,11 @@ void MdlReader::readSaber(ModelNode &node) {
|
|||
*(verticesPtr++) = normalsPtr[2];
|
||||
}
|
||||
|
||||
Mesh::VertexOffsets offsets;
|
||||
offsets.vertexCoords = 0;
|
||||
offsets.texCoords1 = 3 * sizeof(float);
|
||||
offsets.normals = 5 * sizeof(float);
|
||||
offsets.stride = 8 * sizeof(float);
|
||||
VertexAttributes attributes;
|
||||
attributes.stride = 8 * sizeof(float);
|
||||
attributes.offCoords = 0;
|
||||
attributes.offTexCoords1 = 3 * sizeof(float);
|
||||
attributes.offNormals = 5 * sizeof(float);
|
||||
|
||||
vector<uint16_t> indices {
|
||||
0, 13, 12, 0, 1, 13,
|
||||
|
@ -1110,7 +1110,7 @@ void MdlReader::readSaber(ModelNode &node) {
|
|||
10, 6, 7, 10, 7, 11
|
||||
};
|
||||
|
||||
loadMesh(header, numVertices, move(vertices), move(indices), move(offsets), node);
|
||||
loadMesh(header, numVertices, move(vertices), move(indices), move(attributes), node);
|
||||
}
|
||||
|
||||
void MdlReader::readDanglymesh(ModelNode &node) {
|
||||
|
|
|
@ -99,7 +99,7 @@ private:
|
|||
void readAABB(graphics::ModelNode &node);
|
||||
void readSaber(graphics::ModelNode &node);
|
||||
|
||||
void loadMesh(const MeshHeader &header, int numVertices, std::vector<float> &&vertices, std::vector<uint16_t> &&indices, Mesh::VertexOffsets &&offsets, graphics::ModelNode &node);
|
||||
void loadMesh(const MeshHeader &header, int numVertices, std::vector<float> &&vertices, std::vector<uint16_t> &&indices, VertexAttributes &&attributes, graphics::ModelNode &node);
|
||||
MeshHeader readMeshHeader();
|
||||
std::shared_ptr<AABBNode> readAABBNode(uint32_t offset);
|
||||
};
|
||||
|
|
38
src/engine/graphics/vertexattributes.h
Normal file
38
src/engine/graphics/vertexattributes.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 The reone project contributors
|
||||
*
|
||||
* 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace reone {
|
||||
|
||||
namespace graphics {
|
||||
|
||||
struct VertexAttributes {
|
||||
int stride { 0 };
|
||||
int offCoords { 0 };
|
||||
int offNormals { -1 };
|
||||
int offTexCoords1 { -1 };
|
||||
int offTexCoords2 { -1 };
|
||||
int offTangents { -1 };
|
||||
int offBitangents { -1 };
|
||||
int offBoneWeights { -1 };
|
||||
int offBoneIndices { -1 };
|
||||
};
|
||||
|
||||
} // namespace graphics
|
||||
|
||||
} // namespace reone
|
Loading…
Reference in a new issue