diff --git a/ACKNOWLEDGEMENTS.html b/ACKNOWLEDGEMENTS.html
index fea986ca..884d7a67 100644
--- a/ACKNOWLEDGEMENTS.html
+++ b/ACKNOWLEDGEMENTS.html
@@ -556,3 +556,55 @@ https://www.reddit.com/r/gamedev/comments/5iuf3h/i_am_writting_a_3d_monster_mode
http://www.gdcvault.com/play/1020583/Animation-Bootcamp-An-Indie-Approach
+
Qt3D
+
+ /****************************************************************************
+ **
+ ** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+ ** Contact: https://www.qt.io/licensing/
+ **
+ ** This file is part of the Qt3D module of the Qt Toolkit.
+ **
+ ** $QT_BEGIN_LICENSE:BSD$
+ ** Commercial License Usage
+ ** Licensees holding valid commercial Qt licenses may use this file in
+ ** accordance with the commercial license agreement provided with the
+ ** Software or, alternatively, in accordance with the terms contained in
+ ** a written agreement between you and The Qt Company. For licensing terms
+ ** and conditions see https://www.qt.io/terms-conditions. For further
+ ** information use the contact form at https://www.qt.io/contact-us.
+ **
+ ** BSD License Usage
+ ** Alternatively, you may use this file under the terms of the BSD license
+ ** as follows:
+ **
+ ** "Redistribution and use in source and binary forms, with or without
+ ** modification, are permitted provided that the following conditions are
+ ** met:
+ ** * Redistributions of source code must retain the above copyright
+ ** notice, this list of conditions and the following disclaimer.
+ ** * Redistributions in binary form must reproduce the above copyright
+ ** notice, this list of conditions and the following disclaimer in
+ ** the documentation and/or other materials provided with the
+ ** distribution.
+ ** * Neither the name of The Qt Company Ltd nor the names of its
+ ** contributors may be used to endorse or promote products derived
+ ** from this software without specific prior written permission.
+ **
+ **
+ ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+ **
+ ** $QT_END_LICENSE$
+ **
+ ****************************************************************************/
+
\ No newline at end of file
diff --git a/resources.qrc b/resources.qrc
index 3392db02..79ed3608 100644
--- a/resources.qrc
+++ b/resources.qrc
@@ -10,6 +10,8 @@
shaders/default.frag
shaders/default.core.vert
shaders/default.core.frag
+ shaders/pbr.vert
+ shaders/pbr.frag
ACKNOWLEDGEMENTS.html
AUTHORS
CONTRIBUTORS
diff --git a/shaders/default.core.vert b/shaders/default.core.vert
index 4aafcdb5..876b2ba6 100644
--- a/shaders/default.core.vert
+++ b/shaders/default.core.vert
@@ -3,10 +3,14 @@ in vec4 vertex;
in vec3 normal;
in vec3 color;
in vec2 texCoord;
+in float metalness;
+in float roughness;
out vec3 vert;
out vec3 vertNormal;
out vec3 vertColor;
out vec2 vertTexCoord;
+out float vertMetalness;
+out float vertRoughness;
uniform mat4 projMatrix;
uniform mat4 mvMatrix;
uniform mat3 normalMatrix;
@@ -16,5 +20,7 @@ void main()
vertNormal = normalMatrix * normal;
vertColor = color;
vertTexCoord = texCoord;
+ vertMetalness = metalness;
+ vertRoughness = roughness;
gl_Position = projMatrix * mvMatrix * vertex;
}
\ No newline at end of file
diff --git a/shaders/default.vert b/shaders/default.vert
index 63a207d4..a711eda0 100644
--- a/shaders/default.vert
+++ b/shaders/default.vert
@@ -2,10 +2,15 @@ attribute vec4 vertex;
attribute vec3 normal;
attribute vec3 color;
attribute vec2 texCoord;
+attribute float metalness;
+attribute float roughness;
varying vec3 vert;
varying vec3 vertNormal;
varying vec3 vertColor;
varying vec2 vertTexCoord;
+varying float vertMetalness;
+varying float vertRoughness;
+varying vec3 vertView;
uniform mat4 projMatrix;
uniform mat4 mvMatrix;
uniform mat3 normalMatrix;
diff --git a/shaders/pbr.frag b/shaders/pbr.frag
new file mode 100644
index 00000000..3309648a
--- /dev/null
+++ b/shaders/pbr.frag
@@ -0,0 +1,320 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// Please note that, this file "pbr.frag" is copied and slightly modified from the Qt3D's pbr shader "metalrough.inc.frag"
+// https://github.com/qt/qt3d/blob/5.11/src/extras/shaders/gl3/metalrough.inc.frag
+
+// Exposure correction
+float exposure;
+// Gamma correction
+float gamma;
+
+varying highp vec3 vert;
+varying highp vec3 vertNormal;
+varying highp vec3 vertColor;
+varying highp vec2 vertTexCoord;
+varying highp float vertMetalness;
+varying highp float vertRoughness;
+varying highp vec3 vertView;
+uniform highp vec3 lightPos;
+uniform highp sampler2D textureId;
+uniform highp int textureEnabled;
+
+const int MAX_LIGHTS = 8;
+const int TYPE_POINT = 0;
+const int TYPE_DIRECTIONAL = 1;
+const int TYPE_SPOT = 2;
+struct Light {
+ int type;
+ vec3 position;
+ vec3 color;
+ float intensity;
+ vec3 direction;
+ float constantAttenuation;
+ float linearAttenuation;
+ float quadraticAttenuation;
+ float cutOffAngle;
+};
+int lightCount;
+Light lights[MAX_LIGHTS];
+
+float remapRoughness(const in float roughness)
+{
+ // As per page 14 of
+ // http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf
+ // we remap the roughness to give a more perceptually linear response
+ // of "bluriness" as a function of the roughness specified by the user.
+ // r = roughness^2
+ float maxSpecPower;
+ float minRoughness;
+ maxSpecPower = 999999.0;
+ minRoughness = sqrt(2.0 / (maxSpecPower + 2.0));
+ return max(roughness * roughness, minRoughness);
+}
+
+float normalDistribution(const in vec3 n, const in vec3 h, const in float alpha)
+{
+ // Blinn-Phong approximation - see
+ // http://graphicrants.blogspot.co.uk/2013/08/specular-brdf-reference.html
+ float specPower = 2.0 / (alpha * alpha) - 2.0;
+ return (specPower + 2.0) / (2.0 * 3.14159) * pow(max(dot(n, h), 0.0), specPower);
+}
+
+vec3 fresnelFactor(const in vec3 color, const in float cosineFactor)
+{
+ // Calculate the Fresnel effect value
+ vec3 f = color;
+ vec3 F = f + (1.0 - f) * pow(1.0 - cosineFactor, 5.0);
+ return clamp(F, f, vec3(1.0));
+}
+
+float geometricModel(const in float lDotN,
+ const in float vDotN,
+ const in vec3 h)
+{
+ // Implicit geometric model (equal to denominator in specular model).
+ // This currently assumes that there is no attenuation by geometric shadowing or
+ // masking according to the microfacet theory.
+ return lDotN * vDotN;
+}
+
+vec3 specularModel(const in vec3 F0,
+ const in float sDotH,
+ const in float sDotN,
+ const in float vDotN,
+ const in vec3 n,
+ const in vec3 h)
+{
+ // Clamp sDotN and vDotN to small positive value to prevent the
+ // denominator in the reflection equation going to infinity. Balance this
+ // by using the clamped values in the geometric factor function to
+ // avoid ugly seams in the specular lighting.
+ float sDotNPrime = max(sDotN, 0.001);
+ float vDotNPrime = max(vDotN, 0.001);
+
+ vec3 F = fresnelFactor(F0, sDotH);
+ float G = geometricModel(sDotNPrime, vDotNPrime, h);
+
+ vec3 cSpec = F * G / (4.0 * sDotNPrime * vDotNPrime);
+ return clamp(cSpec, vec3(0.0), vec3(1.0));
+}
+
+vec3 pbrModel(const in int lightIndex,
+ const in vec3 wPosition,
+ const in vec3 wNormal,
+ const in vec3 wView,
+ const in vec3 baseColor,
+ const in float metalness,
+ const in float alpha,
+ const in float ambientOcclusion)
+{
+ // Calculate some useful quantities
+ vec3 n = wNormal;
+ vec3 s = vec3(0.0);
+ vec3 v = wView;
+ vec3 h = vec3(0.0);
+
+ float vDotN = dot(v, n);
+ float sDotN = 0.0;
+ float sDotH = 0.0;
+ float att = 1.0;
+
+ if (lights[lightIndex].type != TYPE_DIRECTIONAL) {
+ // Point and Spot lights
+ vec3 sUnnormalized = vec3(lights[lightIndex].position) - wPosition;
+ s = normalize(sUnnormalized);
+
+ // Calculate the attenuation factor
+ sDotN = dot(s, n);
+ if (sDotN > 0.0) {
+ if (lights[lightIndex].constantAttenuation != 0.0
+ || lights[lightIndex].linearAttenuation != 0.0
+ || lights[lightIndex].quadraticAttenuation != 0.0) {
+ float dist = length(sUnnormalized);
+ att = 1.0 / (lights[lightIndex].constantAttenuation +
+ lights[lightIndex].linearAttenuation * dist +
+ lights[lightIndex].quadraticAttenuation * dist * dist);
+ }
+
+ // The light direction is in world space already
+ if (lights[lightIndex].type == TYPE_SPOT) {
+ // Check if fragment is inside or outside of the spot light cone
+ if (degrees(acos(dot(-s, lights[lightIndex].direction))) > lights[lightIndex].cutOffAngle)
+ sDotN = 0.0;
+ }
+ }
+ } else {
+ // Directional lights
+ // The light direction is in world space already
+ s = normalize(-lights[lightIndex].direction);
+ sDotN = dot(s, n);
+ }
+
+ h = normalize(s + v);
+ sDotH = dot(s, h);
+
+ // Calculate diffuse component
+ vec3 diffuseColor = (1.0 - metalness) * baseColor * lights[lightIndex].color;
+ vec3 diffuse = diffuseColor * max(sDotN, 0.0) / 3.14159;
+
+ // Calculate specular component
+ vec3 dielectricColor = vec3(0.04);
+ vec3 F0 = mix(dielectricColor, baseColor, metalness);
+ vec3 specularFactor = vec3(0.0);
+ if (sDotN > 0.0) {
+ specularFactor = specularModel(F0, sDotH, sDotN, vDotN, n, h);
+ specularFactor *= normalDistribution(n, h, alpha);
+ }
+ vec3 specularColor = lights[lightIndex].color;
+ vec3 specular = specularColor * specularFactor;
+
+ // Blend between diffuse and specular to conserver energy
+ vec3 color = att * lights[lightIndex].intensity * (specular + diffuse * (vec3(1.0) - specular));
+
+ // Reduce by ambient occlusion amount
+ color *= ambientOcclusion;
+
+ return color;
+}
+
+vec3 toneMap(const in vec3 c)
+{
+ return c / (c + vec3(1.0));
+}
+
+vec3 gammaCorrect(const in vec3 color)
+{
+ return pow(color, vec3(1.0 / gamma));
+}
+
+vec4 metalRoughFunction(const in vec4 baseColor,
+ const in float metalness,
+ const in float roughness,
+ const in float ambientOcclusion,
+ const in vec3 worldPosition,
+ const in vec3 worldView,
+ const in vec3 worldNormal)
+{
+ vec3 cLinear = vec3(0.0);
+
+ // Remap roughness for a perceptually more linear correspondence
+ float alpha = remapRoughness(roughness);
+
+ for (int i = 0; i < lightCount; ++i) {
+ cLinear += pbrModel(i,
+ worldPosition,
+ worldNormal,
+ worldView,
+ baseColor.rgb,
+ metalness,
+ alpha,
+ ambientOcclusion);
+ }
+
+ // Apply exposure correction
+ cLinear *= pow(2.0, exposure);
+
+ // Apply simple (Reinhard) tonemap transform to get into LDR range [0, 1]
+ vec3 cToneMapped = toneMap(cLinear);
+
+ // Apply gamma correction prior to display
+ vec3 cGamma = gammaCorrect(cToneMapped);
+
+ return vec4(cGamma, 1.0);
+}
+
+void main()
+{
+ highp vec3 color = vertColor;
+ if (textureEnabled == 1) {
+ color = texture2D(textureId, vertTexCoord).rgb;
+ }
+
+ // FIXME: don't hard code here
+ exposure = 0.0;
+ gamma = 2.2;
+ const highp float vertAmbientOcclusion = 1.0;
+
+ // Light settings:
+ // https://doc-snapshots.qt.io/qt5-5.12/qt3d-pbr-materials-lights-qml.html
+ lightCount = 3;
+
+ lights[0].type = TYPE_POINT;
+ lights[0].position = vec3(0.0, 5.0, 0.0);
+ lights[0].color = vec3(1.0, 1.0, 1.0);
+ lights[0].intensity = 5.0;
+ lights[0].constantAttenuation = 1.0;
+ lights[0].linearAttenuation = 0.0;
+ lights[0].quadraticAttenuation = 0.0025;
+
+ lights[1].type = TYPE_POINT;
+ lights[1].position = vec3(5.0, 0.0, 0.0);
+ lights[1].color = vec3(1.0, 1.0, 1.0);
+ lights[1].intensity = 0.8;
+ lights[1].constantAttenuation = 1.0;
+ lights[1].linearAttenuation = 0.0;
+ lights[1].quadraticAttenuation = 0.0025;
+
+ lights[2].type = TYPE_POINT;
+ lights[2].position = vec3(0.0, -5.0, 0.0);
+ lights[2].color = vec3(1.0, 1.0, 1.0);
+ lights[2].intensity = 0.15;
+ lights[2].constantAttenuation = 1.0;
+ lights[2].linearAttenuation = 0.0;
+ lights[2].quadraticAttenuation = 0.0025;
+
+ gl_FragColor = metalRoughFunction(vec4(color, 1.0),
+ vertMetalness,
+ vertRoughness,
+ vertAmbientOcclusion,
+ vert,
+ vertView,
+ vertNormal);
+}
diff --git a/shaders/pbr.vert b/shaders/pbr.vert
new file mode 100644
index 00000000..bbfe78fe
--- /dev/null
+++ b/shaders/pbr.vert
@@ -0,0 +1,27 @@
+attribute vec4 vertex;
+attribute vec3 normal;
+attribute vec3 color;
+attribute vec2 texCoord;
+attribute float metalness;
+attribute float roughness;
+varying vec3 vert;
+varying vec3 vertNormal;
+varying vec3 vertColor;
+varying vec2 vertTexCoord;
+varying float vertMetalness;
+varying float vertRoughness;
+varying vec3 vertView;
+uniform mat4 projMatrix;
+uniform mat4 mvMatrix;
+uniform mat3 normalMatrix;
+void main()
+{
+ vert = vertex.xyz;
+ vertNormal = normalize((projMatrix * mvMatrix * vec4(normal, 1.0)).xyz);
+ vertColor = color;
+ vertTexCoord = texCoord;
+ vertMetalness = metalness;
+ vertRoughness = roughness;
+ vertView = (projMatrix * mvMatrix * vec4(0, 0, 0, 1.0)).xyz;
+ gl_Position = projMatrix * mvMatrix * vertex;
+}
\ No newline at end of file
diff --git a/src/gltffile.cpp b/src/gltffile.cpp
index 807c8cfb..cffed23e 100644
--- a/src/gltffile.cpp
+++ b/src/gltffile.cpp
@@ -157,8 +157,8 @@ GltfFileWriter::GltfFileWriter(MeshResultContext &resultContext,
m_json["meshes"][0]["primitives"][primitiveIndex]["attributes"]["WEIGHTS_0"] = bufferViewIndex + (++attributeIndex);
}
m_json["materials"][primitiveIndex]["pbrMetallicRoughness"]["baseColorTexture"]["index"] = 0;
- m_json["materials"][primitiveIndex]["pbrMetallicRoughness"]["metallicFactor"] = 0.0;
- m_json["materials"][primitiveIndex]["pbrMetallicRoughness"]["roughnessFactor"] = 1.0;
+ m_json["materials"][primitiveIndex]["pbrMetallicRoughness"]["metallicFactor"] = part.second.material.metalness;
+ m_json["materials"][primitiveIndex]["pbrMetallicRoughness"]["roughnessFactor"] = part.second.material.roughness;
primitiveIndex++;
diff --git a/src/main.cpp b/src/main.cpp
index ffe1216a..06dd36d7 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -4,6 +4,7 @@
#include
#include
#include
+#include
#include "skeletondocumentwindow.h"
#include "theme.h"
#include "version.h"
@@ -12,6 +13,10 @@ int main(int argc, char ** argv)
{
QApplication app(argc, argv);
+ QSurfaceFormat format = QSurfaceFormat::defaultFormat();
+ format.setProfile(QSurfaceFormat::OpenGLContextProfile::CompatibilityProfile);
+ QSurfaceFormat::setDefaultFormat(format);
+
// QuantumCD/Qt 5 Dark Fusion Palette
// https://gist.github.com/QuantumCD/6245215
qApp->setStyle(QStyleFactory::create("Fusion"));
diff --git a/src/meshgenerator.cpp b/src/meshgenerator.cpp
index e055fa0f..b51d81ab 100644
--- a/src/meshgenerator.cpp
+++ b/src/meshgenerator.cpp
@@ -251,6 +251,16 @@ void *MeshGenerator::combinePartMesh(QString partId)
if (MeshGenerator::m_enableDebug)
meshlite_bmesh_enable_debug(m_meshliteContext, bmeshId, 1);
+ float metalness = 0.0;
+ QString metalnessString = valueOfKeyInMapOrEmpty(part, "metalness");
+ if (!metalnessString.isEmpty())
+ metalness = metalnessString.toFloat();
+
+ float roughness = 1.0;
+ QString roughnessString = valueOfKeyInMapOrEmpty(part, "roughness");
+ if (!roughnessString.isEmpty())
+ roughness = roughnessString.toFloat();
+
QString mirroredPartId;
QUuid mirroredPartIdNotAsString;
if (xMirrored) {
@@ -291,7 +301,9 @@ void *MeshGenerator::combinePartMesh(QString partId)
bmeshNode.origin = QVector3D(x, y, z);
bmeshNode.radius = radius;
bmeshNode.nodeId = QUuid(nodeId);
- bmeshNode.color = partColor;
+ bmeshNode.material.color = partColor;
+ bmeshNode.material.metalness = metalness;
+ bmeshNode.material.roughness = roughness;
bmeshNode.boneMark = boneMark;
//if (SkeletonBoneMark::None != boneMark)
// bmeshNode.color = SkeletonBoneMarkToColor(boneMark);
@@ -362,7 +374,7 @@ void *MeshGenerator::combinePartMesh(QString partId)
if (m_requirePreviewPartIds.find(partIdNotAsString) != m_requirePreviewPartIds.end()) {
int trimedMeshId = meshlite_trim(m_meshliteContext, meshId, 1);
- m_partPreviewMeshMap[partIdNotAsString] = new MeshLoader(m_meshliteContext, trimedMeshId, -1, partColor, nullptr, m_smoothNormal);
+ m_partPreviewMeshMap[partIdNotAsString] = new MeshLoader(m_meshliteContext, trimedMeshId, -1, {partColor, metalness, roughness}, nullptr, m_smoothNormal);
m_generatedPreviewPartIds.insert(partIdNotAsString);
}
@@ -697,7 +709,7 @@ void MeshGenerator::process()
if (resultMeshId > 0) {
loadGeneratedPositionsToMeshResultContext(m_meshliteContext, triangulatedFinalMeshId);
- m_mesh = new MeshLoader(m_meshliteContext, resultMeshId, triangulatedFinalMeshId, Theme::white, &m_meshResultContext->triangleColors(), m_smoothNormal);
+ m_mesh = new MeshLoader(m_meshliteContext, resultMeshId, triangulatedFinalMeshId, {Theme::white, 0.0, 1.0}, &m_meshResultContext->triangleMaterials(), m_smoothNormal);
}
if (needDeleteCacheContext) {
diff --git a/src/meshloader.cpp b/src/meshloader.cpp
index 886be732..95c9ee2a 100644
--- a/src/meshloader.cpp
+++ b/src/meshloader.cpp
@@ -6,7 +6,7 @@
#define MAX_VERTICES_PER_FACE 100
-MeshLoader::MeshLoader(void *meshlite, int meshId, int triangulatedMeshId, QColor modelColor, const std::vector *triangleColors, bool smoothNormal) :
+MeshLoader::MeshLoader(void *meshlite, int meshId, int triangulatedMeshId, Material material, const std::vector *triangleMaterials, bool smoothNormal) :
m_triangleVertices(nullptr),
m_triangleVertexCount(0),
m_edgeVertices(nullptr),
@@ -68,6 +68,8 @@ MeshLoader::MeshLoader(void *meshlite, int meshId, int triangulatedMeshId, QColo
v->colorB = 0.0;
v->texU = 0.0;
v->texV = 0.0;
+ v->metalness = 0;
+ v->roughness = 1.0;
}
}
@@ -95,9 +97,9 @@ MeshLoader::MeshLoader(void *meshlite, int meshId, int triangulatedMeshId, QColo
GLfloat *triangleNormals = new GLfloat[triangleCount * 3];
int loadedTriangleNormalItemCount = meshlite_get_triangle_normal_array(meshlite, triangleMesh, triangleNormals, triangleCount * 3);
- float modelR = modelColor.redF();
- float modelG = modelColor.greenF();
- float modelB = modelColor.blueF();
+ float modelR = material.color.redF();
+ float modelG = material.color.greenF();
+ float modelB = material.color.blueF();
m_triangleVertexCount = triangleCount * 3;
m_triangleVertices = new Vertex[m_triangleVertexCount * 3];
for (int i = 0; i < triangleCount; i++) {
@@ -105,11 +107,15 @@ MeshLoader::MeshLoader(void *meshlite, int meshId, int triangulatedMeshId, QColo
float useColorR = modelR;
float useColorG = modelG;
float useColorB = modelB;
- if (triangleColors && i < (int)triangleColors->size()) {
- QColor triangleColor = (*triangleColors)[i];
- useColorR = triangleColor.redF();
- useColorG = triangleColor.greenF();
- useColorB = triangleColor.blueF();
+ float useMetalness = material.metalness;
+ float useRoughness = material.roughness;
+ if (triangleMaterials && i < (int)triangleMaterials->size()) {
+ auto triangleMaterial = (*triangleMaterials)[i];
+ useColorR = triangleMaterial.color.redF();
+ useColorG = triangleMaterial.color.greenF();
+ useColorB = triangleMaterial.color.blueF();
+ useMetalness = triangleMaterial.metalness;
+ useRoughness = triangleMaterial.roughness;
}
TriangulatedFace triangulatedFace;
triangulatedFace.color.setRedF(useColorR);
@@ -136,6 +142,8 @@ MeshLoader::MeshLoader(void *meshlite, int meshId, int triangulatedMeshId, QColo
v->colorR = useColorR;
v->colorG = useColorG;
v->colorB = useColorB;
+ v->metalness = useMetalness;
+ v->roughness = useRoughness;
}
m_triangulatedFaces.push_back(triangulatedFace);
}
@@ -226,6 +234,7 @@ MeshLoader::MeshLoader(MeshResultContext &resultContext) :
const ResultVertex *srcVert = &part.second.vertices[vertexIndex];
const QVector3D *srcNormal = &part.second.interpolatedVertexNormals[vertexIndex];
const ResultVertexUv *srcUv = &part.second.vertexUvs[vertexIndex];
+ const Material *srcMaterial = &part.second.material;
Vertex *dest = &m_triangleVertices[destIndex];
dest->colorR = 0;
dest->colorG = 0;
@@ -238,6 +247,8 @@ MeshLoader::MeshLoader(MeshResultContext &resultContext) :
dest->normX = srcNormal->x();
dest->normY = srcNormal->y();
dest->normZ = srcNormal->z();
+ dest->metalness = srcMaterial->metalness;
+ dest->roughness = srcMaterial->roughness;
destIndex++;
}
}
diff --git a/src/meshloader.h b/src/meshloader.h
index 71a166f5..ed364ceb 100644
--- a/src/meshloader.h
+++ b/src/meshloader.h
@@ -26,6 +26,8 @@ typedef struct
GLfloat colorB;
GLfloat texU;
GLfloat texV;
+ GLfloat metalness;
+ GLfloat roughness;
} Vertex;
#pragma pack(pop)
@@ -38,7 +40,7 @@ struct TriangulatedFace
class MeshLoader
{
public:
- MeshLoader(void *meshlite, int meshId, int triangulatedMeshId=-1, QColor modelColor=Theme::white, const std::vector *triangleColors=nullptr, bool smoothNormal=true);
+ MeshLoader(void *meshlite, int meshId, int triangulatedMeshId=-1, Material material={Theme::white, 0.0, 1.0}, const std::vector *triangleMaterials=nullptr, bool smoothNormal=true);
MeshLoader(MeshResultContext &resultContext);
MeshLoader(Vertex *triangleVertices, int vertexNum);
MeshLoader(const MeshLoader &mesh);
diff --git a/src/meshresultcontext.cpp b/src/meshresultcontext.cpp
index 76de8231..850b6430 100644
--- a/src/meshresultcontext.cpp
+++ b/src/meshresultcontext.cpp
@@ -26,7 +26,7 @@ struct CandidateEdge
MeshResultContext::MeshResultContext() :
m_triangleSourceResolved(false),
- m_triangleColorResolved(false),
+ m_triangleMaterialResolved(false),
m_triangleEdgeSourceMapResolved(false),
m_bmeshNodeMapResolved(false),
m_resultPartsResolved(false),
@@ -56,13 +56,13 @@ const std::map> &MeshResultContext::vertexSourceMap
return m_vertexSourceMap;
}
-const std::vector &MeshResultContext::triangleColors()
+const std::vector &MeshResultContext::triangleMaterials()
{
- if (!m_triangleColorResolved) {
- calculateTriangleColors(m_triangleColors);
- m_triangleColorResolved = true;
+ if (!m_triangleMaterialResolved) {
+ calculateTriangleMaterials(m_triangleMaterials);
+ m_triangleMaterialResolved = true;
}
- return m_triangleColors;
+ return m_triangleMaterials;
}
const std::map, std::pair> &MeshResultContext::triangleEdgeSourceMap()
@@ -257,15 +257,15 @@ void MeshResultContext::calculateRemainingVertexSourceNodesAfterTriangleSourceNo
}
}
-void MeshResultContext::calculateTriangleColors(std::vector &triangleColors)
+void MeshResultContext::calculateTriangleMaterials(std::vector &triangleMaterials)
{
- std::map, QColor> nodeColorMap;
+ std::map, Material> nodeMaterialMap;
for (const auto &it: bmeshNodes) {
- nodeColorMap[std::make_pair(it.partId, it.nodeId)] = it.color;
+ nodeMaterialMap[std::make_pair(it.partId, it.nodeId)] = it.material;
}
const auto sourceNodes = triangleSourceNodes();
for (const auto &it: sourceNodes) {
- triangleColors.push_back(nodeColorMap[it]);
+ triangleMaterials.push_back(nodeMaterialMap[it]);
}
}
@@ -322,7 +322,7 @@ void MeshResultContext::calculateResultParts(std::map &parts)
auto it = parts.find(sourceNode.first);
if (it == parts.end()) {
ResultPart newPart;
- newPart.color = triangleColors()[x];
+ newPart.material = triangleMaterials()[x];
parts.insert(std::make_pair(sourceNode.first, newPart));
}
auto &resultPart = parts[sourceNode.first];
diff --git a/src/meshresultcontext.h b/src/meshresultcontext.h
index db345328..acf1358b 100644
--- a/src/meshresultcontext.h
+++ b/src/meshresultcontext.h
@@ -10,13 +10,20 @@
#define MAX_WEIGHT_NUM 4
+struct Material
+{
+ QColor color;
+ float metalness;
+ float roughness;
+};
+
struct BmeshNode
{
QUuid partId;
QUuid nodeId;
QVector3D origin;
float radius = 0;
- QColor color;
+ Material material;
SkeletonBoneMark boneMark;
};
@@ -51,7 +58,7 @@ struct ResultVertexUv
struct ResultPart
{
- QColor color;
+ Material material;
std::vector vertices;
std::vector verticesOldIndicies;
std::vector interpolatedVertexNormals;
@@ -83,7 +90,7 @@ public:
MeshResultContext();
public:
const std::vector> &triangleSourceNodes();
- const std::vector &triangleColors();
+ const std::vector &triangleMaterials();
const std::map, std::pair> &triangleEdgeSourceMap();
const std::map, BmeshNode *> &bmeshNodeMap();
const std::map &parts();
@@ -94,7 +101,7 @@ public:
const std::vector &interpolatedVertexNormals();
private:
bool m_triangleSourceResolved;
- bool m_triangleColorResolved;
+ bool m_triangleMaterialResolved;
bool m_triangleEdgeSourceMapResolved;
bool m_bmeshNodeMapResolved;
bool m_resultPartsResolved;
@@ -103,7 +110,7 @@ private:
bool m_vertexNormalsInterpolated;
private:
std::vector> m_triangleSourceNodes;
- std::vector m_triangleColors;
+ std::vector m_triangleMaterials;
std::map, std::pair> m_triangleEdgeSourceMap;
std::map, BmeshNode *> m_bmeshNodeMap;
std::map m_resultParts;
@@ -117,7 +124,7 @@ private:
private:
void calculateTriangleSourceNodes(std::vector> &triangleSourceNodes, std::map> &vertexSourceMap);
void calculateRemainingVertexSourceNodesAfterTriangleSourceNodesSolved(std::map> &vertexSourceMap);
- void calculateTriangleColors(std::vector &triangleColors);
+ void calculateTriangleMaterials(std::vector &triangleMaterials);
void calculateTriangleEdgeSourceMap(std::map, std::pair> &triangleEdgeSourceMap);
void calculateBmeshNodeMap(std::map, BmeshNode *> &bmeshNodeMap);
void calculateResultParts(std::map &parts);
diff --git a/src/modelmeshbinder.cpp b/src/modelmeshbinder.cpp
index 2b025ac6..c8bb8383 100644
--- a/src/modelmeshbinder.cpp
+++ b/src/modelmeshbinder.cpp
@@ -152,10 +152,14 @@ void ModelMeshBinder::paint(ModelShaderProgram *program)
f->glEnableVertexAttribArray(1);
f->glEnableVertexAttribArray(2);
f->glEnableVertexAttribArray(3);
- f->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 11 * sizeof(GLfloat), 0);
- f->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 11 * sizeof(GLfloat), reinterpret_cast(3 * sizeof(GLfloat)));
- f->glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 11 * sizeof(GLfloat), reinterpret_cast(6 * sizeof(GLfloat)));
- f->glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, 11 * sizeof(GLfloat), reinterpret_cast(9 * sizeof(GLfloat)));
+ f->glEnableVertexAttribArray(4);
+ f->glEnableVertexAttribArray(5);
+ f->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
+ f->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(3 * sizeof(GLfloat)));
+ f->glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(6 * sizeof(GLfloat)));
+ f->glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(9 * sizeof(GLfloat)));
+ f->glVertexAttribPointer(4, 1, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(11 * sizeof(GLfloat)));
+ f->glVertexAttribPointer(5, 1, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(12 * sizeof(GLfloat)));
m_vboTriangle.release();
}
{
@@ -171,10 +175,14 @@ void ModelMeshBinder::paint(ModelShaderProgram *program)
f->glEnableVertexAttribArray(1);
f->glEnableVertexAttribArray(2);
f->glEnableVertexAttribArray(3);
- f->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 11 * sizeof(GLfloat), 0);
- f->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 11 * sizeof(GLfloat), reinterpret_cast(3 * sizeof(GLfloat)));
- f->glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 11 * sizeof(GLfloat), reinterpret_cast(6 * sizeof(GLfloat)));
- f->glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, 11 * sizeof(GLfloat), reinterpret_cast(9 * sizeof(GLfloat)));
+ f->glEnableVertexAttribArray(4);
+ f->glEnableVertexAttribArray(5);
+ f->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
+ f->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(3 * sizeof(GLfloat)));
+ f->glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(6 * sizeof(GLfloat)));
+ f->glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(9 * sizeof(GLfloat)));
+ f->glVertexAttribPointer(4, 1, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(11 * sizeof(GLfloat)));
+ f->glVertexAttribPointer(5, 1, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(12 * sizeof(GLfloat)));
m_vboEdge.release();
}
} else {
diff --git a/src/modelshaderprogram.cpp b/src/modelshaderprogram.cpp
index cd4a161b..b2ed2062 100644
--- a/src/modelshaderprogram.cpp
+++ b/src/modelshaderprogram.cpp
@@ -17,19 +17,26 @@ const QString &ModelShaderProgram::loadShaderSource(const QString &name)
return insertResult.first->second;
}
-ModelShaderProgram::ModelShaderProgram()
+ModelShaderProgram::ModelShaderProgram(bool usePBR)
{
if (QSurfaceFormat::defaultFormat().profile() == QSurfaceFormat::CoreProfile) {
this->addShaderFromSourceCode(QOpenGLShader::Vertex, loadShaderSource(":/shaders/default.core.vert"));
this->addShaderFromSourceCode(QOpenGLShader::Fragment, loadShaderSource(":/shaders/default.core.frag"));
} else {
- this->addShaderFromSourceCode(QOpenGLShader::Vertex, loadShaderSource(":/shaders/default.vert"));
- this->addShaderFromSourceCode(QOpenGLShader::Fragment, loadShaderSource(":/shaders/default.frag"));
+ if (usePBR) {
+ this->addShaderFromSourceCode(QOpenGLShader::Vertex, loadShaderSource(":/shaders/pbr.vert"));
+ this->addShaderFromSourceCode(QOpenGLShader::Fragment, loadShaderSource(":/shaders/pbr.frag"));
+ } else {
+ this->addShaderFromSourceCode(QOpenGLShader::Vertex, loadShaderSource(":/shaders/default.vert"));
+ this->addShaderFromSourceCode(QOpenGLShader::Fragment, loadShaderSource(":/shaders/default.frag"));
+ }
}
this->bindAttributeLocation("vertex", 0);
this->bindAttributeLocation("normal", 1);
this->bindAttributeLocation("color", 2);
this->bindAttributeLocation("texCoord", 3);
+ this->bindAttributeLocation("metalness", 4);
+ this->bindAttributeLocation("roughness", 5);
this->link();
this->bind();
diff --git a/src/modelshaderprogram.h b/src/modelshaderprogram.h
index 11c3a6d2..eb7da3f8 100644
--- a/src/modelshaderprogram.h
+++ b/src/modelshaderprogram.h
@@ -6,7 +6,7 @@
class ModelShaderProgram : public QOpenGLShaderProgram
{
public:
- ModelShaderProgram();
+ ModelShaderProgram(bool usePBR=false);
int projMatrixLoc();
int mvMatrixLoc();
int normalMatrixLoc();
diff --git a/src/skeletondocument.cpp b/src/skeletondocument.cpp
index ba33da4f..e91593c5 100644
--- a/src/skeletondocument.cpp
+++ b/src/skeletondocument.cpp
@@ -874,6 +874,10 @@ void SkeletonDocument::toSnapshot(SkeletonSnapshot *snapshot, const std::setparts[part["id"]] = part;
}
for (const auto &nodeIt: nodeMap) {
@@ -1071,6 +1075,12 @@ void SkeletonDocument::addFromSnapshot(const SkeletonSnapshot &snapshot, bool fr
const auto &deformWidthIt = partKv.second.find("deformWidth");
if (deformWidthIt != partKv.second.end())
part.setDeformWidth(deformWidthIt->second.toFloat());
+ const auto &metalnessIt = partKv.second.find("metalness");
+ if (metalnessIt != partKv.second.end())
+ part.metalness = metalnessIt->second.toFloat();
+ const auto &roughnessIt = partKv.second.find("roughness");
+ if (roughnessIt != partKv.second.end())
+ part.roughness = roughnessIt->second.toFloat();
newAddedPartIds.insert(part.id);
}
for (const auto &nodeKv: snapshot.nodes) {
@@ -2145,6 +2155,32 @@ void SkeletonDocument::setPartDeformWidth(QUuid partId, float width)
emit skeletonChanged();
}
+void SkeletonDocument::setPartMetalness(QUuid partId, float metalness)
+{
+ auto part = partMap.find(partId);
+ if (part == partMap.end()) {
+ qDebug() << "Part not found:" << partId;
+ return;
+ }
+ part->second.metalness = metalness;
+ part->second.dirty = true;
+ emit partMetalnessChanged(partId);
+ emit skeletonChanged();
+}
+
+void SkeletonDocument::setPartRoughness(QUuid partId, float roughness)
+{
+ auto part = partMap.find(partId);
+ if (part == partMap.end()) {
+ qDebug() << "Part not found:" << partId;
+ return;
+ }
+ part->second.roughness = roughness;
+ part->second.dirty = true;
+ emit partRoughnessChanged(partId);
+ emit skeletonChanged();
+}
+
void SkeletonDocument::setPartRoundState(QUuid partId, bool rounded)
{
auto part = partMap.find(partId);
diff --git a/src/skeletondocument.h b/src/skeletondocument.h
index 599fabd6..26532ad9 100644
--- a/src/skeletondocument.h
+++ b/src/skeletondocument.h
@@ -97,6 +97,8 @@ public:
std::vector nodeIds;
bool dirty;
bool wrapped;
+ float metalness;
+ float roughness;
SkeletonPart(const QUuid &withId=QUuid()) :
visible(true),
locked(false),
@@ -110,7 +112,9 @@ public:
color(Theme::white),
hasColor(false),
dirty(true),
- wrapped(false)
+ wrapped(false),
+ metalness(0.0),
+ roughness(1.0)
{
id = withId.isNull() ? QUuid::createUuid() : withId;
}
@@ -142,6 +146,18 @@ public:
{
return deformThicknessAdjusted() || deformWidthAdjusted();
}
+ bool metalnessAdjusted() const
+ {
+ return fabs(metalness - 0.0) >= 0.01;
+ }
+ bool roughnessAdjusted() const
+ {
+ return fabs(roughness - 1.0) >= 0.01;
+ }
+ bool materialAdjusted() const
+ {
+ return metalnessAdjusted() || roughnessAdjusted();
+ }
bool isEditVisible() const
{
return visible && !disabled;
@@ -162,6 +178,8 @@ public:
wrapped = other.wrapped;
componentId = other.componentId;
dirty = other.dirty;
+ metalness = other.metalness;
+ roughness = other.roughness;
}
void updatePreviewMesh(MeshLoader *previewMesh)
{
@@ -444,6 +462,8 @@ signals:
void partRoundStateChanged(QUuid partId);
void partColorStateChanged(QUuid partId);
void partWrapStateChanged(QUuid partId);
+ void partMetalnessChanged(QUuid partId);
+ void partRoughnessChanged(QUuid partId);
void componentInverseStateChanged(QUuid partId);
void cleanup();
void originChanged();
@@ -583,6 +603,8 @@ public slots:
void setPartRoundState(QUuid partId, bool rounded);
void setPartColorState(QUuid partId, bool hasColor, QColor color);
void setPartWrapState(QUuid partId, bool wrapped);
+ void setPartMetalness(QUuid partId, float metalness);
+ void setPartRoughness(QUuid partId, float roughness);
void setComponentInverseState(QUuid componentId, bool inverse);
void moveComponentUp(QUuid componentId);
void moveComponentDown(QUuid componentId);
diff --git a/src/skeletondocumentwindow.cpp b/src/skeletondocumentwindow.cpp
index 683183c7..a41941b4 100644
--- a/src/skeletondocumentwindow.cpp
+++ b/src/skeletondocumentwindow.cpp
@@ -760,6 +760,8 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() :
connect(m_document, &SkeletonDocument::partRoundStateChanged, partTreeWidget, &SkeletonPartTreeWidget::partRoundStateChanged);
connect(m_document, &SkeletonDocument::partWrapStateChanged, partTreeWidget, &SkeletonPartTreeWidget::partWrapStateChanged);
connect(m_document, &SkeletonDocument::partColorStateChanged, partTreeWidget, &SkeletonPartTreeWidget::partColorStateChanged);
+ connect(m_document, &SkeletonDocument::partMetalnessChanged, partTreeWidget, &SkeletonPartTreeWidget::partMetalnessChanged);
+ connect(m_document, &SkeletonDocument::partRoughnessChanged, partTreeWidget, &SkeletonPartTreeWidget::partRoughnessChanged);
connect(m_document, &SkeletonDocument::partRemoved, partTreeWidget, &SkeletonPartTreeWidget::partRemoved);
connect(m_document, &SkeletonDocument::cleanup, partTreeWidget, &SkeletonPartTreeWidget::removeAllContent);
connect(m_document, &SkeletonDocument::partChecked, partTreeWidget, &SkeletonPartTreeWidget::partChecked);
diff --git a/src/skeletonparttreewidget.cpp b/src/skeletonparttreewidget.cpp
index 7b40e6d1..05977b15 100644
--- a/src/skeletonparttreewidget.cpp
+++ b/src/skeletonparttreewidget.cpp
@@ -932,6 +932,28 @@ void SkeletonPartTreeWidget::partColorStateChanged(QUuid partId)
widget->updateColorButton();
}
+void SkeletonPartTreeWidget::partMetalnessChanged(QUuid partId)
+{
+ auto item = m_partItemMap.find(partId);
+ if (item == m_partItemMap.end()) {
+ qDebug() << "Part item not found:" << partId;
+ return;
+ }
+ SkeletonPartWidget *widget = (SkeletonPartWidget *)itemWidget(item->second, 0);
+ widget->updateColorButton();
+}
+
+void SkeletonPartTreeWidget::partRoughnessChanged(QUuid partId)
+{
+ auto item = m_partItemMap.find(partId);
+ if (item == m_partItemMap.end()) {
+ qDebug() << "Part item not found:" << partId;
+ return;
+ }
+ SkeletonPartWidget *widget = (SkeletonPartWidget *)itemWidget(item->second, 0);
+ widget->updateColorButton();
+}
+
void SkeletonPartTreeWidget::partChecked(QUuid partId)
{
auto item = m_partItemMap.find(partId);
diff --git a/src/skeletonparttreewidget.h b/src/skeletonparttreewidget.h
index 1fb8c6e0..73d7736f 100644
--- a/src/skeletonparttreewidget.h
+++ b/src/skeletonparttreewidget.h
@@ -62,6 +62,8 @@ public slots:
void partRoundStateChanged(QUuid partId);
void partWrapStateChanged(QUuid partId);
void partColorStateChanged(QUuid partId);
+ void partMetalnessChanged(QUuid partId);
+ void partRoughnessChanged(QUuid partId);
void partChecked(QUuid partId);
void partUnchecked(QUuid partId);
void groupChanged(QTreeWidgetItem *item, int column);
diff --git a/src/skeletonpartwidget.cpp b/src/skeletonpartwidget.cpp
index c8b3f2bf..8a2f7319 100644
--- a/src/skeletonpartwidget.cpp
+++ b/src/skeletonpartwidget.cpp
@@ -131,6 +131,8 @@ SkeletonPartWidget::SkeletonPartWidget(const SkeletonDocument *document, QUuid p
connect(this, &SkeletonPartWidget::setPartRoundState, m_document, &SkeletonDocument::setPartRoundState);
connect(this, &SkeletonPartWidget::setPartWrapState, m_document, &SkeletonDocument::setPartWrapState);
connect(this, &SkeletonPartWidget::setPartColorState, m_document, &SkeletonDocument::setPartColorState);
+ connect(this, &SkeletonPartWidget::setPartMetalness, m_document, &SkeletonDocument::setPartMetalness);
+ connect(this, &SkeletonPartWidget::setPartRoughness, m_document, &SkeletonDocument::setPartRoughness);
connect(this, &SkeletonPartWidget::checkPart, m_document, &SkeletonDocument::checkPart);
connect(this, &SkeletonPartWidget::enableBackgroundBlur, m_document, &SkeletonDocument::enableBackgroundBlur);
connect(this, &SkeletonPartWidget::disableBackgroundBlur, m_document, &SkeletonDocument::disableBackgroundBlur);
@@ -301,9 +303,10 @@ void SkeletonPartWidget::showColorSettingPopup(const QPoint &pos)
palette.setColor(QPalette::Button, choosenColor);
pickButton->setPalette(palette);
- QHBoxLayout *layout = new QHBoxLayout;
- layout->addWidget(colorEraser);
- layout->addWidget(pickButton);
+ QHBoxLayout *colorLayout = new QHBoxLayout;
+ colorLayout->addWidget(colorEraser);
+ colorLayout->addWidget(pickButton);
+ colorLayout->addStretch();
connect(colorEraser, &QPushButton::clicked, [=]() {
emit setPartColorState(m_partId, false, Theme::white);
@@ -320,7 +323,55 @@ void SkeletonPartWidget::showColorSettingPopup(const QPoint &pos)
}
});
- popup->setLayout(layout);
+ FloatNumberWidget *metalnessWidget = new FloatNumberWidget;
+ metalnessWidget->setItemName(tr("Metalness"));
+ metalnessWidget->setRange(0, 1);
+ metalnessWidget->setValue(part->metalness);
+
+ connect(metalnessWidget, &FloatNumberWidget::valueChanged, [=](float value) {
+ emit setPartMetalness(m_partId, value);
+ emit groupOperationAdded();
+ });
+
+ FloatNumberWidget *roughnessWidget = new FloatNumberWidget;
+ roughnessWidget->setItemName(tr("Roughness"));
+ roughnessWidget->setRange(0, 1);
+ roughnessWidget->setValue(part->roughness);
+
+ connect(roughnessWidget, &FloatNumberWidget::valueChanged, [=](float value) {
+ emit setPartRoughness(m_partId, value);
+ emit groupOperationAdded();
+ });
+
+ QPushButton *metalnessEraser = new QPushButton(QChar(fa::eraser));
+ initToolButton(metalnessEraser);
+
+ connect(metalnessEraser, &QPushButton::clicked, [=]() {
+ metalnessWidget->setValue(0.0);
+ emit groupOperationAdded();
+ });
+
+ QPushButton *roughnessEraser = new QPushButton(QChar(fa::eraser));
+ initToolButton(roughnessEraser);
+
+ connect(roughnessEraser, &QPushButton::clicked, [=]() {
+ roughnessWidget->setValue(1.0);
+ emit groupOperationAdded();
+ });
+
+ QHBoxLayout *metalnessLayout = new QHBoxLayout;
+ QHBoxLayout *roughnessLayout = new QHBoxLayout;
+ metalnessLayout->addWidget(metalnessEraser);
+ metalnessLayout->addWidget(metalnessWidget);
+ roughnessLayout->addWidget(roughnessEraser);
+ roughnessLayout->addWidget(roughnessWidget);
+
+ QVBoxLayout *mainLayout = new QVBoxLayout;
+ mainLayout->addLayout(colorLayout);
+ mainLayout->addLayout(metalnessLayout);
+ mainLayout->addLayout(roughnessLayout);
+
+ popup->setLayout(mainLayout);
QWidgetAction *action = new QWidgetAction(this);
action->setDefaultWidget(popup);
@@ -518,7 +569,7 @@ void SkeletonPartWidget::updateColorButton()
qDebug() << "Part not found:" << m_partId;
return;
}
- if (part->hasColor)
+ if (part->hasColor || part->materialAdjusted())
updateButton(m_colorButton, QChar(fa::eyedropper), true);
else
updateButton(m_colorButton, QChar(fa::eyedropper), false);
diff --git a/src/skeletonpartwidget.h b/src/skeletonpartwidget.h
index 589beae6..f227c3cd 100644
--- a/src/skeletonpartwidget.h
+++ b/src/skeletonpartwidget.h
@@ -21,6 +21,8 @@ signals:
void setPartRoundState(QUuid partId, bool rounded);
void setPartColorState(QUuid partId, bool hasColor, QColor color);
void setPartWrapState(QUuid partId, bool wrapped);
+ void setPartMetalness(QUuid partId, float metalness);
+ void setPartRoughness(QUuid partId, float roughness);
void movePartUp(QUuid partId);
void movePartDown(QUuid partId);
void movePartToTop(QUuid partId);
diff --git a/src/skinnedmeshcreator.cpp b/src/skinnedmeshcreator.cpp
index 01424718..a38555cb 100644
--- a/src/skinnedmeshcreator.cpp
+++ b/src/skinnedmeshcreator.cpp
@@ -45,11 +45,11 @@ MeshLoader *SkinnedMeshCreator::createMeshFromTransform(const std::vector &triangleColors = m_resultContext->triangleColors();
+ const std::vector &triangleMaterials = m_resultContext->triangleMaterials();
const std::vector &triangleUvs = m_resultContext->triangleUvs();
m_resultTextureColorImage = new QImage(TextureGenerator::m_textureWidth, TextureGenerator::m_textureHeight, QImage::Format_ARGB32);
@@ -105,10 +105,10 @@ void TextureGenerator::process()
path.lineTo(uv->uv[j][0] * TextureGenerator::m_textureWidth, uv->uv[j][1] * TextureGenerator::m_textureHeight);
}
}
- QPen textureBorderPen(triangleColors[i]);
+ QPen textureBorderPen(triangleMaterials[i].color);
textureBorderPen.setWidth(32);
texturePainter.setPen(textureBorderPen);
- texturePainter.setBrush(QBrush(triangleColors[i]));
+ texturePainter.setBrush(QBrush(triangleMaterials[i].color));
texturePainter.drawPath(path);
}
// round 2, real paint
@@ -123,7 +123,7 @@ void TextureGenerator::process()
path.lineTo(uv->uv[j][0] * TextureGenerator::m_textureWidth, uv->uv[j][1] * TextureGenerator::m_textureHeight);
}
}
- texturePainter.fillPath(path, QBrush(triangleColors[i]));
+ texturePainter.fillPath(path, QBrush(triangleMaterials[i].color));
}
pen.setWidth(0);