Add PBR support
Now can configure metalness and roughness for parts, however, the PBR render isn't enabled in the code currently.master
parent
5c25d7d4f8
commit
f7b5fb1c6a
|
@ -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
|
||||
</pre>
|
||||
|
||||
<h1>Qt3D</h1>
|
||||
<pre>
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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$
|
||||
**
|
||||
****************************************************************************/
|
||||
</pre>
|
|
@ -10,6 +10,8 @@
|
|||
<file>shaders/default.frag</file>
|
||||
<file>shaders/default.core.vert</file>
|
||||
<file>shaders/default.core.frag</file>
|
||||
<file>shaders/pbr.vert</file>
|
||||
<file>shaders/pbr.frag</file>
|
||||
<file>ACKNOWLEDGEMENTS.html</file>
|
||||
<file>AUTHORS</file>
|
||||
<file>CONTRIBUTORS</file>
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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++;
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <QFontDatabase>
|
||||
#include <QDebug>
|
||||
#include <QtGlobal>
|
||||
#include <QSurfaceFormat>
|
||||
#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"));
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
#define MAX_VERTICES_PER_FACE 100
|
||||
|
||||
MeshLoader::MeshLoader(void *meshlite, int meshId, int triangulatedMeshId, QColor modelColor, const std::vector<QColor> *triangleColors, bool smoothNormal) :
|
||||
MeshLoader::MeshLoader(void *meshlite, int meshId, int triangulatedMeshId, Material material, const std::vector<Material> *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++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<QColor> *triangleColors=nullptr, bool smoothNormal=true);
|
||||
MeshLoader(void *meshlite, int meshId, int triangulatedMeshId=-1, Material material={Theme::white, 0.0, 1.0}, const std::vector<Material> *triangleMaterials=nullptr, bool smoothNormal=true);
|
||||
MeshLoader(MeshResultContext &resultContext);
|
||||
MeshLoader(Vertex *triangleVertices, int vertexNum);
|
||||
MeshLoader(const MeshLoader &mesh);
|
||||
|
|
|
@ -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<int, std::pair<QUuid, QUuid>> &MeshResultContext::vertexSourceMap
|
|||
return m_vertexSourceMap;
|
||||
}
|
||||
|
||||
const std::vector<QColor> &MeshResultContext::triangleColors()
|
||||
const std::vector<Material> &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<int, int>, std::pair<QUuid, QUuid>> &MeshResultContext::triangleEdgeSourceMap()
|
||||
|
@ -257,15 +257,15 @@ void MeshResultContext::calculateRemainingVertexSourceNodesAfterTriangleSourceNo
|
|||
}
|
||||
}
|
||||
|
||||
void MeshResultContext::calculateTriangleColors(std::vector<QColor> &triangleColors)
|
||||
void MeshResultContext::calculateTriangleMaterials(std::vector<Material> &triangleMaterials)
|
||||
{
|
||||
std::map<std::pair<QUuid, QUuid>, QColor> nodeColorMap;
|
||||
std::map<std::pair<QUuid, QUuid>, 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<QUuid, ResultPart> &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];
|
||||
|
|
|
@ -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<ResultVertex> vertices;
|
||||
std::vector<int> verticesOldIndicies;
|
||||
std::vector<QVector3D> interpolatedVertexNormals;
|
||||
|
@ -83,7 +90,7 @@ public:
|
|||
MeshResultContext();
|
||||
public:
|
||||
const std::vector<std::pair<QUuid, QUuid>> &triangleSourceNodes();
|
||||
const std::vector<QColor> &triangleColors();
|
||||
const std::vector<Material> &triangleMaterials();
|
||||
const std::map<std::pair<int, int>, std::pair<QUuid, QUuid>> &triangleEdgeSourceMap();
|
||||
const std::map<std::pair<QUuid, QUuid>, BmeshNode *> &bmeshNodeMap();
|
||||
const std::map<QUuid, ResultPart> &parts();
|
||||
|
@ -94,7 +101,7 @@ public:
|
|||
const std::vector<QVector3D> &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<std::pair<QUuid, QUuid>> m_triangleSourceNodes;
|
||||
std::vector<QColor> m_triangleColors;
|
||||
std::vector<Material> m_triangleMaterials;
|
||||
std::map<std::pair<int, int>, std::pair<QUuid, QUuid>> m_triangleEdgeSourceMap;
|
||||
std::map<std::pair<QUuid, QUuid>, BmeshNode *> m_bmeshNodeMap;
|
||||
std::map<QUuid, ResultPart> m_resultParts;
|
||||
|
@ -117,7 +124,7 @@ private:
|
|||
private:
|
||||
void calculateTriangleSourceNodes(std::vector<std::pair<QUuid, QUuid>> &triangleSourceNodes, std::map<int, std::pair<QUuid, QUuid>> &vertexSourceMap);
|
||||
void calculateRemainingVertexSourceNodesAfterTriangleSourceNodesSolved(std::map<int, std::pair<QUuid, QUuid>> &vertexSourceMap);
|
||||
void calculateTriangleColors(std::vector<QColor> &triangleColors);
|
||||
void calculateTriangleMaterials(std::vector<Material> &triangleMaterials);
|
||||
void calculateTriangleEdgeSourceMap(std::map<std::pair<int, int>, std::pair<QUuid, QUuid>> &triangleEdgeSourceMap);
|
||||
void calculateBmeshNodeMap(std::map<std::pair<QUuid, QUuid>, BmeshNode *> &bmeshNodeMap);
|
||||
void calculateResultParts(std::map<QUuid, ResultPart> &parts);
|
||||
|
|
|
@ -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<void *>(3 * sizeof(GLfloat)));
|
||||
f->glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 11 * sizeof(GLfloat), reinterpret_cast<void *>(6 * sizeof(GLfloat)));
|
||||
f->glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, 11 * sizeof(GLfloat), reinterpret_cast<void *>(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<void *>(3 * sizeof(GLfloat)));
|
||||
f->glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void *>(6 * sizeof(GLfloat)));
|
||||
f->glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void *>(9 * sizeof(GLfloat)));
|
||||
f->glVertexAttribPointer(4, 1, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void *>(11 * sizeof(GLfloat)));
|
||||
f->glVertexAttribPointer(5, 1, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void *>(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<void *>(3 * sizeof(GLfloat)));
|
||||
f->glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 11 * sizeof(GLfloat), reinterpret_cast<void *>(6 * sizeof(GLfloat)));
|
||||
f->glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, 11 * sizeof(GLfloat), reinterpret_cast<void *>(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<void *>(3 * sizeof(GLfloat)));
|
||||
f->glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void *>(6 * sizeof(GLfloat)));
|
||||
f->glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void *>(9 * sizeof(GLfloat)));
|
||||
f->glVertexAttribPointer(4, 1, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void *>(11 * sizeof(GLfloat)));
|
||||
f->glVertexAttribPointer(5, 1, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void *>(12 * sizeof(GLfloat)));
|
||||
m_vboEdge.release();
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -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 {
|
||||
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();
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
class ModelShaderProgram : public QOpenGLShaderProgram
|
||||
{
|
||||
public:
|
||||
ModelShaderProgram();
|
||||
ModelShaderProgram(bool usePBR=false);
|
||||
int projMatrixLoc();
|
||||
int mvMatrixLoc();
|
||||
int normalMatrixLoc();
|
||||
|
|
|
@ -874,6 +874,10 @@ void SkeletonDocument::toSnapshot(SkeletonSnapshot *snapshot, const std::set<QUu
|
|||
part["deformWidth"] = QString::number(partIt.second.deformWidth);
|
||||
if (!partIt.second.name.isEmpty())
|
||||
part["name"] = partIt.second.name;
|
||||
if (partIt.second.metalnessAdjusted())
|
||||
part["metalness"] = QString::number(partIt.second.metalness);
|
||||
if (partIt.second.roughnessAdjusted())
|
||||
part["roughness"] = QString::number(partIt.second.roughness);
|
||||
snapshot->parts[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);
|
||||
|
|
|
@ -97,6 +97,8 @@ public:
|
|||
std::vector<QUuid> 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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -45,11 +45,11 @@ MeshLoader *SkinnedMeshCreator::createMeshFromTransform(const std::vector<QMatri
|
|||
int triangleVerticesNum = 0;
|
||||
for (size_t triangleIndex = 0; triangleIndex < m_meshResultContext.triangles.size(); triangleIndex++) {
|
||||
const auto &sourceTriangle = m_meshResultContext.triangles[triangleIndex];
|
||||
QColor triangleColor = m_meshResultContext.triangleColors()[triangleIndex];
|
||||
Material triangleMaterial = m_meshResultContext.triangleMaterials()[triangleIndex];
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Vertex ¤tVertex = triangleVertices[triangleVerticesNum++];
|
||||
const auto &sourcePosition = transformedPositions[sourceTriangle.indicies[i]];
|
||||
const auto &sourceColor = triangleColor;
|
||||
const auto &sourceColor = triangleMaterial.color;
|
||||
const auto &sourceNormal = transformedPoseNormals[sourceTriangle.indicies[i]];
|
||||
currentVertex.posX = sourcePosition.x();
|
||||
currentVertex.posY = sourcePosition.y();
|
||||
|
@ -62,6 +62,8 @@ MeshLoader *SkinnedMeshCreator::createMeshFromTransform(const std::vector<QMatri
|
|||
currentVertex.normX = sourceNormal.x();
|
||||
currentVertex.normY = sourceNormal.y();
|
||||
currentVertex.normZ = sourceNormal.z();
|
||||
currentVertex.metalness = triangleMaterial.metalness;
|
||||
currentVertex.roughness = triangleMaterial.roughness;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ MeshLoader *TextureGenerator::takeResultMesh()
|
|||
|
||||
void TextureGenerator::process()
|
||||
{
|
||||
const std::vector<QColor> &triangleColors = m_resultContext->triangleColors();
|
||||
const std::vector<Material> &triangleMaterials = m_resultContext->triangleMaterials();
|
||||
const std::vector<ResultTriangleUv> &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);
|
||||
|
|
Loading…
Reference in New Issue