462 lines
11 KiB
C++
462 lines
11 KiB
C++
/*
|
|
* This file is part of the Colobot: Gold Edition source code
|
|
* Copyright (C) 2001-2023, Daniel Roux, EPSITEC SA & TerranovaTeam
|
|
* http://epsitec.ch; http://colobot.info; http://github.com/colobot
|
|
*
|
|
* 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 http://gnu.org/licenses
|
|
*/
|
|
|
|
#include "graphics/model/model_mesh.h"
|
|
|
|
#include <array>
|
|
|
|
namespace Gfx
|
|
{
|
|
|
|
CVertexProxy::CVertexProxy(CModelPart* part, size_t index)
|
|
: m_part(part), m_index(index)
|
|
{
|
|
|
|
}
|
|
|
|
const glm::vec3& CVertexProxy::GetPosition() const
|
|
{
|
|
return m_part->m_positions.array[m_index];
|
|
}
|
|
|
|
void CVertexProxy::SetPosition(const glm::vec3& position) const
|
|
{
|
|
m_part->m_positions.array[m_index] = position;
|
|
}
|
|
|
|
const glm::u8vec4& CVertexProxy::GetColor() const
|
|
{
|
|
return m_part->m_colors.array[m_index];
|
|
}
|
|
|
|
void CVertexProxy::SetColor(const glm::u8vec4& color) const
|
|
{
|
|
m_part->m_colors.array[m_index] = color;
|
|
}
|
|
|
|
const glm::vec2& CVertexProxy::GetUV1() const
|
|
{
|
|
return m_part->m_uvs1.array[m_index];
|
|
}
|
|
|
|
void CVertexProxy::SetUV1(const glm::vec2& uv) const
|
|
{
|
|
m_part->m_uvs1.array[m_index] = uv;
|
|
}
|
|
|
|
const glm::vec2& CVertexProxy::GetUV2() const
|
|
{
|
|
return m_part->m_uvs2.array[m_index];
|
|
}
|
|
|
|
void CVertexProxy::SetUV2(const glm::vec2& uv) const
|
|
{
|
|
m_part->m_uvs2.array[m_index] = uv;
|
|
}
|
|
|
|
const glm::vec3& CVertexProxy::GetNormal() const
|
|
{
|
|
return m_part->m_normals.array[m_index];
|
|
}
|
|
|
|
void CVertexProxy::SetNormal(const glm::vec3& normal) const
|
|
{
|
|
m_part->m_normals.array[m_index] = normal;
|
|
}
|
|
|
|
const glm::vec4& CVertexProxy::GetTangent() const
|
|
{
|
|
return m_part->m_tangents.array[m_index];
|
|
}
|
|
|
|
void CVertexProxy::SetTangent(const glm::vec4& tangent) const
|
|
{
|
|
m_part->m_tangents.array[m_index] = tangent;
|
|
}
|
|
|
|
const glm::u8vec4& CVertexProxy::GetBoneIndices() const
|
|
{
|
|
return m_part->m_boneIndices.array[m_index];
|
|
}
|
|
|
|
void CVertexProxy::SetBoneIndices(const glm::u8vec4& indices) const
|
|
{
|
|
m_part->m_boneIndices.array[m_index] = indices;
|
|
}
|
|
|
|
const glm::vec4& CVertexProxy::GetBoneWeights() const
|
|
{
|
|
return m_part->m_boneWeights.array[m_index];
|
|
}
|
|
|
|
void CVertexProxy::SetBoneWeights(const glm::vec4& weights) const
|
|
{
|
|
m_part->m_boneWeights.array[m_index] = weights;
|
|
}
|
|
|
|
|
|
CModelPart::CModelPart(const Material& material, size_t vertices, size_t indices)
|
|
: m_material(material) {
|
|
m_positions.array.resize(vertices);
|
|
m_indices.array.resize(indices);
|
|
}
|
|
|
|
const Material& CModelPart::GetMaterial() const
|
|
{
|
|
return m_material;
|
|
}
|
|
|
|
void CModelPart::SetVertices(size_t count)
|
|
{
|
|
m_positions.array.resize(count);
|
|
|
|
if (m_colors.enabled) m_colors.array.resize(count);
|
|
if (m_uvs1.enabled) m_uvs1.array.resize(count);
|
|
if (m_uvs2.enabled) m_uvs2.array.resize(count);
|
|
if (m_normals.enabled) m_normals.array.resize(count);
|
|
if (m_tangents.enabled) m_tangents.array.resize(count);
|
|
if (m_boneIndices.enabled) m_boneIndices.array.resize(count);
|
|
if (m_boneWeights.enabled) m_boneWeights.array.resize(count);
|
|
}
|
|
|
|
void CModelPart::SetIndices(size_t count)
|
|
{
|
|
m_indices.enabled = count > 0;
|
|
m_indices.array.resize(count);
|
|
}
|
|
|
|
bool CModelPart::Has(VertexAttribute array) const
|
|
{
|
|
switch (array)
|
|
{
|
|
case VertexAttribute::POSITION:
|
|
return true;
|
|
case VertexAttribute::COLOR:
|
|
return m_colors.enabled;
|
|
case VertexAttribute::UV1:
|
|
return m_uvs1.enabled;
|
|
case VertexAttribute::UV2:
|
|
return m_uvs2.enabled;
|
|
case VertexAttribute::NORMAL:
|
|
return m_normals.enabled;
|
|
case VertexAttribute::TANGENT:
|
|
return m_tangents.enabled;
|
|
case VertexAttribute::BONE_INDICES:
|
|
return m_boneIndices.enabled;
|
|
case VertexAttribute::BONE_WEIGHTS:
|
|
return m_boneWeights.enabled;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void CModelPart::Add(VertexAttribute attribute)
|
|
{
|
|
switch (attribute)
|
|
{
|
|
case VertexAttribute::COLOR:
|
|
m_colors.enabled = true;
|
|
m_colors.array.resize(m_positions.array.size());
|
|
break;
|
|
case VertexAttribute::UV1:
|
|
m_uvs1.enabled = true;
|
|
m_uvs1.array.resize(m_positions.array.size());
|
|
break;
|
|
case VertexAttribute::UV2:
|
|
m_uvs2.enabled = true;
|
|
m_uvs2.array.resize(m_positions.array.size());
|
|
break;
|
|
case VertexAttribute::NORMAL:
|
|
m_normals.enabled = true;
|
|
m_normals.array.resize(m_positions.array.size());
|
|
break;
|
|
case VertexAttribute::TANGENT:
|
|
m_tangents.enabled = true;
|
|
m_tangents.array.resize(m_positions.array.size());
|
|
break;
|
|
case VertexAttribute::BONE_INDICES:
|
|
m_boneIndices.enabled = true;
|
|
m_boneIndices.array.resize(m_positions.array.size());
|
|
break;
|
|
case VertexAttribute::BONE_WEIGHTS:
|
|
m_boneWeights.enabled = true;
|
|
m_boneWeights.array.resize(m_positions.array.size());
|
|
return;
|
|
}
|
|
}
|
|
|
|
void CModelPart::Remove(VertexAttribute attribute)
|
|
{
|
|
switch (attribute)
|
|
{
|
|
case VertexAttribute::COLOR:
|
|
m_colors.enabled = false;
|
|
m_colors.array.resize(0);
|
|
return;
|
|
case VertexAttribute::UV1:
|
|
m_uvs1.enabled = false;
|
|
m_uvs1.array.resize(0);
|
|
return;
|
|
case VertexAttribute::UV2:
|
|
m_uvs2.enabled = false;
|
|
m_uvs2.array.resize(0);
|
|
return;
|
|
case VertexAttribute::NORMAL:
|
|
m_normals.enabled = false;
|
|
m_normals.array.resize(0);
|
|
return;
|
|
case VertexAttribute::TANGENT:
|
|
m_tangents.enabled = false;
|
|
m_tangents.array.resize(0);
|
|
return;
|
|
case VertexAttribute::BONE_INDICES:
|
|
m_boneIndices.enabled = false;
|
|
m_boneIndices.array.resize(0);
|
|
return;
|
|
case VertexAttribute::BONE_WEIGHTS:
|
|
m_boneWeights.enabled = false;
|
|
m_boneWeights.array.resize(0);
|
|
return;
|
|
}
|
|
}
|
|
|
|
bool CModelPart::IsIndexed() const
|
|
{
|
|
return m_indices.enabled;
|
|
}
|
|
|
|
size_t CModelPart::GetVertexCount() const
|
|
{
|
|
return m_positions.array.size();
|
|
}
|
|
|
|
size_t CModelPart::GetIndexCount() const
|
|
{
|
|
return m_indices.array.size();
|
|
}
|
|
|
|
const std::vector<unsigned int>& CModelPart::GetIndices() const
|
|
{
|
|
return m_indices.array;
|
|
}
|
|
|
|
CVertexProxy CModelPart::GetVertex(size_t index)
|
|
{
|
|
return CVertexProxy(this, index);
|
|
}
|
|
|
|
std::uint32_t CModelPart::GetIndex(size_t index)
|
|
{
|
|
return m_indices.array[index];
|
|
}
|
|
|
|
void CModelPart::AddVertex(const Vertex3D& vertex)
|
|
{
|
|
m_positions.array.push_back(vertex.position);
|
|
|
|
if (m_colors.enabled) m_colors.array.push_back(vertex.color);
|
|
if (m_uvs1.enabled) m_uvs1.array.push_back(vertex.uv);
|
|
if (m_uvs2.enabled) m_uvs2.array.push_back(vertex.uv2);
|
|
if (m_normals.enabled) m_normals.array.push_back(vertex.normal);
|
|
if (m_tangents.enabled) m_tangents.array.push_back({ 1.0f, 0.0f, 0.0f, 1.0f });
|
|
if (m_boneIndices.enabled) m_boneIndices.array.push_back({ 0, 0, 0, 0 });
|
|
if (m_boneWeights.enabled) m_boneWeights.array.push_back({ 1.0f, 0.0f, 0.0f, 0.0f });
|
|
}
|
|
|
|
void CModelPart::AddIndex(unsigned int index)
|
|
{
|
|
if (m_indices.enabled) m_indices.array.push_back(index);
|
|
}
|
|
|
|
void CModelPart::SetIndex(size_t index, unsigned int value)
|
|
{
|
|
m_indices.array[index] = value;
|
|
}
|
|
|
|
void CModelPart::GetTriangles(std::vector<Gfx::ModelTriangle>& triangles)
|
|
{
|
|
size_t n = IsIndexed()
|
|
? GetIndexCount()
|
|
: GetVertexCount();
|
|
|
|
for (size_t i = 0; i < n - 2; i += 3)
|
|
{
|
|
std::array<Gfx::Vertex3D, 3> verts;
|
|
|
|
for (size_t j = 0; j < 3; j++)
|
|
{
|
|
size_t index = IsIndexed()
|
|
? GetIndex(i + j)
|
|
: i + j;
|
|
auto vertex = GetVertex(index);
|
|
|
|
verts[j].position = vertex.GetPosition();
|
|
|
|
if (Has(VertexAttribute::COLOR)) verts[j].color = vertex.GetColor();
|
|
else verts[j].color = { 255, 255, 255, 255 };
|
|
|
|
if (Has(VertexAttribute::UV1)) verts[j].uv = vertex.GetUV1();
|
|
else verts[j].uv = { 0, 0 };
|
|
|
|
if (Has(VertexAttribute::UV2)) verts[j].uv2 = vertex.GetUV2();
|
|
else verts[j].uv2 = { 0, 0 };
|
|
|
|
if (Has(VertexAttribute::NORMAL)) verts[j].normal = vertex.GetNormal();
|
|
else verts[j].normal = { 0, 0, 1 };
|
|
}
|
|
|
|
triangles.push_back({ verts[0], verts[1], verts[2], GetMaterial() });
|
|
}
|
|
}
|
|
|
|
void CModelMesh::AddTriangle(const ModelTriangle& triangle)
|
|
{
|
|
for (auto& part : m_parts)
|
|
{
|
|
if (part->GetMaterial() == triangle.material)
|
|
{
|
|
part->AddVertex(triangle.p1);
|
|
part->AddVertex(triangle.p2);
|
|
part->AddVertex(triangle.p3);
|
|
return;
|
|
}
|
|
}
|
|
|
|
auto part = std::make_unique<CModelPart>(triangle.material, 0, 0);
|
|
|
|
part->Add(VertexAttribute::COLOR);
|
|
part->Add(VertexAttribute::UV1);
|
|
part->Add(VertexAttribute::UV2);
|
|
part->Add(VertexAttribute::NORMAL);
|
|
|
|
part->AddVertex(triangle.p1);
|
|
part->AddVertex(triangle.p2);
|
|
part->AddVertex(triangle.p3);
|
|
|
|
m_parts.push_back(std::move(part));
|
|
}
|
|
|
|
void CModelMesh::AddTriangle(const Triangle& triangle, const Material& material)
|
|
{
|
|
for (auto& part : m_parts)
|
|
{
|
|
if (part->GetMaterial() == material)
|
|
{
|
|
part->AddVertex(triangle.p1);
|
|
part->AddVertex(triangle.p2);
|
|
part->AddVertex(triangle.p3);
|
|
return;
|
|
}
|
|
}
|
|
|
|
auto part = std::make_unique<CModelPart>(material, 0, 0);
|
|
|
|
part->Add(VertexAttribute::COLOR);
|
|
part->Add(VertexAttribute::UV1);
|
|
part->Add(VertexAttribute::UV2);
|
|
part->Add(VertexAttribute::NORMAL);
|
|
|
|
part->AddVertex(triangle.p1);
|
|
part->AddVertex(triangle.p2);
|
|
part->AddVertex(triangle.p3);
|
|
|
|
m_parts.push_back(std::move(part));
|
|
}
|
|
|
|
size_t CModelMesh::GetPartCount() const
|
|
{
|
|
return m_parts.size();
|
|
}
|
|
|
|
CModelPart* CModelMesh::GetPart(size_t index) const
|
|
{
|
|
return m_parts[index].get();
|
|
}
|
|
|
|
CModelPart* CModelMesh::AddPart(const Material& material)
|
|
{
|
|
for (auto& part : m_parts)
|
|
{
|
|
if (part->GetMaterial() == material)
|
|
{
|
|
return part.get();
|
|
}
|
|
}
|
|
|
|
auto part = std::make_unique<CModelPart>(material, 0, 0);
|
|
|
|
m_parts.push_back(std::move(part));
|
|
|
|
return m_parts.back().get();
|
|
}
|
|
|
|
const glm::vec3& CModelMesh::GetPosition() const
|
|
{
|
|
return m_position;
|
|
}
|
|
|
|
void CModelMesh::SetPosition(const glm::vec3& position)
|
|
{
|
|
m_position = position;
|
|
}
|
|
|
|
const glm::vec3& CModelMesh::GetRotation() const
|
|
{
|
|
return m_rotation;
|
|
}
|
|
|
|
void CModelMesh::SetRotation(const glm::vec3& rotation)
|
|
{
|
|
m_rotation = rotation;
|
|
}
|
|
|
|
const glm::vec3& CModelMesh::GetScale() const
|
|
{
|
|
return m_scale;
|
|
}
|
|
|
|
void CModelMesh::SetScale(const glm::vec3& scale)
|
|
{
|
|
m_scale = scale;
|
|
}
|
|
|
|
const std::string& CModelMesh::GetParent() const
|
|
{
|
|
return m_parent;
|
|
}
|
|
|
|
void CModelMesh::SetParent(const std::string& parent)
|
|
{
|
|
m_parent = parent;
|
|
}
|
|
|
|
std::vector<ModelTriangle> CModelMesh::GetTriangles() const
|
|
{
|
|
std::vector<ModelTriangle> triangles;
|
|
|
|
for (const auto& part : m_parts)
|
|
{
|
|
part->GetTriangles(triangles);
|
|
}
|
|
|
|
return triangles;
|
|
}
|
|
|
|
} // namespace Gfx
|