Fix IBL shader
Implement DDS cubemap file reader to load the Cedar Bridge environment cubemap file, and render IBL on core profile.master
parent
81a95d6cad
commit
c84bb14ada
|
@ -1296,4 +1296,9 @@ https://www.reddit.com/r/gamedev/comments/5iuf3h/i_am_writting_a_3d_monster_mode
|
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
</pre>
|
||||
|
||||
<h1>Greg Zaal</h1>
|
||||
<pre>
|
||||
https://hdrihaven.com/hdri/?h=cedar_bridge
|
||||
</pre>
|
|
@ -505,6 +505,9 @@ HEADERS += src/projectfacestonodes.h
|
|||
SOURCES += src/simulateclothmeshes.cpp
|
||||
HEADERS += src/simulateclothmeshes.h
|
||||
|
||||
SOURCES += src/ddsfile.cpp
|
||||
HEADERS += src/ddsfile.h
|
||||
|
||||
SOURCES += src/main.cpp
|
||||
|
||||
HEADERS += src/version.h
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
<file>resources/heptagon.png</file>
|
||||
<file>resources/userdefined.png</file>
|
||||
<file>resources/checkuv.png</file>
|
||||
<file>resources/cedar_bridge_irradiance.dds</file>
|
||||
<file>resources/cedar_bridge_specular.dds</file>
|
||||
<file>shaders/default.vert</file>
|
||||
<file>shaders/default.frag</file>
|
||||
<file>shaders/default.core.vert</file>
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -53,35 +53,39 @@
|
|||
// https://github.com/qt/qt3d/blob/5.11/src/extras/shaders/gl3/metalrough.inc.frag
|
||||
|
||||
// Exposure correction
|
||||
highp float exposure;
|
||||
float exposure;
|
||||
// Gamma correction
|
||||
highp float gamma;
|
||||
float gamma;
|
||||
|
||||
in highp vec3 vert;
|
||||
in highp vec3 vertRaw;
|
||||
in highp vec3 vertNormal;
|
||||
in highp vec3 vertColor;
|
||||
in highp vec2 vertTexCoord;
|
||||
in highp float vertMetalness;
|
||||
in highp float vertRoughness;
|
||||
in highp vec3 cameraPos;
|
||||
in highp vec3 firstLightPos;
|
||||
in highp vec3 secondLightPos;
|
||||
in highp vec3 thirdLightPos;
|
||||
in highp float vertAlpha;
|
||||
out highp vec4 fragColor;
|
||||
uniform highp vec3 lightPos;
|
||||
uniform highp sampler2D textureId;
|
||||
uniform highp int textureEnabled;
|
||||
uniform highp sampler2D normalMapId;
|
||||
uniform highp int normalMapEnabled;
|
||||
uniform highp sampler2D metalnessRoughnessAmbientOcclusionMapId;
|
||||
uniform highp int metalnessMapEnabled;
|
||||
uniform highp int roughnessMapEnabled;
|
||||
uniform highp int ambientOcclusionMapEnabled;
|
||||
uniform highp int mousePickEnabled;
|
||||
uniform highp vec3 mousePickTargetPosition;
|
||||
uniform highp float mousePickRadius;
|
||||
in vec3 vert;
|
||||
in vec3 vertRaw;
|
||||
in vec3 vertNormal;
|
||||
in vec3 vertColor;
|
||||
in vec2 vertTexCoord;
|
||||
in float vertMetalness;
|
||||
in float vertRoughness;
|
||||
in vec3 cameraPos;
|
||||
in vec3 firstLightPos;
|
||||
in vec3 secondLightPos;
|
||||
in vec3 thirdLightPos;
|
||||
in float vertAlpha;
|
||||
out vec4 fragColor;
|
||||
uniform vec3 lightPos;
|
||||
uniform sampler2D textureId;
|
||||
uniform int textureEnabled;
|
||||
uniform sampler2D normalMapId;
|
||||
uniform int normalMapEnabled;
|
||||
uniform sampler2D metalnessRoughnessAmbientOcclusionMapId;
|
||||
uniform int metalnessMapEnabled;
|
||||
uniform int roughnessMapEnabled;
|
||||
uniform int ambientOcclusionMapEnabled;
|
||||
uniform int mousePickEnabled;
|
||||
uniform vec3 mousePickTargetPosition;
|
||||
uniform float mousePickRadius;
|
||||
uniform samplerCube environmentIrradianceMapId;
|
||||
uniform int environmentIrradianceMapEnabled;
|
||||
uniform samplerCube environmentSpecularMapId;
|
||||
uniform int environmentSpecularMapEnabled;
|
||||
|
||||
const int MAX_LIGHTS = 8;
|
||||
const int TYPE_POINT = 0;
|
||||
|
@ -89,51 +93,88 @@ const int TYPE_DIRECTIONAL = 1;
|
|||
const int TYPE_SPOT = 2;
|
||||
struct Light {
|
||||
int type;
|
||||
highp vec3 position;
|
||||
highp vec3 color;
|
||||
highp float intensity;
|
||||
highp vec3 direction;
|
||||
highp float constantAttenuation;
|
||||
highp float linearAttenuation;
|
||||
highp float quadraticAttenuation;
|
||||
highp float cutOffAngle;
|
||||
vec3 position;
|
||||
vec3 color;
|
||||
float intensity;
|
||||
vec3 direction;
|
||||
float constantAttenuation;
|
||||
float linearAttenuation;
|
||||
float quadraticAttenuation;
|
||||
float cutOffAngle;
|
||||
};
|
||||
int lightCount;
|
||||
Light lights[MAX_LIGHTS];
|
||||
|
||||
highp float remapRoughness(const in highp float roughness)
|
||||
int mipLevelCount(const in samplerCube cube)
|
||||
{
|
||||
int baseSize = textureSize(cube, 0).x;
|
||||
int nMips = int(log2(float(baseSize > 0 ? baseSize : 1))) + 1;
|
||||
return nMips;
|
||||
}
|
||||
|
||||
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
|
||||
highp float maxSpecPower;
|
||||
highp float minRoughness;
|
||||
float maxSpecPower;
|
||||
float minRoughness;
|
||||
maxSpecPower = 999999.0;
|
||||
minRoughness = sqrt(2.0 / (maxSpecPower + 2.0));
|
||||
return max(roughness * roughness, minRoughness);
|
||||
}
|
||||
|
||||
highp float normalDistribution(const in highp vec3 n, const in highp vec3 h, const in highp float alpha)
|
||||
float alphaToMipLevel(float alpha)
|
||||
{
|
||||
float specPower = 2.0 / (alpha * alpha) - 2.0;
|
||||
|
||||
// We use the mip level calculation from Lys' default power drop, which in
|
||||
// turn is a slight modification of that used in Marmoset Toolbag. See
|
||||
// https://docs.knaldtech.com/doku.php?id=specular_lys for details.
|
||||
// For now we assume a max specular power of 999999 which gives
|
||||
// maxGlossiness = 1.
|
||||
const float k0 = 0.00098;
|
||||
const float k1 = 0.9921;
|
||||
float glossiness = (pow(2.0, -10.0 / sqrt(specPower)) - k0) / k1;
|
||||
|
||||
// TODO: Optimize by doing this on CPU and set as
|
||||
// uniform int environmentSpecularMapIdMipLevels say (if present in shader).
|
||||
// Lookup the number of mips in the specular envmap
|
||||
int mipLevels = mipLevelCount(environmentSpecularMapId);
|
||||
|
||||
// Offset of smallest miplevel we should use (corresponds to specular
|
||||
// power of 1). I.e. in the 32x32 sized mip.
|
||||
const float mipOffset = 5.0;
|
||||
|
||||
// The final factor is really 1 - g / g_max but as mentioned above g_max
|
||||
// is 1 by definition here so we can avoid the division. If we make the
|
||||
// max specular power for the spec map configurable, this will need to
|
||||
// be handled properly.
|
||||
float mipLevel = (mipLevels - 1.0 - mipOffset) * (1.0 - glossiness);
|
||||
return mipLevel;
|
||||
}
|
||||
|
||||
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
|
||||
highp float specPower = 2.0 / (alpha * alpha) - 2.0;
|
||||
float specPower = 2.0 / (alpha * alpha) - 2.0;
|
||||
return (specPower + 2.0) / (2.0 * 3.14159) * pow(max(dot(n, h), 0.0), specPower);
|
||||
}
|
||||
|
||||
highp vec3 fresnelFactor(const in highp vec3 color, const in highp float cosineFactor)
|
||||
vec3 fresnelFactor(const in vec3 color, const in float cosineFactor)
|
||||
{
|
||||
// Calculate the Fresnel effect value
|
||||
highp vec3 f = color;
|
||||
highp vec3 F = f + (1.0 - f) * pow(1.0 - cosineFactor, 5.0);
|
||||
vec3 f = color;
|
||||
vec3 F = f + (1.0 - f) * pow(1.0 - cosineFactor, 5.0);
|
||||
return clamp(F, f, vec3(1.0));
|
||||
}
|
||||
|
||||
highp float geometricModel(const in highp float lDotN,
|
||||
const in highp float vDotN,
|
||||
const in highp vec3 h)
|
||||
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
|
||||
|
@ -141,50 +182,50 @@ highp float geometricModel(const in highp float lDotN,
|
|||
return lDotN * vDotN;
|
||||
}
|
||||
|
||||
highp vec3 specularModel(const in highp vec3 F0,
|
||||
const in highp float sDotH,
|
||||
const in highp float sDotN,
|
||||
const in highp float vDotN,
|
||||
const in highp vec3 n,
|
||||
const in highp vec3 h)
|
||||
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.
|
||||
highp float sDotNPrime = max(sDotN, 0.001);
|
||||
highp float vDotNPrime = max(vDotN, 0.001);
|
||||
float sDotNPrime = max(sDotN, 0.001);
|
||||
float vDotNPrime = max(vDotN, 0.001);
|
||||
|
||||
highp vec3 F = fresnelFactor(F0, sDotH);
|
||||
highp float G = geometricModel(sDotNPrime, vDotNPrime, h);
|
||||
vec3 F = fresnelFactor(F0, sDotH);
|
||||
float G = geometricModel(sDotNPrime, vDotNPrime, h);
|
||||
|
||||
highp vec3 cSpec = F * G / (4.0 * sDotNPrime * vDotNPrime);
|
||||
vec3 cSpec = F * G / (4.0 * sDotNPrime * vDotNPrime);
|
||||
return clamp(cSpec, vec3(0.0), vec3(1.0));
|
||||
}
|
||||
|
||||
highp vec3 pbrModel(const in int lightIndex,
|
||||
const in highp vec3 wPosition,
|
||||
const in highp vec3 wNormal,
|
||||
const in highp vec3 wView,
|
||||
const in highp vec3 baseColor,
|
||||
const in highp float metalness,
|
||||
const in highp float alpha,
|
||||
const in highp float ambientOcclusion)
|
||||
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
|
||||
highp vec3 n = wNormal;
|
||||
highp vec3 s = vec3(0.0);
|
||||
highp vec3 v = wView;
|
||||
highp vec3 h = vec3(0.0);
|
||||
vec3 n = wNormal;
|
||||
vec3 s = vec3(0.0);
|
||||
vec3 v = wView;
|
||||
vec3 h = vec3(0.0);
|
||||
|
||||
highp float vDotN = dot(v, n);
|
||||
highp float sDotN = 0.0;
|
||||
highp float sDotH = 0.0;
|
||||
highp float att = 1.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
|
||||
highp vec3 sUnnormalized = vec3(lights[lightIndex].position) - wPosition;
|
||||
vec3 sUnnormalized = vec3(lights[lightIndex].position) - wPosition;
|
||||
s = normalize(sUnnormalized);
|
||||
|
||||
// Calculate the attenuation factor
|
||||
|
@ -193,7 +234,7 @@ highp vec3 pbrModel(const in int lightIndex,
|
|||
if (lights[lightIndex].constantAttenuation != 0.0
|
||||
|| lights[lightIndex].linearAttenuation != 0.0
|
||||
|| lights[lightIndex].quadraticAttenuation != 0.0) {
|
||||
highp float dist = length(sUnnormalized);
|
||||
float dist = length(sUnnormalized);
|
||||
att = 1.0 / (lights[lightIndex].constantAttenuation +
|
||||
lights[lightIndex].linearAttenuation * dist +
|
||||
lights[lightIndex].quadraticAttenuation * dist * dist);
|
||||
|
@ -217,22 +258,22 @@ highp vec3 pbrModel(const in int lightIndex,
|
|||
sDotH = dot(s, h);
|
||||
|
||||
// Calculate diffuse component
|
||||
highp vec3 diffuseColor = (1.0 - metalness) * baseColor * lights[lightIndex].color;
|
||||
highp vec3 diffuse = diffuseColor * max(sDotN, 0.0) / 3.14159;
|
||||
vec3 diffuseColor = (1.0 - metalness) * baseColor * lights[lightIndex].color;
|
||||
vec3 diffuse = diffuseColor * max(sDotN, 0.0) / 3.14159;
|
||||
|
||||
// Calculate specular component
|
||||
highp vec3 dielectricColor = vec3(0.04);
|
||||
highp vec3 F0 = mix(dielectricColor, baseColor, metalness);
|
||||
highp vec3 specularFactor = vec3(0.0);
|
||||
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);
|
||||
}
|
||||
highp vec3 specularColor = lights[lightIndex].color;
|
||||
highp vec3 specular = specularColor * specularFactor;
|
||||
vec3 specularColor = lights[lightIndex].color;
|
||||
vec3 specular = specularColor * specularFactor;
|
||||
|
||||
// Blend between diffuse and specular to conserver energy
|
||||
highp vec3 color = att * lights[lightIndex].intensity * (specular + diffuse * (vec3(1.0) - specular));
|
||||
vec3 color = att * lights[lightIndex].intensity * (specular + diffuse * (vec3(1.0) - specular));
|
||||
|
||||
// Reduce by ambient occlusion amount
|
||||
color *= ambientOcclusion;
|
||||
|
@ -240,29 +281,99 @@ highp vec3 pbrModel(const in int lightIndex,
|
|||
return color;
|
||||
}
|
||||
|
||||
highp vec3 toneMap(const in highp vec3 c)
|
||||
vec3 pbrIblModel(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 reflection direction of view vector about surface normal
|
||||
// vector in world space. This is used in the fragment shader to sample
|
||||
// from the environment textures for a light source. This is equivalent
|
||||
// to the l vector for punctual light sources. Armed with this, calculate
|
||||
// the usual factors needed
|
||||
vec3 n = wNormal;
|
||||
vec3 l = reflect(-wView, n);
|
||||
vec3 v = wView;
|
||||
vec3 h = normalize(l + v);
|
||||
float vDotN = dot(v, n);
|
||||
float lDotN = dot(l, n);
|
||||
float lDotH = dot(l, h);
|
||||
|
||||
// Calculate diffuse component
|
||||
vec3 diffuseColor = (1.0 - metalness) * baseColor;
|
||||
vec3 diffuse = diffuseColor * texture(environmentIrradianceMapId, l).rgb;
|
||||
|
||||
// Calculate specular component
|
||||
vec3 dielectricColor = vec3(0.04);
|
||||
vec3 F0 = mix(dielectricColor, baseColor, metalness);
|
||||
vec3 specularFactor = specularModel(F0, lDotH, lDotN, vDotN, n, h);
|
||||
|
||||
float lod = alphaToMipLevel(alpha);
|
||||
//#define DEBUG_SPECULAR_LODS
|
||||
#ifdef DEBUG_SPECULAR_LODS
|
||||
if (lod > 7.0)
|
||||
return vec3(1.0, 0.0, 0.0);
|
||||
else if (lod > 6.0)
|
||||
return vec3(1.0, 0.333, 0.0);
|
||||
else if (lod > 5.0)
|
||||
return vec3(1.0, 1.0, 0.0);
|
||||
else if (lod > 4.0)
|
||||
return vec3(0.666, 1.0, 0.0);
|
||||
else if (lod > 3.0)
|
||||
return vec3(0.0, 1.0, 0.666);
|
||||
else if (lod > 2.0)
|
||||
return vec3(0.0, 0.666, 1.0);
|
||||
else if (lod > 1.0)
|
||||
return vec3(0.0, 0.0, 1.0);
|
||||
else if (lod > 0.0)
|
||||
return vec3(1.0, 0.0, 1.0);
|
||||
#endif
|
||||
vec3 specularSkyColor = textureLod(environmentSpecularMapId, l, lod).rgb;
|
||||
vec3 specular = specularSkyColor * specularFactor;
|
||||
|
||||
// Blend between diffuse and specular to conserve energy
|
||||
vec3 color = specular + diffuse * (vec3(1.0) - specularFactor);
|
||||
|
||||
// Reduce by ambient occlusion amount
|
||||
color *= ambientOcclusion;
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
vec3 toneMap(const in vec3 c)
|
||||
{
|
||||
return c / (c + vec3(1.0));
|
||||
}
|
||||
|
||||
highp vec3 gammaCorrect(const in highp vec3 color)
|
||||
vec3 gammaCorrect(const in vec3 color)
|
||||
{
|
||||
return pow(color, vec3(1.0 / gamma));
|
||||
}
|
||||
|
||||
highp vec4 metalRoughFunction(const in highp vec4 baseColor,
|
||||
const in highp float metalness,
|
||||
const in highp float roughness,
|
||||
const in highp float ambientOcclusion,
|
||||
const in highp vec3 worldPosition,
|
||||
const in highp vec3 worldView,
|
||||
const in highp vec3 worldNormal)
|
||||
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)
|
||||
{
|
||||
highp vec3 cLinear = vec3(0.0);
|
||||
vec3 cLinear = vec3(0.0);
|
||||
|
||||
// Remap roughness for a perceptually more linear correspondence
|
||||
highp float alpha = remapRoughness(roughness);
|
||||
|
||||
float alpha = remapRoughness(roughness);
|
||||
|
||||
if (environmentIrradianceMapEnabled == 1) {
|
||||
cLinear += pbrIblModel(worldNormal,
|
||||
worldView,
|
||||
baseColor.rgb,
|
||||
metalness,
|
||||
alpha,
|
||||
ambientOcclusion);
|
||||
}
|
||||
|
||||
for (int i = 0; i < lightCount; ++i) {
|
||||
cLinear += pbrModel(i,
|
||||
worldPosition,
|
||||
|
@ -278,10 +389,10 @@ highp vec4 metalRoughFunction(const in highp vec4 baseColor,
|
|||
cLinear *= pow(2.0, exposure);
|
||||
|
||||
// Apply simple (Reinhard) tonemap transform to get into LDR range [0, 1]
|
||||
highp vec3 cToneMapped = toneMap(cLinear);
|
||||
vec3 cToneMapped = toneMap(cLinear);
|
||||
|
||||
// Apply gamma correction prior to display
|
||||
highp vec3 cGamma = gammaCorrect(cToneMapped);
|
||||
vec3 cGamma = gammaCorrect(cToneMapped);
|
||||
|
||||
return vec4(cGamma, baseColor.a);
|
||||
}
|
||||
|
@ -323,10 +434,10 @@ void main()
|
|||
lights[2].linearAttenuation = 0.0;
|
||||
lights[2].quadraticAttenuation = 0.0;
|
||||
|
||||
highp vec3 color = vertColor;
|
||||
highp float alpha = vertAlpha;
|
||||
vec3 color = vertColor;
|
||||
float alpha = vertAlpha;
|
||||
if (textureEnabled == 1) {
|
||||
highp vec4 textColor = texture(textureId, vertTexCoord);
|
||||
vec4 textColor = texture(textureId, vertTexCoord);
|
||||
color = textColor.rgb;
|
||||
alpha = textColor.a;
|
||||
}
|
||||
|
@ -337,7 +448,7 @@ void main()
|
|||
}
|
||||
color = pow(color, vec3(gamma));
|
||||
|
||||
highp vec3 normal = vertNormal;
|
||||
vec3 normal = vertNormal;
|
||||
if (normalMapEnabled == 1) {
|
||||
normal = texture(normalMapId, vertTexCoord).rgb;
|
||||
normal = normalize(normal * 2.0 - 1.0);
|
||||
|
@ -347,23 +458,25 @@ void main()
|
|||
// Green: Roughness
|
||||
// Blue: Metallic
|
||||
|
||||
highp float metalness = vertMetalness;
|
||||
float metalness = vertMetalness;
|
||||
if (metalnessMapEnabled == 1) {
|
||||
metalness = texture(metalnessRoughnessAmbientOcclusionMapId, vertTexCoord).b;
|
||||
}
|
||||
|
||||
highp float roughness = vertRoughness;
|
||||
float roughness = vertRoughness;
|
||||
if (roughnessMapEnabled == 1) {
|
||||
roughness = texture(metalnessRoughnessAmbientOcclusionMapId, vertTexCoord).g;
|
||||
}
|
||||
|
||||
highp float ambientOcclusion = 1.0;
|
||||
float ambientOcclusion = 1.0;
|
||||
if (ambientOcclusionMapEnabled == 1) {
|
||||
ambientOcclusion = texture(metalnessRoughnessAmbientOcclusionMapId, vertTexCoord).r;
|
||||
}
|
||||
|
||||
roughness = min(0.99, roughness);
|
||||
metalness = min(0.99, metalness);
|
||||
if (environmentIrradianceMapEnabled != 1) {
|
||||
roughness = min(0.99, roughness);
|
||||
metalness = min(0.99, metalness);
|
||||
}
|
||||
|
||||
fragColor = metalRoughFunction(vec4(color, alpha),
|
||||
metalness,
|
||||
|
|
|
@ -1,29 +1,29 @@
|
|||
#version 330
|
||||
layout(location = 0) in highp vec4 vertex;
|
||||
layout(location = 1) in highp vec3 normal;
|
||||
layout(location = 2) in highp vec3 color;
|
||||
layout(location = 3) in highp vec2 texCoord;
|
||||
layout(location = 4) in highp float metalness;
|
||||
layout(location = 5) in highp float roughness;
|
||||
layout(location = 6) in highp vec3 tangent;
|
||||
layout(location = 7) in highp float alpha;
|
||||
out highp vec3 vert;
|
||||
out highp vec3 vertRaw;
|
||||
out highp vec3 vertNormal;
|
||||
out highp vec3 vertColor;
|
||||
out highp vec2 vertTexCoord;
|
||||
out highp float vertMetalness;
|
||||
out highp float vertRoughness;
|
||||
out highp vec3 cameraPos;
|
||||
out highp vec3 firstLightPos;
|
||||
out highp vec3 secondLightPos;
|
||||
out highp vec3 thirdLightPos;
|
||||
out highp float vertAlpha;
|
||||
uniform highp mat4 projectionMatrix;
|
||||
uniform highp mat4 modelMatrix;
|
||||
uniform highp mat3 normalMatrix;
|
||||
uniform highp mat4 viewMatrix;
|
||||
uniform highp int normalMapEnabled;
|
||||
layout(location = 0) in vec4 vertex;
|
||||
layout(location = 1) in vec3 normal;
|
||||
layout(location = 2) in vec3 color;
|
||||
layout(location = 3) in vec2 texCoord;
|
||||
layout(location = 4) in float metalness;
|
||||
layout(location = 5) in float roughness;
|
||||
layout(location = 6) in vec3 tangent;
|
||||
layout(location = 7) in float alpha;
|
||||
out vec3 vert;
|
||||
out vec3 vertRaw;
|
||||
out vec3 vertNormal;
|
||||
out vec3 vertColor;
|
||||
out vec2 vertTexCoord;
|
||||
out float vertMetalness;
|
||||
out float vertRoughness;
|
||||
out vec3 cameraPos;
|
||||
out vec3 firstLightPos;
|
||||
out vec3 secondLightPos;
|
||||
out vec3 thirdLightPos;
|
||||
out float vertAlpha;
|
||||
uniform mat4 projectionMatrix;
|
||||
uniform mat4 modelMatrix;
|
||||
uniform mat3 normalMatrix;
|
||||
uniform mat4 viewMatrix;
|
||||
uniform int normalMapEnabled;
|
||||
|
||||
mat3 transpose(mat3 m)
|
||||
{
|
||||
|
|
|
@ -52,34 +52,34 @@
|
|||
// https://github.com/qt/qt3d/blob/5.11/src/extras/shaders/gl3/metalrough.inc.frag
|
||||
|
||||
// Exposure correction
|
||||
highp float exposure;
|
||||
float exposure;
|
||||
// Gamma correction
|
||||
highp float gamma;
|
||||
float gamma;
|
||||
|
||||
varying highp vec3 vert;
|
||||
varying highp vec3 vertRaw;
|
||||
varying highp vec3 vertNormal;
|
||||
varying highp vec3 vertColor;
|
||||
varying highp vec2 vertTexCoord;
|
||||
varying highp float vertMetalness;
|
||||
varying highp float vertRoughness;
|
||||
varying highp vec3 cameraPos;
|
||||
varying highp vec3 firstLightPos;
|
||||
varying highp vec3 secondLightPos;
|
||||
varying highp vec3 thirdLightPos;
|
||||
varying highp float vertAlpha;
|
||||
uniform highp vec3 lightPos;
|
||||
uniform highp sampler2D textureId;
|
||||
uniform highp int textureEnabled;
|
||||
uniform highp sampler2D normalMapId;
|
||||
uniform highp int normalMapEnabled;
|
||||
uniform highp sampler2D metalnessRoughnessAmbientOcclusionMapId;
|
||||
uniform highp int metalnessMapEnabled;
|
||||
uniform highp int roughnessMapEnabled;
|
||||
uniform highp int ambientOcclusionMapEnabled;
|
||||
uniform highp int mousePickEnabled;
|
||||
uniform highp vec3 mousePickTargetPosition;
|
||||
uniform highp float mousePickRadius;
|
||||
varying vec3 vert;
|
||||
varying vec3 vertRaw;
|
||||
varying vec3 vertNormal;
|
||||
varying vec3 vertColor;
|
||||
varying vec2 vertTexCoord;
|
||||
varying float vertMetalness;
|
||||
varying float vertRoughness;
|
||||
varying vec3 cameraPos;
|
||||
varying vec3 firstLightPos;
|
||||
varying vec3 secondLightPos;
|
||||
varying vec3 thirdLightPos;
|
||||
varying float vertAlpha;
|
||||
uniform vec3 lightPos;
|
||||
uniform sampler2D textureId;
|
||||
uniform int textureEnabled;
|
||||
uniform sampler2D normalMapId;
|
||||
uniform int normalMapEnabled;
|
||||
uniform sampler2D metalnessRoughnessAmbientOcclusionMapId;
|
||||
uniform int metalnessMapEnabled;
|
||||
uniform int roughnessMapEnabled;
|
||||
uniform int ambientOcclusionMapEnabled;
|
||||
uniform int mousePickEnabled;
|
||||
uniform vec3 mousePickTargetPosition;
|
||||
uniform float mousePickRadius;
|
||||
|
||||
const int MAX_LIGHTS = 8;
|
||||
const int TYPE_POINT = 0;
|
||||
|
@ -87,51 +87,51 @@ const int TYPE_DIRECTIONAL = 1;
|
|||
const int TYPE_SPOT = 2;
|
||||
struct Light {
|
||||
int type;
|
||||
highp vec3 position;
|
||||
highp vec3 color;
|
||||
highp float intensity;
|
||||
highp vec3 direction;
|
||||
highp float constantAttenuation;
|
||||
highp float linearAttenuation;
|
||||
highp float quadraticAttenuation;
|
||||
highp float cutOffAngle;
|
||||
vec3 position;
|
||||
vec3 color;
|
||||
float intensity;
|
||||
vec3 direction;
|
||||
float constantAttenuation;
|
||||
float linearAttenuation;
|
||||
float quadraticAttenuation;
|
||||
float cutOffAngle;
|
||||
};
|
||||
int lightCount;
|
||||
Light lights[MAX_LIGHTS];
|
||||
|
||||
highp float remapRoughness(const in highp float roughness)
|
||||
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
|
||||
highp float maxSpecPower;
|
||||
highp float minRoughness;
|
||||
float maxSpecPower;
|
||||
float minRoughness;
|
||||
maxSpecPower = 999999.0;
|
||||
minRoughness = sqrt(2.0 / (maxSpecPower + 2.0));
|
||||
return max(roughness * roughness, minRoughness);
|
||||
}
|
||||
|
||||
highp float normalDistribution(const in highp vec3 n, const in highp vec3 h, const in highp float alpha)
|
||||
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
|
||||
highp float specPower = 2.0 / (alpha * alpha) - 2.0;
|
||||
float specPower = 2.0 / (alpha * alpha) - 2.0;
|
||||
return (specPower + 2.0) / (2.0 * 3.14159) * pow(max(dot(n, h), 0.0), specPower);
|
||||
}
|
||||
|
||||
highp vec3 fresnelFactor(const in highp vec3 color, const in highp float cosineFactor)
|
||||
vec3 fresnelFactor(const in vec3 color, const in float cosineFactor)
|
||||
{
|
||||
// Calculate the Fresnel effect value
|
||||
highp vec3 f = color;
|
||||
highp vec3 F = f + (1.0 - f) * pow(1.0 - cosineFactor, 5.0);
|
||||
vec3 f = color;
|
||||
vec3 F = f + (1.0 - f) * pow(1.0 - cosineFactor, 5.0);
|
||||
return clamp(F, f, vec3(1.0));
|
||||
}
|
||||
|
||||
highp float geometricModel(const in highp float lDotN,
|
||||
const in highp float vDotN,
|
||||
const in highp vec3 h)
|
||||
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
|
||||
|
@ -139,50 +139,50 @@ highp float geometricModel(const in highp float lDotN,
|
|||
return lDotN * vDotN;
|
||||
}
|
||||
|
||||
highp vec3 specularModel(const in highp vec3 F0,
|
||||
const in highp float sDotH,
|
||||
const in highp float sDotN,
|
||||
const in highp float vDotN,
|
||||
const in highp vec3 n,
|
||||
const in highp vec3 h)
|
||||
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.
|
||||
highp float sDotNPrime = max(sDotN, 0.001);
|
||||
highp float vDotNPrime = max(vDotN, 0.001);
|
||||
float sDotNPrime = max(sDotN, 0.001);
|
||||
float vDotNPrime = max(vDotN, 0.001);
|
||||
|
||||
highp vec3 F = fresnelFactor(F0, sDotH);
|
||||
highp float G = geometricModel(sDotNPrime, vDotNPrime, h);
|
||||
vec3 F = fresnelFactor(F0, sDotH);
|
||||
float G = geometricModel(sDotNPrime, vDotNPrime, h);
|
||||
|
||||
highp vec3 cSpec = F * G / (4.0 * sDotNPrime * vDotNPrime);
|
||||
vec3 cSpec = F * G / (4.0 * sDotNPrime * vDotNPrime);
|
||||
return clamp(cSpec, vec3(0.0), vec3(1.0));
|
||||
}
|
||||
|
||||
highp vec3 pbrModel(const in int lightIndex,
|
||||
const in highp vec3 wPosition,
|
||||
const in highp vec3 wNormal,
|
||||
const in highp vec3 wView,
|
||||
const in highp vec3 baseColor,
|
||||
const in highp float metalness,
|
||||
const in highp float alpha,
|
||||
const in highp float ambientOcclusion)
|
||||
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
|
||||
highp vec3 n = wNormal;
|
||||
highp vec3 s = vec3(0.0);
|
||||
highp vec3 v = wView;
|
||||
highp vec3 h = vec3(0.0);
|
||||
vec3 n = wNormal;
|
||||
vec3 s = vec3(0.0);
|
||||
vec3 v = wView;
|
||||
vec3 h = vec3(0.0);
|
||||
|
||||
highp float vDotN = dot(v, n);
|
||||
highp float sDotN = 0.0;
|
||||
highp float sDotH = 0.0;
|
||||
highp float att = 1.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
|
||||
highp vec3 sUnnormalized = vec3(lights[lightIndex].position) - wPosition;
|
||||
vec3 sUnnormalized = vec3(lights[lightIndex].position) - wPosition;
|
||||
s = normalize(sUnnormalized);
|
||||
|
||||
// Calculate the attenuation factor
|
||||
|
@ -191,7 +191,7 @@ highp vec3 pbrModel(const in int lightIndex,
|
|||
if (lights[lightIndex].constantAttenuation != 0.0
|
||||
|| lights[lightIndex].linearAttenuation != 0.0
|
||||
|| lights[lightIndex].quadraticAttenuation != 0.0) {
|
||||
highp float dist = length(sUnnormalized);
|
||||
float dist = length(sUnnormalized);
|
||||
att = 1.0 / (lights[lightIndex].constantAttenuation +
|
||||
lights[lightIndex].linearAttenuation * dist +
|
||||
lights[lightIndex].quadraticAttenuation * dist * dist);
|
||||
|
@ -215,22 +215,22 @@ highp vec3 pbrModel(const in int lightIndex,
|
|||
sDotH = dot(s, h);
|
||||
|
||||
// Calculate diffuse component
|
||||
highp vec3 diffuseColor = (1.0 - metalness) * baseColor * lights[lightIndex].color;
|
||||
highp vec3 diffuse = diffuseColor * max(sDotN, 0.0) / 3.14159;
|
||||
vec3 diffuseColor = (1.0 - metalness) * baseColor * lights[lightIndex].color;
|
||||
vec3 diffuse = diffuseColor * max(sDotN, 0.0) / 3.14159;
|
||||
|
||||
// Calculate specular component
|
||||
highp vec3 dielectricColor = vec3(0.04);
|
||||
highp vec3 F0 = mix(dielectricColor, baseColor, metalness);
|
||||
highp vec3 specularFactor = vec3(0.0);
|
||||
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);
|
||||
}
|
||||
highp vec3 specularColor = lights[lightIndex].color;
|
||||
highp vec3 specular = specularColor * specularFactor;
|
||||
vec3 specularColor = lights[lightIndex].color;
|
||||
vec3 specular = specularColor * specularFactor;
|
||||
|
||||
// Blend between diffuse and specular to conserver energy
|
||||
highp vec3 color = att * lights[lightIndex].intensity * (specular + diffuse * (vec3(1.0) - specular));
|
||||
vec3 color = att * lights[lightIndex].intensity * (specular + diffuse * (vec3(1.0) - specular));
|
||||
|
||||
// Reduce by ambient occlusion amount
|
||||
color *= ambientOcclusion;
|
||||
|
@ -238,28 +238,28 @@ highp vec3 pbrModel(const in int lightIndex,
|
|||
return color;
|
||||
}
|
||||
|
||||
highp vec3 toneMap(const in highp vec3 c)
|
||||
vec3 toneMap(const in vec3 c)
|
||||
{
|
||||
return c / (c + vec3(1.0));
|
||||
}
|
||||
|
||||
highp vec3 gammaCorrect(const in highp vec3 color)
|
||||
vec3 gammaCorrect(const in vec3 color)
|
||||
{
|
||||
return pow(color, vec3(1.0 / gamma));
|
||||
}
|
||||
|
||||
highp vec4 metalRoughFunction(const in highp vec4 baseColor,
|
||||
const in highp float metalness,
|
||||
const in highp float roughness,
|
||||
const in highp float ambientOcclusion,
|
||||
const in highp vec3 worldPosition,
|
||||
const in highp vec3 worldView,
|
||||
const in highp vec3 worldNormal)
|
||||
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)
|
||||
{
|
||||
highp vec3 cLinear = vec3(0.0);
|
||||
vec3 cLinear = vec3(0.0);
|
||||
|
||||
// Remap roughness for a perceptually more linear correspondence
|
||||
highp float alpha = remapRoughness(roughness);
|
||||
float alpha = remapRoughness(roughness);
|
||||
|
||||
for (int i = 0; i < lightCount; ++i) {
|
||||
cLinear += pbrModel(i,
|
||||
|
@ -276,10 +276,10 @@ highp vec4 metalRoughFunction(const in highp vec4 baseColor,
|
|||
cLinear *= pow(2.0, exposure);
|
||||
|
||||
// Apply simple (Reinhard) tonemap transform to get into LDR range [0, 1]
|
||||
highp vec3 cToneMapped = toneMap(cLinear);
|
||||
vec3 cToneMapped = toneMap(cLinear);
|
||||
|
||||
// Apply gamma correction prior to display
|
||||
highp vec3 cGamma = gammaCorrect(cToneMapped);
|
||||
vec3 cGamma = gammaCorrect(cToneMapped);
|
||||
|
||||
return vec4(cGamma, baseColor.a);
|
||||
}
|
||||
|
@ -321,10 +321,10 @@ void main()
|
|||
lights[2].linearAttenuation = 0.0;
|
||||
lights[2].quadraticAttenuation = 0.0;
|
||||
|
||||
highp vec3 color = vertColor;
|
||||
highp float alpha = vertAlpha;
|
||||
vec3 color = vertColor;
|
||||
float alpha = vertAlpha;
|
||||
if (textureEnabled == 1) {
|
||||
highp vec4 textColor = texture2D(textureId, vertTexCoord);
|
||||
vec4 textColor = texture2D(textureId, vertTexCoord);
|
||||
color = textColor.rgb;
|
||||
alpha = textColor.a;
|
||||
}
|
||||
|
@ -335,7 +335,7 @@ void main()
|
|||
}
|
||||
color = pow(color, vec3(gamma));
|
||||
|
||||
highp vec3 normal = vertNormal;
|
||||
vec3 normal = vertNormal;
|
||||
if (normalMapEnabled == 1) {
|
||||
normal = texture2D(normalMapId, vertTexCoord).rgb;
|
||||
normal = normalize(normal * 2.0 - 1.0);
|
||||
|
@ -345,17 +345,17 @@ void main()
|
|||
// Green: Roughness
|
||||
// Blue: Metallic
|
||||
|
||||
highp float metalness = vertMetalness;
|
||||
float metalness = vertMetalness;
|
||||
if (metalnessMapEnabled == 1) {
|
||||
metalness = texture2D(metalnessRoughnessAmbientOcclusionMapId, vertTexCoord).b;
|
||||
}
|
||||
|
||||
highp float roughness = vertRoughness;
|
||||
float roughness = vertRoughness;
|
||||
if (roughnessMapEnabled == 1) {
|
||||
roughness = texture2D(metalnessRoughnessAmbientOcclusionMapId, vertTexCoord).g;
|
||||
}
|
||||
|
||||
highp float ambientOcclusion = 1.0;
|
||||
float ambientOcclusion = 1.0;
|
||||
if (ambientOcclusionMapEnabled == 1) {
|
||||
ambientOcclusion = texture2D(metalnessRoughnessAmbientOcclusionMapId, vertTexCoord).r;
|
||||
}
|
||||
|
|
|
@ -1,28 +1,28 @@
|
|||
attribute highp vec4 vertex;
|
||||
attribute highp vec3 normal;
|
||||
attribute highp vec3 color;
|
||||
attribute highp vec2 texCoord;
|
||||
attribute highp float metalness;
|
||||
attribute highp float roughness;
|
||||
attribute highp vec3 tangent;
|
||||
attribute highp float alpha;
|
||||
varying highp vec3 vert;
|
||||
varying highp vec3 vertRaw;
|
||||
varying highp vec3 vertNormal;
|
||||
varying highp vec3 vertColor;
|
||||
varying highp vec2 vertTexCoord;
|
||||
varying highp float vertMetalness;
|
||||
varying highp float vertRoughness;
|
||||
varying highp vec3 cameraPos;
|
||||
varying highp vec3 firstLightPos;
|
||||
varying highp vec3 secondLightPos;
|
||||
varying highp vec3 thirdLightPos;
|
||||
varying highp float vertAlpha;
|
||||
uniform highp mat4 projectionMatrix;
|
||||
uniform highp mat4 modelMatrix;
|
||||
uniform highp mat3 normalMatrix;
|
||||
uniform highp mat4 viewMatrix;
|
||||
uniform highp int normalMapEnabled;
|
||||
attribute vec4 vertex;
|
||||
attribute vec3 normal;
|
||||
attribute vec3 color;
|
||||
attribute vec2 texCoord;
|
||||
attribute float metalness;
|
||||
attribute float roughness;
|
||||
attribute vec3 tangent;
|
||||
attribute float alpha;
|
||||
varying vec3 vert;
|
||||
varying vec3 vertRaw;
|
||||
varying vec3 vertNormal;
|
||||
varying vec3 vertColor;
|
||||
varying vec2 vertTexCoord;
|
||||
varying float vertMetalness;
|
||||
varying float vertRoughness;
|
||||
varying vec3 cameraPos;
|
||||
varying vec3 firstLightPos;
|
||||
varying vec3 secondLightPos;
|
||||
varying vec3 thirdLightPos;
|
||||
varying float vertAlpha;
|
||||
uniform mat4 projectionMatrix;
|
||||
uniform mat4 modelMatrix;
|
||||
uniform mat3 normalMatrix;
|
||||
uniform mat4 viewMatrix;
|
||||
uniform int normalMapEnabled;
|
||||
|
||||
mat3 transpose(mat3 m)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,523 @@
|
|||
#include <QtGlobal>
|
||||
#include <QFile>
|
||||
#include <QDebug>
|
||||
#include <QtEndian>
|
||||
#include <QByteArray>
|
||||
#include <QOpenGLPixelTransferOptions>
|
||||
#include "ddsfile.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
typedef quint32 DWORD;
|
||||
typedef quint32 UINT;
|
||||
#endif
|
||||
|
||||
// DDS data struct copy from
|
||||
// https://docs.microsoft.com/en-us/windows/win32/direct3ddds/dx-graphics-dds-pguide
|
||||
|
||||
typedef struct {
|
||||
DWORD dwSize;
|
||||
DWORD dwFlags;
|
||||
DWORD dwFourCC;
|
||||
DWORD dwRGBBitCount;
|
||||
DWORD dwRBitMask;
|
||||
DWORD dwGBitMask;
|
||||
DWORD dwBBitMask;
|
||||
DWORD dwABitMask;
|
||||
} DDS_PIXELFORMAT;
|
||||
|
||||
typedef enum {
|
||||
DDSCAPS2_CUBEMAP = 0x200,
|
||||
DDSCAPS2_CUBEMAP_POSITIVEX = 0x400,
|
||||
DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800,
|
||||
DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000,
|
||||
DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000,
|
||||
DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000,
|
||||
DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000,
|
||||
DDSCAPS2_VOLUME = 0x200000
|
||||
} DDS_CAPS2_FLAGS;
|
||||
|
||||
typedef struct {
|
||||
DWORD dwSize;
|
||||
DWORD dwFlags;
|
||||
DWORD dwHeight;
|
||||
DWORD dwWidth;
|
||||
DWORD dwPitchOrLinearSize;
|
||||
DWORD dwDepth;
|
||||
DWORD dwMipMapCount;
|
||||
DWORD dwReserved1[11];
|
||||
DDS_PIXELFORMAT ddspf;
|
||||
DWORD dwCaps;
|
||||
DWORD dwCaps2;
|
||||
DWORD dwCaps3;
|
||||
DWORD dwCaps4;
|
||||
DWORD dwReserved2;
|
||||
} DDS_HEADER;
|
||||
|
||||
typedef enum {
|
||||
DXGI_FORMAT_UNKNOWN,
|
||||
DXGI_FORMAT_R32G32B32A32_TYPELESS,
|
||||
DXGI_FORMAT_R32G32B32A32_FLOAT,
|
||||
DXGI_FORMAT_R32G32B32A32_UINT,
|
||||
DXGI_FORMAT_R32G32B32A32_SINT,
|
||||
DXGI_FORMAT_R32G32B32_TYPELESS,
|
||||
DXGI_FORMAT_R32G32B32_FLOAT,
|
||||
DXGI_FORMAT_R32G32B32_UINT,
|
||||
DXGI_FORMAT_R32G32B32_SINT,
|
||||
DXGI_FORMAT_R16G16B16A16_TYPELESS,
|
||||
DXGI_FORMAT_R16G16B16A16_FLOAT,
|
||||
DXGI_FORMAT_R16G16B16A16_UNORM,
|
||||
DXGI_FORMAT_R16G16B16A16_UINT,
|
||||
DXGI_FORMAT_R16G16B16A16_SNORM,
|
||||
DXGI_FORMAT_R16G16B16A16_SINT,
|
||||
DXGI_FORMAT_R32G32_TYPELESS,
|
||||
DXGI_FORMAT_R32G32_FLOAT,
|
||||
DXGI_FORMAT_R32G32_UINT,
|
||||
DXGI_FORMAT_R32G32_SINT,
|
||||
DXGI_FORMAT_R32G8X24_TYPELESS,
|
||||
DXGI_FORMAT_D32_FLOAT_S8X24_UINT,
|
||||
DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS,
|
||||
DXGI_FORMAT_X32_TYPELESS_G8X24_UINT,
|
||||
DXGI_FORMAT_R10G10B10A2_TYPELESS,
|
||||
DXGI_FORMAT_R10G10B10A2_UNORM,
|
||||
DXGI_FORMAT_R10G10B10A2_UINT,
|
||||
DXGI_FORMAT_R11G11B10_FLOAT,
|
||||
DXGI_FORMAT_R8G8B8A8_TYPELESS,
|
||||
DXGI_FORMAT_R8G8B8A8_UNORM,
|
||||
DXGI_FORMAT_R8G8B8A8_UNORM_SRGB,
|
||||
DXGI_FORMAT_R8G8B8A8_UINT,
|
||||
DXGI_FORMAT_R8G8B8A8_SNORM,
|
||||
DXGI_FORMAT_R8G8B8A8_SINT,
|
||||
DXGI_FORMAT_R16G16_TYPELESS,
|
||||
DXGI_FORMAT_R16G16_FLOAT,
|
||||
DXGI_FORMAT_R16G16_UNORM,
|
||||
DXGI_FORMAT_R16G16_UINT,
|
||||
DXGI_FORMAT_R16G16_SNORM,
|
||||
DXGI_FORMAT_R16G16_SINT,
|
||||
DXGI_FORMAT_R32_TYPELESS,
|
||||
DXGI_FORMAT_D32_FLOAT,
|
||||
DXGI_FORMAT_R32_FLOAT,
|
||||
DXGI_FORMAT_R32_UINT,
|
||||
DXGI_FORMAT_R32_SINT,
|
||||
DXGI_FORMAT_R24G8_TYPELESS,
|
||||
DXGI_FORMAT_D24_UNORM_S8_UINT,
|
||||
DXGI_FORMAT_R24_UNORM_X8_TYPELESS,
|
||||
DXGI_FORMAT_X24_TYPELESS_G8_UINT,
|
||||
DXGI_FORMAT_R8G8_TYPELESS,
|
||||
DXGI_FORMAT_R8G8_UNORM,
|
||||
DXGI_FORMAT_R8G8_UINT,
|
||||
DXGI_FORMAT_R8G8_SNORM,
|
||||
DXGI_FORMAT_R8G8_SINT,
|
||||
DXGI_FORMAT_R16_TYPELESS,
|
||||
DXGI_FORMAT_R16_FLOAT,
|
||||
DXGI_FORMAT_D16_UNORM,
|
||||
DXGI_FORMAT_R16_UNORM,
|
||||
DXGI_FORMAT_R16_UINT,
|
||||
DXGI_FORMAT_R16_SNORM,
|
||||
DXGI_FORMAT_R16_SINT,
|
||||
DXGI_FORMAT_R8_TYPELESS,
|
||||
DXGI_FORMAT_R8_UNORM,
|
||||
DXGI_FORMAT_R8_UINT,
|
||||
DXGI_FORMAT_R8_SNORM,
|
||||
DXGI_FORMAT_R8_SINT,
|
||||
DXGI_FORMAT_A8_UNORM,
|
||||
DXGI_FORMAT_R1_UNORM,
|
||||
DXGI_FORMAT_R9G9B9E5_SHAREDEXP,
|
||||
DXGI_FORMAT_R8G8_B8G8_UNORM,
|
||||
DXGI_FORMAT_G8R8_G8B8_UNORM,
|
||||
DXGI_FORMAT_BC1_TYPELESS,
|
||||
DXGI_FORMAT_BC1_UNORM,
|
||||
DXGI_FORMAT_BC1_UNORM_SRGB,
|
||||
DXGI_FORMAT_BC2_TYPELESS,
|
||||
DXGI_FORMAT_BC2_UNORM,
|
||||
DXGI_FORMAT_BC2_UNORM_SRGB,
|
||||
DXGI_FORMAT_BC3_TYPELESS,
|
||||
DXGI_FORMAT_BC3_UNORM,
|
||||
DXGI_FORMAT_BC3_UNORM_SRGB,
|
||||
DXGI_FORMAT_BC4_TYPELESS,
|
||||
DXGI_FORMAT_BC4_UNORM,
|
||||
DXGI_FORMAT_BC4_SNORM,
|
||||
DXGI_FORMAT_BC5_TYPELESS,
|
||||
DXGI_FORMAT_BC5_UNORM,
|
||||
DXGI_FORMAT_BC5_SNORM,
|
||||
DXGI_FORMAT_B5G6R5_UNORM,
|
||||
DXGI_FORMAT_B5G5R5A1_UNORM,
|
||||
DXGI_FORMAT_B8G8R8A8_UNORM,
|
||||
DXGI_FORMAT_B8G8R8X8_UNORM,
|
||||
DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM,
|
||||
DXGI_FORMAT_B8G8R8A8_TYPELESS,
|
||||
DXGI_FORMAT_B8G8R8A8_UNORM_SRGB,
|
||||
DXGI_FORMAT_B8G8R8X8_TYPELESS,
|
||||
DXGI_FORMAT_B8G8R8X8_UNORM_SRGB,
|
||||
DXGI_FORMAT_BC6H_TYPELESS,
|
||||
DXGI_FORMAT_BC6H_UF16,
|
||||
DXGI_FORMAT_BC6H_SF16,
|
||||
DXGI_FORMAT_BC7_TYPELESS,
|
||||
DXGI_FORMAT_BC7_UNORM,
|
||||
DXGI_FORMAT_BC7_UNORM_SRGB,
|
||||
DXGI_FORMAT_AYUV,
|
||||
DXGI_FORMAT_Y410,
|
||||
DXGI_FORMAT_Y416,
|
||||
DXGI_FORMAT_NV12,
|
||||
DXGI_FORMAT_P010,
|
||||
DXGI_FORMAT_P016,
|
||||
DXGI_FORMAT_420_OPAQUE,
|
||||
DXGI_FORMAT_YUY2,
|
||||
DXGI_FORMAT_Y210,
|
||||
DXGI_FORMAT_Y216,
|
||||
DXGI_FORMAT_NV11,
|
||||
DXGI_FORMAT_AI44,
|
||||
DXGI_FORMAT_IA44,
|
||||
DXGI_FORMAT_P8,
|
||||
DXGI_FORMAT_A8P8,
|
||||
DXGI_FORMAT_B4G4R4A4_UNORM,
|
||||
DXGI_FORMAT_P208,
|
||||
DXGI_FORMAT_V208,
|
||||
DXGI_FORMAT_V408,
|
||||
DXGI_FORMAT_FORCE_UINT
|
||||
} DXGI_FORMAT;
|
||||
|
||||
static const char *DxgiFormatToString(DXGI_FORMAT dxgiFormat)
|
||||
{
|
||||
static const char *names[] = {
|
||||
"DXGI_FORMAT_UNKNOWN",
|
||||
"DXGI_FORMAT_R32G32B32A32_TYPELESS",
|
||||
"DXGI_FORMAT_R32G32B32A32_FLOAT",
|
||||
"DXGI_FORMAT_R32G32B32A32_UINT",
|
||||
"DXGI_FORMAT_R32G32B32A32_SINT",
|
||||
"DXGI_FORMAT_R32G32B32_TYPELESS",
|
||||
"DXGI_FORMAT_R32G32B32_FLOAT",
|
||||
"DXGI_FORMAT_R32G32B32_UINT",
|
||||
"DXGI_FORMAT_R32G32B32_SINT",
|
||||
"DXGI_FORMAT_R16G16B16A16_TYPELESS",
|
||||
"DXGI_FORMAT_R16G16B16A16_FLOAT",
|
||||
"DXGI_FORMAT_R16G16B16A16_UNORM",
|
||||
"DXGI_FORMAT_R16G16B16A16_UINT",
|
||||
"DXGI_FORMAT_R16G16B16A16_SNORM",
|
||||
"DXGI_FORMAT_R16G16B16A16_SINT",
|
||||
"DXGI_FORMAT_R32G32_TYPELESS",
|
||||
"DXGI_FORMAT_R32G32_FLOAT",
|
||||
"DXGI_FORMAT_R32G32_UINT",
|
||||
"DXGI_FORMAT_R32G32_SINT",
|
||||
"DXGI_FORMAT_R32G8X24_TYPELESS",
|
||||
"DXGI_FORMAT_D32_FLOAT_S8X24_UINT",
|
||||
"DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS",
|
||||
"DXGI_FORMAT_X32_TYPELESS_G8X24_UINT",
|
||||
"DXGI_FORMAT_R10G10B10A2_TYPELESS",
|
||||
"DXGI_FORMAT_R10G10B10A2_UNORM",
|
||||
"DXGI_FORMAT_R10G10B10A2_UINT",
|
||||
"DXGI_FORMAT_R11G11B10_FLOAT",
|
||||
"DXGI_FORMAT_R8G8B8A8_TYPELESS",
|
||||
"DXGI_FORMAT_R8G8B8A8_UNORM",
|
||||
"DXGI_FORMAT_R8G8B8A8_UNORM_SRGB",
|
||||
"DXGI_FORMAT_R8G8B8A8_UINT",
|
||||
"DXGI_FORMAT_R8G8B8A8_SNORM",
|
||||
"DXGI_FORMAT_R8G8B8A8_SINT",
|
||||
"DXGI_FORMAT_R16G16_TYPELESS",
|
||||
"DXGI_FORMAT_R16G16_FLOAT",
|
||||
"DXGI_FORMAT_R16G16_UNORM",
|
||||
"DXGI_FORMAT_R16G16_UINT",
|
||||
"DXGI_FORMAT_R16G16_SNORM",
|
||||
"DXGI_FORMAT_R16G16_SINT",
|
||||
"DXGI_FORMAT_R32_TYPELESS",
|
||||
"DXGI_FORMAT_D32_FLOAT",
|
||||
"DXGI_FORMAT_R32_FLOAT",
|
||||
"DXGI_FORMAT_R32_UINT",
|
||||
"DXGI_FORMAT_R32_SINT",
|
||||
"DXGI_FORMAT_R24G8_TYPELESS",
|
||||
"DXGI_FORMAT_D24_UNORM_S8_UINT",
|
||||
"DXGI_FORMAT_R24_UNORM_X8_TYPELESS",
|
||||
"DXGI_FORMAT_X24_TYPELESS_G8_UINT",
|
||||
"DXGI_FORMAT_R8G8_TYPELESS",
|
||||
"DXGI_FORMAT_R8G8_UNORM",
|
||||
"DXGI_FORMAT_R8G8_UINT",
|
||||
"DXGI_FORMAT_R8G8_SNORM",
|
||||
"DXGI_FORMAT_R8G8_SINT",
|
||||
"DXGI_FORMAT_R16_TYPELESS",
|
||||
"DXGI_FORMAT_R16_FLOAT",
|
||||
"DXGI_FORMAT_D16_UNORM",
|
||||
"DXGI_FORMAT_R16_UNORM",
|
||||
"DXGI_FORMAT_R16_UINT",
|
||||
"DXGI_FORMAT_R16_SNORM",
|
||||
"DXGI_FORMAT_R16_SINT",
|
||||
"DXGI_FORMAT_R8_TYPELESS",
|
||||
"DXGI_FORMAT_R8_UNORM",
|
||||
"DXGI_FORMAT_R8_UINT",
|
||||
"DXGI_FORMAT_R8_SNORM",
|
||||
"DXGI_FORMAT_R8_SINT",
|
||||
"DXGI_FORMAT_A8_UNORM",
|
||||
"DXGI_FORMAT_R1_UNORM",
|
||||
"DXGI_FORMAT_R9G9B9E5_SHAREDEXP",
|
||||
"DXGI_FORMAT_R8G8_B8G8_UNORM",
|
||||
"DXGI_FORMAT_G8R8_G8B8_UNORM",
|
||||
"DXGI_FORMAT_BC1_TYPELESS",
|
||||
"DXGI_FORMAT_BC1_UNORM",
|
||||
"DXGI_FORMAT_BC1_UNORM_SRGB",
|
||||
"DXGI_FORMAT_BC2_TYPELESS",
|
||||
"DXGI_FORMAT_BC2_UNORM",
|
||||
"DXGI_FORMAT_BC2_UNORM_SRGB",
|
||||
"DXGI_FORMAT_BC3_TYPELESS",
|
||||
"DXGI_FORMAT_BC3_UNORM",
|
||||
"DXGI_FORMAT_BC3_UNORM_SRGB",
|
||||
"DXGI_FORMAT_BC4_TYPELESS",
|
||||
"DXGI_FORMAT_BC4_UNORM",
|
||||
"DXGI_FORMAT_BC4_SNORM",
|
||||
"DXGI_FORMAT_BC5_TYPELESS",
|
||||
"DXGI_FORMAT_BC5_UNORM",
|
||||
"DXGI_FORMAT_BC5_SNORM",
|
||||
"DXGI_FORMAT_B5G6R5_UNORM",
|
||||
"DXGI_FORMAT_B5G5R5A1_UNORM",
|
||||
"DXGI_FORMAT_B8G8R8A8_UNORM",
|
||||
"DXGI_FORMAT_B8G8R8X8_UNORM",
|
||||
"DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM",
|
||||
"DXGI_FORMAT_B8G8R8A8_TYPELESS",
|
||||
"DXGI_FORMAT_B8G8R8A8_UNORM_SRGB",
|
||||
"DXGI_FORMAT_B8G8R8X8_TYPELESS",
|
||||
"DXGI_FORMAT_B8G8R8X8_UNORM_SRGB",
|
||||
"DXGI_FORMAT_BC6H_TYPELESS",
|
||||
"DXGI_FORMAT_BC6H_UF16",
|
||||
"DXGI_FORMAT_BC6H_SF16",
|
||||
"DXGI_FORMAT_BC7_TYPELESS",
|
||||
"DXGI_FORMAT_BC7_UNORM",
|
||||
"DXGI_FORMAT_BC7_UNORM_SRGB",
|
||||
"DXGI_FORMAT_AYUV",
|
||||
"DXGI_FORMAT_Y410",
|
||||
"DXGI_FORMAT_Y416",
|
||||
"DXGI_FORMAT_NV12",
|
||||
"DXGI_FORMAT_P010",
|
||||
"DXGI_FORMAT_P016",
|
||||
"DXGI_FORMAT_420_OPAQUE",
|
||||
"DXGI_FORMAT_YUY2",
|
||||
"DXGI_FORMAT_Y210",
|
||||
"DXGI_FORMAT_Y216",
|
||||
"DXGI_FORMAT_NV11",
|
||||
"DXGI_FORMAT_AI44",
|
||||
"DXGI_FORMAT_IA44",
|
||||
"DXGI_FORMAT_P8",
|
||||
"DXGI_FORMAT_A8P8",
|
||||
"DXGI_FORMAT_B4G4R4A4_UNORM",
|
||||
"DXGI_FORMAT_P208",
|
||||
"DXGI_FORMAT_V208",
|
||||
"DXGI_FORMAT_V408",
|
||||
"DXGI_FORMAT_FORCE_UINT",
|
||||
};
|
||||
int index = (int)dxgiFormat;
|
||||
if (index >= 0 && index < sizeof(names) / sizeof(names[0]))
|
||||
return names[index];
|
||||
return "(Unknown)";
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
D3D10_RESOURCE_DIMENSION_UNKNOWN,
|
||||
D3D10_RESOURCE_DIMENSION_BUFFER,
|
||||
D3D10_RESOURCE_DIMENSION_TEXTURE1D,
|
||||
D3D10_RESOURCE_DIMENSION_TEXTURE2D,
|
||||
D3D10_RESOURCE_DIMENSION_TEXTURE3D
|
||||
} D3D10_RESOURCE_DIMENSION;
|
||||
|
||||
static const char *ResourceDimensionToString(D3D10_RESOURCE_DIMENSION resourceDimension)
|
||||
{
|
||||
static const char *names[] = {
|
||||
"D3D10_RESOURCE_DIMENSION_UNKNOWN",
|
||||
"D3D10_RESOURCE_DIMENSION_BUFFER",
|
||||
"D3D10_RESOURCE_DIMENSION_TEXTURE1D",
|
||||
"D3D10_RESOURCE_DIMENSION_TEXTURE2D",
|
||||
"D3D10_RESOURCE_DIMENSION_TEXTURE3D",
|
||||
};
|
||||
int index = (int)resourceDimension;
|
||||
if (index >= 0 && index < sizeof(names) / sizeof(names[0]))
|
||||
return names[index];
|
||||
return "(Unknown)";
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
D3D10_RESOURCE_MISC_GENERATE_MIPS,
|
||||
D3D10_RESOURCE_MISC_SHARED,
|
||||
D3D10_RESOURCE_MISC_TEXTURECUBE,
|
||||
D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX,
|
||||
D3D10_RESOURCE_MISC_GDI_COMPATIBLE
|
||||
} D3D10_RESOURCE_MISC_FLAG;
|
||||
|
||||
static const char *MiscFlagToString(UINT miscFlag)
|
||||
{
|
||||
static const char *names[] = {
|
||||
"D3D10_RESOURCE_MISC_GENERATE_MIPS",
|
||||
"D3D10_RESOURCE_MISC_SHARED",
|
||||
"D3D10_RESOURCE_MISC_TEXTURECUBE",
|
||||
"D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX",
|
||||
"D3D10_RESOURCE_MISC_GDI_COMPATIBLE",
|
||||
};
|
||||
int index = (int)miscFlag;
|
||||
if (index >= 0 && index < sizeof(names) / sizeof(names[0]))
|
||||
return names[index];
|
||||
return "(Unknown)";
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
DXGI_FORMAT dxgiFormat;
|
||||
D3D10_RESOURCE_DIMENSION resourceDimension;
|
||||
UINT miscFlag;
|
||||
UINT arraySize;
|
||||
UINT miscFlags2;
|
||||
} DDS_HEADER_DXT10;
|
||||
|
||||
typedef struct {
|
||||
DWORD dwMagic;
|
||||
DDS_HEADER header;
|
||||
DDS_HEADER_DXT10 header10;
|
||||
} DDS_FILE_HEADER;
|
||||
|
||||
DdsFileReader::DdsFileReader(const QString &filename) :
|
||||
m_filename(filename)
|
||||
{
|
||||
}
|
||||
|
||||
QOpenGLTexture *DdsFileReader::createOpenGLTexture()
|
||||
{
|
||||
QFile file(m_filename);
|
||||
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
qDebug() << "Open" << m_filename << "failed";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DDS_FILE_HEADER fileHeader;
|
||||
if (sizeof(fileHeader) != file.read((char *)&fileHeader, sizeof(fileHeader))) {
|
||||
qDebug() << "Read DDS file hader failed";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (0x20534444 != qFromLittleEndian<quint32>(&fileHeader.dwMagic)) {
|
||||
qDebug() << "Not a DDS file";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (0x30315844 != qFromLittleEndian<quint32>(&fileHeader.header.ddspf.dwFourCC)) {
|
||||
qDebug() << "Unsupported DDS file, expected DX10 file";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto caps2 = qFromLittleEndian<quint32>(&fileHeader.header.dwCaps2);
|
||||
if (!(DDSCAPS2_CUBEMAP & caps2)) {
|
||||
qDebug() << "Unsupported DDS file, expected CUBEMAP file";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//qDebug() << "Start anyalize DDS file...";
|
||||
|
||||
int width = qFromLittleEndian<quint32>(&fileHeader.header.dwWidth);
|
||||
int height = qFromLittleEndian<quint32>(&fileHeader.header.dwHeight);
|
||||
|
||||
//qDebug() << "DDS size:" << width << "X" << height;
|
||||
|
||||
//auto pitchOrLinearSize = qFromLittleEndian<quint32>(&fileHeader.header.dwPitchOrLinearSize);
|
||||
//qDebug() << "DDS pitch or linear size:" << pitchOrLinearSize;
|
||||
|
||||
auto arraySize = qFromLittleEndian<quint32>(&fileHeader.header10.arraySize);
|
||||
//qDebug() << "DDS array size:" << arraySize;
|
||||
|
||||
auto mipMapCount = qFromLittleEndian<quint32>(&fileHeader.header.dwMipMapCount);
|
||||
//qDebug() << "DDS mip map count:" << mipMapCount;
|
||||
|
||||
DXGI_FORMAT dxgiFormat = (DXGI_FORMAT)qFromLittleEndian<quint32>(&fileHeader.header10.dxgiFormat);
|
||||
//qDebug() << "DDS dxgi format:" << DxgiFormatToString(dxgiFormat);
|
||||
//qDebug() << "DDS resource dimension:" << ResourceDimensionToString((D3D10_RESOURCE_DIMENSION)qFromLittleEndian<quint32>(&fileHeader.header10.resourceDimension));
|
||||
//qDebug() << "DDS misc flag:" << MiscFlagToString((UINT)qFromLittleEndian<quint32>(&fileHeader.header10.miscFlag));
|
||||
|
||||
auto faces = 0;
|
||||
if (DDSCAPS2_CUBEMAP_POSITIVEX & caps2) {
|
||||
//qDebug() << "DDS found +x";
|
||||
++faces;
|
||||
}
|
||||
if (DDSCAPS2_CUBEMAP_NEGATIVEX & caps2) {
|
||||
//qDebug() << "DDS found -x";
|
||||
++faces;
|
||||
}
|
||||
if (DDSCAPS2_CUBEMAP_POSITIVEY & caps2) {
|
||||
//qDebug() << "DDS found +y";
|
||||
++faces;
|
||||
}
|
||||
if (DDSCAPS2_CUBEMAP_NEGATIVEY & caps2) {
|
||||
//qDebug() << "DDS found -y";
|
||||
++faces;
|
||||
}
|
||||
if (DDSCAPS2_CUBEMAP_POSITIVEZ & caps2) {
|
||||
//qDebug() << "DDS found +z";
|
||||
++faces;
|
||||
}
|
||||
if (DDSCAPS2_CUBEMAP_NEGATIVEZ & caps2) {
|
||||
//qDebug() << "DDS found -z";
|
||||
++faces;
|
||||
}
|
||||
|
||||
if (6 != faces) {
|
||||
qDebug() << "Unsupported DDS file, expected six faces";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (1 != arraySize) {
|
||||
qDebug() << "Unsupported DDS file, expected one layer";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (DXGI_FORMAT_R16G16B16A16_FLOAT != dxgiFormat) {
|
||||
qDebug() << "Unsupported DDS file, expected dxgi format: DXGI_FORMAT_R16G16B16A16_FLOAT";
|
||||
return nullptr;
|
||||
}
|
||||
int components = 8;
|
||||
int oneFaceSize = 0;
|
||||
auto calculateOneFaceSizeAtLevel = [=](int level) {
|
||||
return qMax(width >> level, 1) * qMax(height >> level, 1) * components;
|
||||
};
|
||||
for (auto level = 0; level < mipMapCount; ++level) {
|
||||
oneFaceSize += calculateOneFaceSizeAtLevel(level);
|
||||
}
|
||||
int totalSize = arraySize * faces * oneFaceSize;
|
||||
const QByteArray data = file.read(totalSize);
|
||||
if (data.size() < totalSize) {
|
||||
qDebug() << "DDS file invalid, expected total size:" << totalSize << "read size:" << data.size();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int depth = 1;
|
||||
|
||||
QOpenGLTexture* texture = new QOpenGLTexture(QOpenGLTexture::TargetCubeMap);
|
||||
texture->setFormat(QOpenGLTexture::RGBA16F);
|
||||
texture->setSize(width, height, depth);
|
||||
texture->setAutoMipMapGenerationEnabled(false);
|
||||
texture->setMipBaseLevel(0);
|
||||
texture->setMipMaxLevel(mipMapCount - 1);
|
||||
texture->setMipLevels(mipMapCount);
|
||||
|
||||
if (!texture->create()) {
|
||||
qDebug() << "QOpenGLTexture::create failed";
|
||||
delete texture;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
texture->allocateStorage();
|
||||
if (!texture->isStorageAllocated()) {
|
||||
qDebug() << "QOpenGLTexture::isStorageAllocated false";
|
||||
delete texture;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint64_t dataOffset = 0;
|
||||
for (int layer = 0; layer < arraySize; ++layer) {
|
||||
for (int face = 0; face < faces; ++face) {
|
||||
for (int level = 0; level < mipMapCount; ++level) {
|
||||
QOpenGLPixelTransferOptions uploadOptions;
|
||||
uploadOptions.setAlignment(1);
|
||||
texture->setData(level,
|
||||
layer,
|
||||
static_cast<QOpenGLTexture::CubeMapFace>(QOpenGLTexture::CubeMapPositiveX + face),
|
||||
QOpenGLTexture::RGBA,
|
||||
QOpenGLTexture::Float16,
|
||||
data.constData() + dataOffset,
|
||||
&uploadOptions);
|
||||
dataOffset += calculateOneFaceSizeAtLevel(level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return texture;
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
#ifndef DUST3D_DDS_FILE_H
|
||||
#define DUST3D_DDS_FILE_H
|
||||
#include <QString>
|
||||
#include <QOpenGLTexture>
|
||||
|
||||
class DdsFileReader
|
||||
{
|
||||
public:
|
||||
DdsFileReader(const QString &filename);
|
||||
QOpenGLTexture *createOpenGLTexture();
|
||||
private:
|
||||
QString m_filename;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -7,7 +7,7 @@
|
|||
#include <QApplication>
|
||||
#include <QVector3D>
|
||||
#include <functional>
|
||||
#include <QBuffer>
|
||||
#include <QtCore/qbuffer.h>
|
||||
#include <QElapsedTimer>
|
||||
#include <queue>
|
||||
#include "document.h"
|
||||
|
@ -57,7 +57,6 @@ Document::Document() :
|
|||
m_postProcessedOutcome(new Outcome),
|
||||
m_resultTextureMesh(nullptr),
|
||||
m_textureImageUpdateVersion(0),
|
||||
m_sharedContextWidget(nullptr),
|
||||
m_allPositionRelatedLocksEnabled(true),
|
||||
m_smoothNormal(!Preferences::instance().flatShading()),
|
||||
m_rigGenerator(nullptr),
|
||||
|
@ -3311,11 +3310,6 @@ void Document::checkExportReadyState()
|
|||
emit exportReady();
|
||||
}
|
||||
|
||||
void Document::setSharedContextWidget(QOpenGLWidget *sharedContextWidget)
|
||||
{
|
||||
m_sharedContextWidget = sharedContextWidget;
|
||||
}
|
||||
|
||||
void Document::collectComponentDescendantParts(QUuid componentId, std::vector<QUuid> &partIds) const
|
||||
{
|
||||
const Component *component = findComponent(componentId);
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#include <QImage>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <QOpenGLWidget>
|
||||
#include <QPolygon>
|
||||
#include "snapshot.h"
|
||||
#include "meshloader.h"
|
||||
|
@ -577,7 +576,6 @@ public:
|
|||
const std::vector<RiggerBone> *resultRigBones() const;
|
||||
const std::map<int, RiggerVertexWeights> *resultRigWeights() const;
|
||||
void updateTurnaround(const QImage &image);
|
||||
void setSharedContextWidget(QOpenGLWidget *sharedContextWidget);
|
||||
bool hasPastableMaterialsInClipboard() const;
|
||||
bool hasPastablePosesInClipboard() const;
|
||||
bool hasPastableMotionsInClipboard() const;
|
||||
|
@ -781,7 +779,6 @@ private: // need initialize
|
|||
Outcome *m_postProcessedOutcome;
|
||||
MeshLoader *m_resultTextureMesh;
|
||||
unsigned long long m_textureImageUpdateVersion;
|
||||
QOpenGLWidget *m_sharedContextWidget;
|
||||
QUuid m_currentCanvasComponentId;
|
||||
bool m_allPositionRelatedLocksEnabled;
|
||||
bool m_smoothNormal;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#include <QPushButton>
|
||||
#include <QFileDialog>
|
||||
#include <QTabWidget>
|
||||
#include <QBuffer>
|
||||
#include <QtCore/qbuffer.h>
|
||||
#include <QMessageBox>
|
||||
#include <QTimer>
|
||||
#include <QMenuBar>
|
||||
|
@ -311,6 +311,7 @@ DocumentWindow::DocumentWindow() :
|
|||
m_modelRenderWidget->move(DocumentWindow::m_modelRenderWidgetInitialX, DocumentWindow::m_modelRenderWidgetInitialY);
|
||||
m_modelRenderWidget->setMousePickRadius(m_document->mousePickRadius());
|
||||
m_modelRenderWidget->toggleWireframe();
|
||||
m_modelRenderWidget->enableEnvironmentLight();
|
||||
|
||||
connect(m_modelRenderWidget, &ModelWidget::mouseRayChanged, m_document,
|
||||
[=](const QVector3D &nearPosition, const QVector3D &farPosition) {
|
||||
|
@ -343,8 +344,6 @@ DocumentWindow::DocumentWindow() :
|
|||
m_graphicsWidget->setModelWidget(m_modelRenderWidget);
|
||||
containerWidget->setModelWidget(m_modelRenderWidget);
|
||||
|
||||
m_document->setSharedContextWidget(m_modelRenderWidget);
|
||||
|
||||
setTabPosition(Qt::RightDockWidgetArea, QTabWidget::East);
|
||||
|
||||
QDockWidget *partTreeDocker = new QDockWidget(tr("Parts"), this);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#include <fbxproperty.h>
|
||||
#include <QDateTime>
|
||||
#include <QtMath>
|
||||
#include <QBuffer>
|
||||
#include <QtCore/qbuffer.h>
|
||||
#include <QByteArray>
|
||||
#include <QFileInfo>
|
||||
#include "fbxfile.h"
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include <QDataStream>
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
#include <QBuffer>
|
||||
#include <QtCore/qbuffer.h>
|
||||
#include "glbfile.h"
|
||||
#include "version.h"
|
||||
#include "util.h"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include <map>
|
||||
#include <QMutex>
|
||||
#include <QMutexLocker>
|
||||
#include <QBuffer>
|
||||
#include <QtCore/qbuffer.h>
|
||||
#include "imageforever.h"
|
||||
|
||||
struct ImageForeverItem
|
||||
|
|
|
@ -19,10 +19,10 @@ int main(int argc, char ** argv)
|
|||
if (translator.load(QLocale(), QLatin1String("dust3d"), QLatin1String("_"), QLatin1String(":/languages")))
|
||||
app.installTranslator(&translator);
|
||||
|
||||
//QSurfaceFormat format = QSurfaceFormat::defaultFormat();
|
||||
//format.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile);
|
||||
//format.setVersion(3, 3);
|
||||
//QSurfaceFormat::setDefaultFormat(format);
|
||||
QSurfaceFormat format = QSurfaceFormat::defaultFormat();
|
||||
format.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile);
|
||||
format.setVersion(3, 3);
|
||||
QSurfaceFormat::setDefaultFormat(format);
|
||||
|
||||
// QuantumCD/Qt 5 Dark Fusion Palette
|
||||
// https://gist.github.com/QuantumCD/6245215
|
||||
|
|
|
@ -48,6 +48,7 @@ MaterialEditWidget::MaterialEditWidget(const Document *document, QWidget *parent
|
|||
m_previewWidget->setMinimumSize(128, 128);
|
||||
m_previewWidget->resize(512, 512);
|
||||
m_previewWidget->move(-128, -128);
|
||||
m_previewWidget->enableEnvironmentLight();
|
||||
|
||||
QFont nameFont;
|
||||
nameFont.setWeight(QFont::Light);
|
||||
|
|
|
@ -12,6 +12,7 @@ MaterialWidget::MaterialWidget(const Document *document, QUuid materialId) :
|
|||
m_previewWidget->setFixedSize(Theme::materialPreviewImageSize, Theme::materialPreviewImageSize);
|
||||
m_previewWidget->enableMove(false);
|
||||
m_previewWidget->enableZoom(false);
|
||||
m_previewWidget->enableEnvironmentLight();
|
||||
|
||||
m_nameLabel = new QLabel;
|
||||
m_nameLabel->setAlignment(Qt::AlignCenter);
|
||||
|
|
|
@ -5,8 +5,9 @@
|
|||
#include <map>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QSurfaceFormat>
|
||||
#include "modelmeshbinder.h"
|
||||
#include "ds3file.h"
|
||||
#include "ddsfile.h"
|
||||
|
||||
ModelMeshBinder::ModelMeshBinder(bool toolEnabled) :
|
||||
m_toolEnabled(toolEnabled)
|
||||
|
@ -20,6 +21,8 @@ ModelMeshBinder::~ModelMeshBinder()
|
|||
delete m_texture;
|
||||
delete m_normalMap;
|
||||
delete m_metalnessRoughnessAmbientOcclusionMap;
|
||||
delete m_environmentIrradianceMap;
|
||||
delete m_environmentSpecularMap;
|
||||
}
|
||||
|
||||
void ModelMeshBinder::updateMesh(MeshLoader *mesh)
|
||||
|
@ -53,6 +56,11 @@ void ModelMeshBinder::initialize()
|
|||
m_vaoTool.create();
|
||||
}
|
||||
|
||||
void ModelMeshBinder::enableEnvironmentLight()
|
||||
{
|
||||
m_environmentLightEnabled = true;
|
||||
}
|
||||
|
||||
void ModelMeshBinder::paint(ModelShaderProgram *program)
|
||||
{
|
||||
MeshLoader *newMesh = nullptr;
|
||||
|
@ -72,7 +80,6 @@ void ModelMeshBinder::paint(ModelShaderProgram *program)
|
|||
delete m_mesh;
|
||||
m_mesh = newMesh;
|
||||
if (m_mesh) {
|
||||
|
||||
m_hasTexture = nullptr != m_mesh->textureImage();
|
||||
delete m_texture;
|
||||
m_texture = nullptr;
|
||||
|
@ -101,7 +108,20 @@ void ModelMeshBinder::paint(ModelShaderProgram *program)
|
|||
if (nullptr != m_mesh->metalnessRoughnessAmbientOcclusionImage() &&
|
||||
(m_hasMetalnessMap || m_hasRoughnessMap || m_hasAmbientOcclusionMap))
|
||||
m_metalnessRoughnessAmbientOcclusionMap = new QOpenGLTexture(*m_mesh->metalnessRoughnessAmbientOcclusionImage());
|
||||
|
||||
delete m_environmentIrradianceMap;
|
||||
m_environmentIrradianceMap = nullptr;
|
||||
delete m_environmentSpecularMap;
|
||||
m_environmentSpecularMap = nullptr;
|
||||
if (program->isCoreProfile() && m_environmentLightEnabled &&
|
||||
(m_hasMetalnessMap || m_hasRoughnessMap)) {
|
||||
DdsFileReader irradianceFile(":/resources/cedar_bridge_irradiance.dds");
|
||||
m_environmentIrradianceMap = irradianceFile.createOpenGLTexture();
|
||||
|
||||
DdsFileReader specularFile(":/resources/cedar_bridge_specular.dds");
|
||||
m_environmentSpecularMap = specularFile.createOpenGLTexture();
|
||||
}
|
||||
|
||||
{
|
||||
QOpenGLVertexArrayObject::Binder vaoBinder(&m_vaoTriangle);
|
||||
if (m_vboTriangle.isCreated())
|
||||
|
@ -215,6 +235,10 @@ void ModelMeshBinder::paint(ModelShaderProgram *program)
|
|||
program->setUniformValue(program->metalnessMapEnabledLoc(), 0);
|
||||
program->setUniformValue(program->roughnessMapEnabledLoc(), 0);
|
||||
program->setUniformValue(program->ambientOcclusionMapEnabledLoc(), 0);
|
||||
if (program->isCoreProfile()) {
|
||||
program->setUniformValue(program->environmentIrradianceMapEnabledLoc(), 0);
|
||||
program->setUniformValue(program->environmentSpecularMapEnabledLoc(), 0);
|
||||
}
|
||||
f->glDrawArrays(GL_LINES, 0, m_renderEdgeVertexCount);
|
||||
}
|
||||
}
|
||||
|
@ -246,6 +270,22 @@ void ModelMeshBinder::paint(ModelShaderProgram *program)
|
|||
program->setUniformValue(program->metalnessMapEnabledLoc(), m_hasMetalnessMap ? 1 : 0);
|
||||
program->setUniformValue(program->roughnessMapEnabledLoc(), m_hasRoughnessMap ? 1 : 0);
|
||||
program->setUniformValue(program->ambientOcclusionMapEnabledLoc(), m_hasAmbientOcclusionMap ? 1 : 0);
|
||||
if (program->isCoreProfile()) {
|
||||
if (nullptr != m_environmentIrradianceMap) {
|
||||
m_environmentIrradianceMap->bind(3);
|
||||
program->setUniformValue(program->environmentIrradianceMapIdLoc(), 3);
|
||||
program->setUniformValue(program->environmentIrradianceMapEnabledLoc(), 1);
|
||||
} else {
|
||||
program->setUniformValue(program->environmentIrradianceMapEnabledLoc(), 0);
|
||||
}
|
||||
if (nullptr != m_environmentSpecularMap) {
|
||||
m_environmentSpecularMap->bind(4);
|
||||
program->setUniformValue(program->environmentSpecularMapIdLoc(), 4);
|
||||
program->setUniformValue(program->environmentSpecularMapEnabledLoc(), 1);
|
||||
} else {
|
||||
program->setUniformValue(program->environmentSpecularMapEnabledLoc(), 0);
|
||||
}
|
||||
}
|
||||
f->glDrawArrays(GL_TRIANGLES, 0, m_renderTriangleVertexCount);
|
||||
}
|
||||
if (m_toolEnabled) {
|
||||
|
@ -257,6 +297,10 @@ void ModelMeshBinder::paint(ModelShaderProgram *program)
|
|||
program->setUniformValue(program->metalnessMapEnabledLoc(), 0);
|
||||
program->setUniformValue(program->roughnessMapEnabledLoc(), 0);
|
||||
program->setUniformValue(program->ambientOcclusionMapEnabledLoc(), 0);
|
||||
if (program->isCoreProfile()) {
|
||||
program->setUniformValue(program->environmentIrradianceMapEnabledLoc(), 0);
|
||||
program->setUniformValue(program->environmentSpecularMapEnabledLoc(), 0);
|
||||
}
|
||||
f->glDrawArrays(GL_TRIANGLES, 0, m_renderToolVertexCount);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ public:
|
|||
bool isWireframesVisible();
|
||||
void enableCheckUv();
|
||||
void disableCheckUv();
|
||||
void enableEnvironmentLight();
|
||||
bool isCheckUvEnabled();
|
||||
void reloadMesh();
|
||||
private:
|
||||
|
@ -42,6 +43,9 @@ private:
|
|||
QOpenGLTexture *m_metalnessRoughnessAmbientOcclusionMap = nullptr;
|
||||
bool m_toolEnabled = false;
|
||||
bool m_checkUvEnabled = false;
|
||||
bool m_environmentLightEnabled = false;
|
||||
QOpenGLTexture *m_environmentIrradianceMap = nullptr;
|
||||
QOpenGLTexture *m_environmentSpecularMap = nullptr;
|
||||
private:
|
||||
QOpenGLVertexArrayObject m_vaoTriangle;
|
||||
QOpenGLBuffer m_vboTriangle;
|
||||
|
|
|
@ -17,11 +17,17 @@ const QString &ModelShaderProgram::loadShaderSource(const QString &name)
|
|||
return insertResult.first->second;
|
||||
}
|
||||
|
||||
bool ModelShaderProgram::isCoreProfile()
|
||||
{
|
||||
return m_isCoreProfile;
|
||||
}
|
||||
|
||||
ModelShaderProgram::ModelShaderProgram()
|
||||
{
|
||||
if (QSurfaceFormat::defaultFormat().profile() == QSurfaceFormat::CoreProfile) {
|
||||
this->addShaderFromSourceCode(QOpenGLShader::Vertex, loadShaderSource(":/shaders/default.core.vert"));
|
||||
this->addShaderFromSourceCode(QOpenGLShader::Fragment, loadShaderSource(":/shaders/default.core.frag"));
|
||||
m_isCoreProfile = true;
|
||||
} else {
|
||||
this->addShaderFromSourceCode(QOpenGLShader::Vertex, loadShaderSource(":/shaders/default.vert"));
|
||||
this->addShaderFromSourceCode(QOpenGLShader::Fragment, loadShaderSource(":/shaders/default.frag"));
|
||||
|
@ -53,6 +59,12 @@ ModelShaderProgram::ModelShaderProgram()
|
|||
m_mousePickEnabledLoc = this->uniformLocation("mousePickEnabled");
|
||||
m_mousePickTargetPositionLoc = this->uniformLocation("mousePickTargetPosition");
|
||||
m_mousePickRadiusLoc = this->uniformLocation("mousePickRadius");
|
||||
if (m_isCoreProfile) {
|
||||
m_environmentIrradianceMapIdLoc = this->uniformLocation("environmentIrradianceMapId");
|
||||
m_environmentIrradianceMapEnabledLoc = this->uniformLocation("environmentIrradianceMapEnabled");
|
||||
m_environmentSpecularMapIdLoc = this->uniformLocation("environmentSpecularMapId");
|
||||
m_environmentSpecularMapEnabledLoc = this->uniformLocation("environmentSpecularMapEnabled");
|
||||
}
|
||||
}
|
||||
|
||||
int ModelShaderProgram::projectionMatrixLoc()
|
||||
|
@ -134,3 +146,24 @@ int ModelShaderProgram::mousePickRadiusLoc()
|
|||
{
|
||||
return m_mousePickRadiusLoc;
|
||||
}
|
||||
|
||||
int ModelShaderProgram::environmentIrradianceMapIdLoc()
|
||||
{
|
||||
return m_environmentIrradianceMapIdLoc;
|
||||
}
|
||||
|
||||
int ModelShaderProgram::environmentIrradianceMapEnabledLoc()
|
||||
{
|
||||
return m_environmentIrradianceMapEnabledLoc;
|
||||
}
|
||||
|
||||
int ModelShaderProgram::environmentSpecularMapIdLoc()
|
||||
{
|
||||
return m_environmentSpecularMapIdLoc;
|
||||
}
|
||||
|
||||
int ModelShaderProgram::environmentSpecularMapEnabledLoc()
|
||||
{
|
||||
return m_environmentSpecularMapEnabledLoc;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,24 +23,34 @@ public:
|
|||
int mousePickEnabledLoc();
|
||||
int mousePickTargetPositionLoc();
|
||||
int mousePickRadiusLoc();
|
||||
int environmentIrradianceMapIdLoc();
|
||||
int environmentIrradianceMapEnabledLoc();
|
||||
int environmentSpecularMapIdLoc();
|
||||
int environmentSpecularMapEnabledLoc();
|
||||
bool isCoreProfile();
|
||||
static const QString &loadShaderSource(const QString &name);
|
||||
private:
|
||||
int m_projectionMatrixLoc;
|
||||
int m_modelMatrixLoc;
|
||||
int m_normalMatrixLoc;
|
||||
int m_viewMatrixLoc;
|
||||
int m_lightPosLoc;
|
||||
int m_textureIdLoc;
|
||||
int m_textureEnabledLoc;
|
||||
int m_normalMapEnabledLoc;
|
||||
int m_normalMapIdLoc;
|
||||
int m_metalnessMapEnabledLoc;
|
||||
int m_roughnessMapEnabledLoc;
|
||||
int m_ambientOcclusionMapEnabledLoc;
|
||||
int m_metalnessRoughnessAmbientOcclusionMapIdLoc;
|
||||
int m_mousePickEnabledLoc;
|
||||
int m_mousePickTargetPositionLoc;
|
||||
int m_mousePickRadiusLoc;
|
||||
bool m_isCoreProfile = false;
|
||||
int m_projectionMatrixLoc = 0;
|
||||
int m_modelMatrixLoc = 0;
|
||||
int m_normalMatrixLoc = 0;
|
||||
int m_viewMatrixLoc = 0;
|
||||
int m_lightPosLoc = 0;
|
||||
int m_textureIdLoc = 0;
|
||||
int m_textureEnabledLoc = 0;
|
||||
int m_normalMapEnabledLoc = 0;
|
||||
int m_normalMapIdLoc = 0;
|
||||
int m_metalnessMapEnabledLoc = 0;
|
||||
int m_roughnessMapEnabledLoc = 0;
|
||||
int m_ambientOcclusionMapEnabledLoc = 0;
|
||||
int m_metalnessRoughnessAmbientOcclusionMapIdLoc = 0;
|
||||
int m_mousePickEnabledLoc = 0;
|
||||
int m_mousePickTargetPositionLoc = 0;
|
||||
int m_mousePickRadiusLoc = 0;
|
||||
int m_environmentIrradianceMapIdLoc = 0;
|
||||
int m_environmentIrradianceMapEnabledLoc = 0;
|
||||
int m_environmentSpecularMapIdLoc = 0;
|
||||
int m_environmentSpecularMapEnabledLoc = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -211,6 +211,12 @@ void ModelWidget::toggleWireframe()
|
|||
update();
|
||||
}
|
||||
|
||||
void ModelWidget::enableEnvironmentLight()
|
||||
{
|
||||
m_meshBinder.enableEnvironmentLight();
|
||||
update();
|
||||
}
|
||||
|
||||
void ModelWidget::toggleRotation()
|
||||
{
|
||||
if (nullptr != m_rotationTimer) {
|
||||
|
|
|
@ -39,6 +39,7 @@ public:
|
|||
void toggleWireframe();
|
||||
void toggleRotation();
|
||||
void toggleUvCheck();
|
||||
void enableEnvironmentLight();
|
||||
void enableMove(bool enabled);
|
||||
void enableZoom(bool enabled);
|
||||
void enableMousePicking(bool enabled);
|
||||
|
|
Loading…
Reference in New Issue