Merge branch 'master' of https://github.com/highfidelity/hifi into HMDMode

This commit is contained in:
ZappoMan 2014-11-25 12:46:18 -08:00
commit 333f323856
38 changed files with 1558 additions and 302 deletions

View file

@ -96,6 +96,7 @@ function printVector(string, vector) {
function shootBullet(position, velocity) {
var BULLET_SIZE = 0.01;
var BULLET_LIFETIME = 20.0;
var BULLET_GRAVITY = -0.02;
Entities.addEntity(
{ type: "Sphere",
@ -103,6 +104,7 @@ function shootBullet(position, velocity) {
dimensions: { x: BULLET_SIZE, y: BULLET_SIZE, z: BULLET_SIZE },
color: { red: 10, green: 10, blue: 10 },
velocity: velocity,
lifetime: BULLET_LIFETIME,
gravity: { x: 0, y: BULLET_GRAVITY, z: 0 },
damping: 0 });
@ -118,6 +120,7 @@ function shootBullet(position, velocity) {
function shootTarget() {
var TARGET_SIZE = 0.25;
var TARGET_GRAVITY = -0.6;
var TARGET_LIFETIME = 300.0;
var TARGET_UP_VELOCITY = 3.0;
var TARGET_FWD_VELOCITY = 5.0;
var DISTANCE_TO_LAUNCH_FROM = 3.0;
@ -140,7 +143,7 @@ function shootTarget() {
color: { red: 0, green: 200, blue: 200 },
velocity: velocity,
gravity: { x: 0, y: TARGET_GRAVITY, z: 0 },
lifetime: 1000.0,
lifetime: TARGET_LIFETIME,
damping: 0.99 });
// Record start time

View file

@ -205,7 +205,7 @@ SelectionDisplay = (function () {
var MINIMUM_DIMENSION = 0.001;
var GRABBER_DISTANCE_TO_SIZE_RATIO = 0.015;
var GRABBER_DISTANCE_TO_SIZE_RATIO = 0.0075;
var spaceMode = SPACE_LOCAL;
var mode = "UNKNOWN";
@ -260,6 +260,7 @@ SelectionDisplay = (function () {
var grabberColorFace = { red: 120, green: 120, blue: 120 };
var grabberLineWidth = 0.5;
var grabberSolid = true;
var grabberMoveUpPosition = { x: 0, y: 0, z: 0 };
var grabberPropertiesCorner = {
position: { x:0, y: 0, z: 0},
@ -1093,7 +1094,8 @@ SelectionDisplay = (function () {
Overlays.editOverlay(grabberEdgeFL, { visible: stretchHandlesVisible, rotation: rotation, position: EdgeFL });
var grabberMoveUpOffset = 0.1;
Overlays.editOverlay(grabberMoveUp, { visible: activeTool == null || mode == "TRANSLATE_UP_DOWN", position: { x: position.x, y: position.y + worldTop + grabberMoveUpOffset, z: position.z } });
grabberMoveUpPosition = { x: position.x, y: position.y + worldTop + grabberMoveUpOffset, z: position.z }
Overlays.editOverlay(grabberMoveUp, { visible: activeTool == null || mode == "TRANSLATE_UP_DOWN" });
};
that.setOverlaysVisible = function(isVisible) {
@ -2297,8 +2299,10 @@ SelectionDisplay = (function () {
Overlays.editOverlay(rollHandle, {
scale: handleSize,
});
var pos = Vec3.sum(grabberMoveUpPosition, { x: 0, y: Vec3.length(diff) * GRABBER_DISTANCE_TO_SIZE_RATIO * 3, z: 0 });
Overlays.editOverlay(grabberMoveUp, {
scale: handleSize,
position: pos,
scale: handleSize / 2,
});
}
}

377
examples/notifications.js Normal file
View file

@ -0,0 +1,377 @@
//
// notifications.js
// Created by Adrian
//
// Adrian McCarlie 8-10-14
// This script demonstrates on-screen overlay type notifications.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// This script demonstrates notifications created via a number of ways, such as:
// Simple key press alerts, which only depend on a key being pressed,
// dummy examples of this are "q", "w", "e", "r", and "SPACEBAR".
// actual working examples are "a" for left turn, "d" for right turn and Ctrl/s for snapshot.
// System generated alerts such as users joining and leaving and chat messages which mention this user.
// System generated alerts which originate with a user interface event such as Window Resize, and Mic Mute/Unmute.
// Mic Mute/Unmute may appear to be a key press alert, but it actually gets the call from the system as mic is muted and unmuted,
// so the mic mute/unmute will also trigger the notification by clicking the Mic Mute button at the top of the screen.
// To add a new System notification type:
//
// 1. Set the Event Connector at the bottom of the script.
// example:
// GlobalServices.incomingMessage.connect(onIncomingMessage);
//
// 2. Create a new function to produce a text string, do not include new line returns.
// example:
// function onIncomingMessage(user, message) {
// //do stuff here;
// var text = "This is a notification";
// wordWrap(text);
// }
//
// This new function must call wordWrap(text) if the length of message is longer than 42 chars or unknown.
// wordWrap() will format the text to fit the notifications overlay and send it to createNotification(text).
// If the message is 42 chars or less you should bypass wordWrap() and call createNotification() directly.
// To add a keypress driven notification:
//
// 1. Add a key to the keyPressEvent(key).
// 2. Declare a text string.
// 3. Call createNotifications(text) parsing the text.
// example:
// if (key.text == "a") {
// var noteString = "Turning to the Left";
// createNotification(noteString);
// }
var width = 340.0; //width of notification overlay
var height = 40.0; // height of a single line notification overlay
var windowDimensions = Controller.getViewportDimensions(); // get the size of the interface window
var overlayLocationX = (windowDimensions.x - (width + 60.0));// positions window 60px from the right of the interface window
var buttonLocationX = overlayLocationX + (width - 28.0);
var locationY = 20.0; // position down from top of interface window
var topMargin = 13.0;
var leftMargin = 10.0;
var textColor = { red: 228, green: 228, blue: 228}; // text color
var backColor = { red: 38, green: 38, blue: 38}; // background color
var backgroundAlpha = 0;
var fontSize = 12.0;
var persistTime = 10.0; // time in seconds before notification fades
var clickedText = false;
var frame = 0;
var ourWidth = Window.innerWidth;
var ourHeight = Window.innerHeight;
var text = "placeholder";
var last_users = GlobalServices.onlineUsers;
var users = [];
var ctrlIsPressed = false;
var ready = true;
// When our script shuts down, we should clean up all of our overlays
function scriptEnding() {
for (i = 0; i < notifications.length; i++) {
Overlays.deleteOverlay(notifications[i]);
Overlays.deleteOverlay(buttons[i]);
}
}
Script.scriptEnding.connect(scriptEnding);
var notifications = [];
var buttons = [];
var times = [];
var heights = [];
var myAlpha = [];
var arrays = [];
// This function creates and sizes the overlays
function createNotification(text) {
var count = (text.match(/\n/g) || []).length;
var breakPoint = 43.0; // length when new line is added
var extraLine = 0;
var breaks = 0;
var height = 40.0;
var stack = 0;
if (text.length >= breakPoint) {
breaks = count;
}
var extraLine = breaks * 16.0;
for (i = 0; i < heights.length; i++) {
stack = stack + heights[i];
}
var level = (stack + 20.0);
height = height + extraLine;
var overlayProperties = {
x: overlayLocationX,
y: level,
width: width,
height: height,
color: textColor,
backgroundColor: backColor,
alpha: backgroundAlpha,
topMargin: topMargin,
leftMargin: leftMargin,
font: {size: fontSize},
text: text,
};
var bLevel = level + 12.0;
var buttonProperties = {
x: buttonLocationX,
y: bLevel,
width: 15.0,
height: 15.0,
subImage: { x: 0, y: 0, width: 10, height: 10 },
imageURL: "http://hifi-public.s3.amazonaws.com/images/close-small-light.svg",
color: { red: 255, green: 255, blue: 255},
visible: true,
alpha: backgroundAlpha,
};
Notify(overlayProperties, buttonProperties, height);
}
// Pushes data to each array and sets up data for 2nd dimension array
// to handle auxiliary data not carried by the overlay class
// specifically notification "heights", "times" of creation, and .
function Notify(notice, button, height){
notifications.push((Overlays.addOverlay("text", notice)));
buttons.push((Overlays.addOverlay("image",button)));
times.push(new Date().getTime() / 1000);
height = height + 1.0;
heights.push(height);
myAlpha.push(0);
var last = notifications.length - 1;
createArrays(notifications[last], buttons[last], times[last], heights[last], myAlpha[last]);
fadeIn(notifications[last], buttons[last])
}
function fadeIn(noticeIn, buttonIn) {
var myLength = arrays.length;
var q = 0;
var pauseTimer = null;
pauseTimer = Script.setInterval(function() {
q++;
qFade = q / 10.0;
Overlays.editOverlay(noticeIn, {alpha: qFade});
Overlays.editOverlay(buttonIn, {alpha: qFade});
if (q >= 9.0) {
Script.clearInterval(pauseTimer);
}
}, 10);
}
// push data from above to the 2 dimensional array
function createArrays(notice, button, createTime, height, myAlpha) {
arrays.push([notice, button, createTime, height, myAlpha]);
}
// handles mouse clicks on buttons
function mousePressEvent(event) {
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); //identify which overlay was clicked
for (i = 0; i < buttons.length; i++) { //if user clicked a button
if(clickedOverlay == buttons[i]) {
Overlays.deleteOverlay(notifications[i]);
Overlays.deleteOverlay(buttons[i]);
notifications.splice(i, 1);
buttons.splice(i, 1);
times.splice(i, 1);
heights.splice(i, 1);
myAlpha.splice(i, 1);
arrays.splice(i, 1);
}
}
}
// Control key remains active only while key is held down
function keyReleaseEvent(key) {
if (key.key == 16777249) {
ctrlIsPressed = false;
}
}
// Triggers notification on specific key driven events
function keyPressEvent(key) {
if (key.key == 16777249) {
ctrlIsPressed = true;
}
if (key.text == "a") {
var noteString = "Turning to the Left";
createNotification(noteString);
}
if (key.text == "d") {
var noteString = "Turning to the Right";
createNotification(noteString);
}
if (key.text == "s") {
if (ctrlIsPressed == true){
var noteString = "You have taken a snapshot";
createNotification(noteString);
}
}
if (key.text == "q") {
var noteString = "Enable Scripted Motor control is now on.";
wordWrap(noteString);
}
if (key.text == "w") {
var noteString = "This notification spans 2 lines. The overlay will resize to fit new lines.";
var noteString = "editVoxels.js stopped, editModels.js stopped, selectAudioDevice.js stopped.";
wordWrap(noteString);
}
if (key.text == "e") {
var noteString = "This is an example of a multiple line notification. This notification will span 3 lines."
wordWrap(noteString);
}
if (key.text == "r") {
var noteString = "This is a very long line of text that we are going to use in this example to divide it into rows of maximum 43 chars and see how many lines we use.";
wordWrap(noteString);
}
if (key.text == "SPACE") {
var noteString = "You have pressed the Spacebar, This is an example of a multiple line notification. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam.";
wordWrap(noteString);
}
}
// formats string to add newline every 43 chars
function wordWrap(str) {
var result = stringDivider(str, 43.0, "\n");
createNotification(result);
}
// wraps whole word to newline
function stringDivider(str, slotWidth, spaceReplacer) {
if (str.length > slotWidth) {
var p = slotWidth;
for (; p > 0 && str[p] != ' '; p--) {
}
if (p > 0) {
var left = str.substring(0, p);
var right = str.substring(p + 1);
return left + spaceReplacer + stringDivider(right, slotWidth, spaceReplacer);
}
}
return str;
}
// This fires a notification on window resize
function checkSize(){
if((Window.innerWidth != ourWidth)||(Window.innerHeight != ourHeight)) {
var windowResize = "Window has been resized";
ourWidth = Window.innerWidth;
ourHeight = Window.innerHeight;
windowDimensions = Controller.getViewportDimensions();
overlayLocationX = (windowDimensions.x - (width + 60.0));
buttonLocationX = overlayLocationX + (width - 35.0);
createNotification(windowResize)
}
}
// Triggers notification if a user logs on or off
function onOnlineUsersChanged(users) {
var joiners = [];
var leavers = [];
for (user in users) {
if (last_users.indexOf(users[user]) == -1.0) {
joiners.push(users[user]);
createNotification(users[user] + " Has joined");
}
}
for (user in last_users) {
if (users.indexOf(last_users[user]) == -1.0) {
leavers.push(last_users[user]);
createNotification(last_users[user] + " Has left");
}
}
last_users = users;
}
// Triggers notification if @MyUserName is mentioned in chat and returns the message to the notification.
function onIncomingMessage(user, message) {
var myMessage = message;
var alertMe = "@" + GlobalServices.myUsername;
var thisAlert = user + ": " + myMessage;
if (myMessage.indexOf(alertMe) > -1.0) {
wordWrap(thisAlert);
}
}
// Triggers mic mute notification
function onMuteStateChanged() {
var muteState = AudioDevice.getMuted() ? "Muted" : "Unmuted";
var muteString = "Microphone is set to " + muteState;
createNotification(muteString);
}
function update(){
frame++;
if ((frame % 60.0) == 0) { // only update once a second
checkSize(); // checks for size change to trigger windowResize notification
locationY = 20.0;
for (var i = 0; i < arrays.length; i++) { //repositions overlays as others fade
var nextOverlay = Overlays.getOverlayAtPoint({x: overlayLocationX, y: locationY});
Overlays.editOverlay(notifications[i], { x:overlayLocationX, y:locationY});
Overlays.editOverlay(buttons[i], { x:buttonLocationX, y:locationY + 12.0});
locationY = locationY + arrays[i][3];
}
}
// This checks the age of the notification and prepares to fade it after 9.0 seconds (var persistTime - 1)
for (var i = 0; i < arrays.length; i++) {
if (ready){
var j = arrays[i][2];
var k = j + persistTime;
if (k < (new Date().getTime() / 1000)) {
ready = false;
noticeOut = arrays[i][0];
buttonOut = arrays[i][1];
var arraysOut = i;
fadeOut(noticeOut, buttonOut, arraysOut);
}
}
}
}
// this fades the notification ready for dismissal, and removes it from the arrays
function fadeOut(noticeOut, buttonOut, arraysOut) {
var myLength = arrays.length;
var r = 9.0;
var pauseTimer = null;
pauseTimer = Script.setInterval(function() {
r--;
rFade = r / 10.0;
Overlays.editOverlay(noticeOut, {alpha: rFade});
Overlays.editOverlay(buttonOut, {alpha: rFade});
if (r < 0) {
dismiss(noticeOut, buttonOut, arraysOut);
arrays.splice(arraysOut, 1);
ready = true;
Script.clearInterval(pauseTimer);
}
}, 20);
}
// This handles the final dismissal of a notification after fading
function dismiss(firstNoteOut, firstButOut, firstOut) {
var working = firstOut
Overlays.deleteOverlay(firstNoteOut);
Overlays.deleteOverlay(firstButOut);
notifications.splice(firstOut, 1);
buttons.splice(firstOut, 1);
times.splice(firstOut, 1);
heights.splice(firstOut, 1);
myAlpha.splice(firstOut,1);
}
onMuteStateChanged();
AudioDevice.muteToggled.connect(onMuteStateChanged);
Controller.keyPressEvent.connect(keyPressEvent);
Controller.mousePressEvent.connect(mousePressEvent);
GlobalServices.onlineUsersChanged.connect(onOnlineUsersChanged);
GlobalServices.incomingMessage.connect(onIncomingMessage);
Controller.keyReleaseEvent.connect(keyReleaseEvent);
Script.update.connect(update);

View file

@ -11,6 +11,10 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
const int MAX_TEXCOORDS = 2;
uniform mat4 texcoordMatrices[MAX_TEXCOORDS];
// the interpolated normal
varying vec4 normal;
@ -22,7 +26,7 @@ void main(void) {
gl_FrontColor = gl_Color * gl_FrontMaterial.diffuse;
// and the texture coordinates
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_TexCoord[0] = texcoordMatrices[0] * vec4(gl_MultiTexCoord0.xy, 0.0, 1.0);
// use standard pipeline transform
gl_Position = ftransform();

View file

@ -0,0 +1,37 @@
#version 120
//
// model_lightmap.frag
// fragment shader
//
// Created by Samuel Gateau on 11/19/14.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// the diffuse texture
uniform sampler2D diffuseMap;
// the emissive map texture and parameters
uniform sampler2D emissiveMap;
uniform vec2 emissiveParams;
// the alpha threshold
uniform float alphaThreshold;
// the interpolated normal
varying vec4 normal;
// the interpolated texcoord1
varying vec2 interpolatedTexcoord1;
void main(void) {
// set the diffuse, normal, specular data
vec4 diffuse = texture2D(diffuseMap, gl_TexCoord[0].st);
vec4 emissive = texture2D(emissiveMap, interpolatedTexcoord1.st);
gl_FragData[0] = vec4(gl_Color.rgb * diffuse.rgb * (vec3(emissiveParams.x) + emissiveParams.y * emissive.rgb), mix(gl_Color.a, 1.0 - gl_Color.a, step(diffuse.a, alphaThreshold)));
gl_FragData[1] = normalize(normal) * 0.5 + vec4(0.5, 0.5, 0.5, 1.0);
gl_FragData[2] = vec4(gl_FrontMaterial.specular.rgb, gl_FrontMaterial.shininess / 128.0);
}

View file

@ -0,0 +1,40 @@
#version 120
//
// model_lightmap.vert
// vertex shader
//
// Created by Sam Gateau on 11/21/14.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
const int MAX_TEXCOORDS = 2;
uniform mat4 texcoordMatrices[MAX_TEXCOORDS];
attribute vec2 texcoord1;
// the interpolated normal
varying vec4 normal;
// the interpolated texcoord1
varying vec2 interpolatedTexcoord1;
void main(void) {
// transform and store the normal for interpolation
normal = normalize(gl_ModelViewMatrix * vec4(gl_Normal, 0.0));
// pass along the diffuse color
gl_FrontColor = gl_Color * gl_FrontMaterial.diffuse;
// and the texture coordinates
gl_TexCoord[0] = texcoordMatrices[0] * vec4(gl_MultiTexCoord0.xy, 0.0, 1.0);
interpolatedTexcoord1 = vec2(texcoordMatrices[1] * vec4(texcoord1.xy, 0.0, 1.0)).xy;
// use standard pipeline transform
gl_Position = ftransform();
}

View file

@ -0,0 +1,50 @@
#version 120
//
// model_lightmap_normal_map.frag
// fragment shader
//
// Created by Samuel Gateau on 11/19/14.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// the diffuse texture
uniform sampler2D diffuseMap;
// the normal map texture
uniform sampler2D normalMap;
// the emissive map texture and parameters
uniform sampler2D emissiveMap;
uniform vec2 emissiveParams;
// the alpha threshold
uniform float alphaThreshold;
// the interpolated normal
varying vec4 interpolatedNormal;
// the interpolated tangent
varying vec4 interpolatedTangent;
varying vec2 interpolatedTexcoord1;
void main(void) {
// compute the view normal from the various bits
vec3 normalizedNormal = normalize(vec3(interpolatedNormal));
vec3 normalizedTangent = normalize(vec3(interpolatedTangent));
vec3 normalizedBitangent = normalize(cross(normalizedNormal, normalizedTangent));
vec3 localNormal = vec3(texture2D(normalMap, gl_TexCoord[0].st)) - vec3(0.5, 0.5, 0.5);
vec4 viewNormal = vec4(normalizedTangent * localNormal.x +
normalizedBitangent * localNormal.y + normalizedNormal * localNormal.z, 0.0);
// set the diffuse, normal, specular data
vec4 diffuse = texture2D(diffuseMap, gl_TexCoord[0].st);
vec4 emissive = texture2D(emissiveMap, interpolatedTexcoord1.st);
gl_FragData[0] = vec4(gl_Color.rgb * diffuse.rgb * (vec3(emissiveParams.x) + emissiveParams.y * emissive.rgb), mix(gl_Color.a, 1.0 - gl_Color.a, step(diffuse.a, alphaThreshold)));
gl_FragData[1] = viewNormal + vec4(0.5, 0.5, 0.5, 1.0);
gl_FragData[2] = vec4(gl_FrontMaterial.specular.rgb, gl_FrontMaterial.shininess / 128.0);
}

View file

@ -0,0 +1,46 @@
#version 120
//
// model_lightmap_normal_map.vert
// vertex shader
//
// Created by Sam Gateau on 11/21/14.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
const int MAX_TEXCOORDS = 2;
uniform mat4 texcoordMatrices[MAX_TEXCOORDS];
// the tangent vector
attribute vec3 tangent;
attribute vec2 texcoord1;
// the interpolated normal
varying vec4 interpolatedNormal;
// the interpolated tangent
varying vec4 interpolatedTangent;
// the interpolated texcoord1
varying vec2 interpolatedTexcoord1;
void main(void) {
// transform and store the normal and tangent for interpolation
interpolatedNormal = gl_ModelViewMatrix * vec4(gl_Normal, 0.0);
interpolatedTangent = gl_ModelViewMatrix * vec4(tangent, 0.0);
// pass along the diffuse color
gl_FrontColor = gl_Color * gl_FrontMaterial.diffuse;
// and the texture coordinates
gl_TexCoord[0] = texcoordMatrices[0] * vec4(gl_MultiTexCoord0.xy, 0.0, 1.0);
interpolatedTexcoord1 = vec2(texcoordMatrices[1] * vec4(texcoord1.xy, 0.0, 1.0)).xy;
// use standard pipeline transform
gl_Position = ftransform();
}

View file

@ -0,0 +1,54 @@
#version 120
//
// model_lightmap_normal_specular_map.frag
// fragment shader
//
// Created by Samuel Gateau on 11/19/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// the diffuse texture
uniform sampler2D diffuseMap;
// the emissive map texture and parameters
uniform sampler2D emissiveMap;
uniform vec2 emissiveParams;
// the normal map texture
uniform sampler2D normalMap;
// the specular map texture
uniform sampler2D specularMap;
// the alpha threshold
uniform float alphaThreshold;
// the interpolated normal
varying vec4 interpolatedNormal;
// the interpolated tangent
varying vec4 interpolatedTangent;
varying vec2 interpolatedTexcoord1;
void main(void) {
// compute the view normal from the various bits
vec3 normalizedNormal = normalize(vec3(interpolatedNormal));
vec3 normalizedTangent = normalize(vec3(interpolatedTangent));
vec3 normalizedBitangent = normalize(cross(normalizedNormal, normalizedTangent));
vec3 localNormal = vec3(texture2D(normalMap, gl_TexCoord[0].st)) - vec3(0.5, 0.5, 0.5);
vec4 viewNormal = vec4(normalizedTangent * localNormal.x +
normalizedBitangent * localNormal.y + normalizedNormal * localNormal.z, 0.0);
// set the diffuse, normal, specular data
vec4 diffuse = texture2D(diffuseMap, gl_TexCoord[0].st);
vec4 emissive = texture2D(emissiveMap, interpolatedTexcoord1.st);
gl_FragData[0] = vec4(gl_Color.rgb * diffuse.rgb * (vec3(emissiveParams.x) + emissiveParams.y * emissive.rgb), mix(gl_Color.a, 1.0 - gl_Color.a, step(diffuse.a, alphaThreshold)));
gl_FragData[1] = viewNormal + vec4(0.5, 0.5, 0.5, 1.0);
gl_FragData[2] = vec4(gl_FrontMaterial.specular.rgb * texture2D(specularMap, gl_TexCoord[0].st).rgb,
gl_FrontMaterial.shininess / 128.0);
}

View file

@ -0,0 +1,40 @@
#version 120
//
// model_lightmap_specular_map.frag
// fragment shader
//
// Created by Samuel Gateau on 11/19/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// the diffuse texture
uniform sampler2D diffuseMap;
// the emissive map texture and parameters
uniform sampler2D emissiveMap;
uniform vec2 emissiveParams;
// the specular texture
uniform sampler2D specularMap;
// the alpha threshold
uniform float alphaThreshold;
// the interpolated normal
varying vec4 normal;
varying vec2 interpolatedTexcoord1;
void main(void) {
// set the diffuse, normal, specular data
vec4 diffuse = texture2D(diffuseMap, gl_TexCoord[0].st);
vec4 emissive = texture2D(emissiveMap, interpolatedTexcoord1.st);
gl_FragData[0] = vec4(gl_Color.rgb * diffuse.rgb * (vec3(emissiveParams.x) + emissiveParams.y * emissive.rgb), mix(gl_Color.a, 1.0 - gl_Color.a, step(diffuse.a, alphaThreshold)));
gl_FragData[1] = normalize(normal) * 0.5 + vec4(0.5, 0.5, 0.5, 1.0);
gl_FragData[2] = vec4(gl_FrontMaterial.specular.rgb * texture2D(specularMap, gl_TexCoord[0].st).rgb,
gl_FrontMaterial.shininess / 128.0);
}

View file

@ -4,7 +4,7 @@
// model_normal_map.frag
// fragment shader
//
// Created by Andrzej Kapolka on 10/29/13.
// Created by Andrzej Kapolka on 10/14/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.

View file

@ -11,6 +11,10 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
const int MAX_TEXCOORDS = 2;
uniform mat4 texcoordMatrices[MAX_TEXCOORDS];
// the tangent vector
attribute vec3 tangent;
@ -29,7 +33,7 @@ void main(void) {
gl_FrontColor = gl_Color * gl_FrontMaterial.diffuse;
// and the texture coordinates
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_TexCoord[0] = texcoordMatrices[0] * vec4(gl_MultiTexCoord0.xy, 0.0, 1.0);
// use standard pipeline transform
gl_Position = ftransform();

View file

@ -11,10 +11,12 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
const int MAX_TEXCOORDS = 2;
const int MAX_CLUSTERS = 128;
const int INDICES_PER_VERTEX = 4;
uniform mat4 clusterMatrices[MAX_CLUSTERS];
uniform mat4 texcoordMatrices[MAX_TEXCOORDS];
attribute vec4 clusterIndices;
attribute vec4 clusterWeights;
@ -38,7 +40,7 @@ void main(void) {
gl_FrontColor = gl_FrontMaterial.diffuse;
// and the texture coordinates
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_TexCoord[0] = texcoordMatrices[0] * vec4(gl_MultiTexCoord0.xy, 0.0, 1.0);
gl_Position = gl_ModelViewProjectionMatrix * position;
}

View file

@ -11,10 +11,12 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
const int MAX_TEXCOORDS = 2;
const int MAX_CLUSTERS = 128;
const int INDICES_PER_VERTEX = 4;
uniform mat4 clusterMatrices[MAX_CLUSTERS];
uniform mat4 texcoordMatrices[MAX_TEXCOORDS];
// the tangent vector
attribute vec3 tangent;
@ -46,7 +48,7 @@ void main(void) {
gl_FrontColor = gl_FrontMaterial.diffuse;
// and the texture coordinates
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_TexCoord[0] = texcoordMatrices[0] * vec4(gl_MultiTexCoord0.xy, 0.0, 1.0);
gl_Position = gl_ModelViewProjectionMatrix * interpolatedPosition;
}

View file

@ -2845,7 +2845,12 @@ void Application::updateShadowMap() {
// render JS/scriptable overlays
{
PerformanceTimer perfTimer("3dOverlays");
_overlays.render3D(RenderArgs::SHADOW_RENDER_MODE);
_overlays.render3D(false, RenderArgs::SHADOW_RENDER_MODE);
}
{
PerformanceTimer perfTimer("3dOverlaysFront");
_overlays.render3D(true, RenderArgs::SHADOW_RENDER_MODE);
}
glDisable(GL_POLYGON_OFFSET_FILL);
@ -3060,7 +3065,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly, RenderAr
// render JS/scriptable overlays
{
PerformanceTimer perfTimer("3dOverlays");
_overlays.render3D();
_overlays.render3D(false);
}
// render the ambient occlusion effect if enabled
@ -3144,6 +3149,13 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly, RenderAr
if (Menu::getInstance()->isOptionChecked(MenuOption::Wireframe)) {
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
// Render 3D overlays that should be drawn in front
{
PerformanceTimer perfTimer("3dOverlaysFront");
glClear(GL_DEPTH_BUFFER_BIT);
_overlays.render3D(true);
}
}
void Application::updateUntranslatedViewMatrix(const glm::vec3& viewMatrixTranslation) {

View file

@ -989,7 +989,8 @@ void Audio::processReceivedSamples(const QByteArray& inputBuffer, QByteArray& ou
QByteArray buffer = inputBuffer;
// Accumulate direct transmission of audio from sender to receiver
if (Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingIncludeOriginal)) {
bool includeOriginal = true; // Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingIncludeOriginal)
if (includeOriginal) {
emit preProcessOriginalInboundAudio(sampleTime, buffer, _desiredOutputFormat);
addSpatialAudioToBuffer(sampleTime, buffer, numNetworkOutputSamples);
}
@ -1264,7 +1265,8 @@ void Audio::selectAudioSourceSine440() {
}
void Audio::toggleAudioSpatialProcessing() {
_processSpatialAudio = !_processSpatialAudio;
// spatial audio disabled for now
_processSpatialAudio = false; //!_processSpatialAudio;
if (_processSpatialAudio) {
_spatialAudioStart = 0;
_spatialAudioFinish = 0;

View file

@ -66,10 +66,15 @@ AudioReflector::AudioReflector(QObject* parent) :
}
bool AudioReflector::haveAttributesChanged() {
bool withDiffusion = Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingWithDiffusions);
bool dontDistanceAttenuate = Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingDontDistanceAttenuate);
bool alternateDistanceAttenuate = Menu::getInstance()->isOptionChecked(
MenuOption::AudioSpatialProcessingAlternateDistanceAttenuate);
// Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingWithDiffusions);
bool withDiffusion = true;
// Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingDontDistanceAttenuate);
bool dontDistanceAttenuate = false;
//Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingAlternateDistanceAttenuate);
bool alternateDistanceAttenuate = false;
bool attributesChange = (_withDiffusion != withDiffusion
|| _lastPreDelay != _preDelay
@ -107,7 +112,8 @@ void AudioReflector::render() {
calculateAllReflections();
// only render if we've been asked to do so
if (Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingRenderPaths)) {
bool renderPaths = false; // Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingRenderPaths)
if (renderPaths) {
drawRays();
}
}
@ -116,7 +122,8 @@ void AudioReflector::render() {
// = 3ms per meter
float AudioReflector::getDelayFromDistance(float distance) {
float delay = (_soundMsPerMeter * distance);
if (Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingPreDelay)) {
bool includePreDelay = true; // Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingPreDelay)
if (includePreDelay) {
delay += _preDelay;
}
return delay;
@ -126,12 +133,11 @@ float AudioReflector::getDelayFromDistance(float distance) {
float AudioReflector::getDistanceAttenuationCoefficient(float distance) {
bool doDistanceAttenuation = !Menu::getInstance()->isOptionChecked(
MenuOption::AudioSpatialProcessingDontDistanceAttenuate);
//!Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingDontDistanceAttenuate);
bool doDistanceAttenuation = true;
bool originalFormula = !Menu::getInstance()->isOptionChecked(
MenuOption::AudioSpatialProcessingAlternateDistanceAttenuate);
//!Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingAlternateDistanceAttenuate);
bool originalFormula = true;
float distanceCoefficient = 1.0f;
@ -170,7 +176,8 @@ float AudioReflector::getDistanceAttenuationCoefficient(float distance) {
}
glm::vec3 AudioReflector::getFaceNormal(BoxFace face) {
bool wantSlightRandomness = Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingSlightlyRandomSurfaces);
// Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingSlightlyRandomSurfaces);
bool wantSlightRandomness = true;
glm::vec3 faceNormal;
const float MIN_RANDOM_LENGTH = 0.99f;
const float MAX_RANDOM_LENGTH = 1.0f;
@ -202,8 +209,8 @@ const int NUMBER_OF_CHANNELS = 2;
void AudioReflector::injectAudiblePoint(AudioSource source, const AudiblePoint& audiblePoint,
const QByteArray& samples, unsigned int sampleTime, int sampleRate) {
bool wantEarSeparation = Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingSeparateEars);
bool wantStereo = Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingStereoSource);
bool wantEarSeparation = true; // Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingSeparateEars);
bool wantStereo = true; // Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingStereoSource);
glm::vec3 rightEarPosition = wantEarSeparation ? _myAvatar->getHead()->getRightEarPosition() :
_myAvatar->getHead()->getPosition();
glm::vec3 leftEarPosition = wantEarSeparation ? _myAvatar->getHead()->getLeftEarPosition() :
@ -316,7 +323,8 @@ void AudioReflector::preProcessOriginalInboundAudio(unsigned int sampleTime,
}
void AudioReflector::processLocalAudio(unsigned int sampleTime, const QByteArray& samples, const QAudioFormat& format) {
if (Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingProcessLocalAudio)) {
bool processLocalAudio = true; // Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingProcessLocalAudio)
if (processLocalAudio) {
const int NUM_CHANNELS_INPUT = 1;
const int NUM_CHANNELS_OUTPUT = 2;
const int EXPECTED_SAMPLE_RATE = 24000;
@ -458,7 +466,7 @@ void AudioReflector::identifyAudioSources() {
void AudioReflector::calculateAllReflections() {
// only recalculate when we've moved, or if the attributes have changed
// TODO: what about case where new voxels are added in front of us???
bool wantHeadOrientation = Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingHeadOriented);
bool wantHeadOrientation = true; // Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingHeadOriented);
glm::quat orientation = wantHeadOrientation ? _myAvatar->getHead()->getFinalOrientationInWorldFrame() : _myAvatar->getOrientation();
glm::vec3 origin = _myAvatar->getHead()->getPosition();
glm::vec3 listenerPosition = _myAvatar->getHead()->getPosition();
@ -505,7 +513,8 @@ void AudioReflector::drawRays() {
}
}
if (Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingProcessLocalAudio)) {
bool processLocalAudio = true; // Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingProcessLocalAudio)
if (processLocalAudio) {
// draw the paths for local audio
foreach(AudioPath* const& path, _localAudioPaths) {
// if this is an original reflection, draw it in RED
@ -575,7 +584,8 @@ void AudioReflector::analyzePaths() {
float initialAttenuation = 1.0f;
float preDelay = Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingPreDelay) ? _preDelay : 0.0f;
bool wantPreDelay = true; // Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingPreDelay)
float preDelay = wantPreDelay ? _preDelay : 0.0f;
// NOTE: we're still calculating our initial paths based on the listeners position. But the analysis code has been
// updated to support individual sound sources (which is how we support diffusion), we can use this new paradigm to
@ -701,7 +711,7 @@ void AudioReflector::handlePathPoint(AudioPath* path, float distance, OctreeElem
float reflectiveAttenuation = currentReflectiveAttenuation * material.reflectiveRatio;
float totalDiffusionAttenuation = currentReflectiveAttenuation * material.diffusionRatio;
bool wantDiffusions = Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingWithDiffusions);
bool wantDiffusions = true; // Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingWithDiffusions);
int fanout = wantDiffusions ? _diffusionFanout : 0;
float partialDiffusionAttenuation = fanout < 1 ? 0.0f : totalDiffusionAttenuation / (float)fanout;

View file

@ -622,58 +622,6 @@ Menu::Menu() :
audioScopeFramesGroup->addAction(fiftyFrames);
}
QMenu* spatialAudioMenu = audioDebugMenu->addMenu("Spatial Audio");
addCheckableActionToQMenuAndActionHash(spatialAudioMenu, MenuOption::AudioSpatialProcessing,
Qt::CTRL | Qt::SHIFT | Qt::Key_M,
false,
appInstance->getAudio(),
SLOT(toggleAudioSpatialProcessing()));
addCheckableActionToQMenuAndActionHash(spatialAudioMenu, MenuOption::AudioSpatialProcessingIncludeOriginal,
Qt::CTRL | Qt::SHIFT | Qt::Key_O,
true);
addCheckableActionToQMenuAndActionHash(spatialAudioMenu, MenuOption::AudioSpatialProcessingSeparateEars,
Qt::CTRL | Qt::SHIFT | Qt::Key_E,
true);
addCheckableActionToQMenuAndActionHash(spatialAudioMenu, MenuOption::AudioSpatialProcessingPreDelay,
Qt::CTRL | Qt::SHIFT | Qt::Key_D,
true);
addCheckableActionToQMenuAndActionHash(spatialAudioMenu, MenuOption::AudioSpatialProcessingStereoSource,
Qt::CTRL | Qt::SHIFT | Qt::Key_S,
true);
addCheckableActionToQMenuAndActionHash(spatialAudioMenu, MenuOption::AudioSpatialProcessingHeadOriented,
Qt::CTRL | Qt::SHIFT | Qt::Key_H,
true);
addCheckableActionToQMenuAndActionHash(spatialAudioMenu, MenuOption::AudioSpatialProcessingWithDiffusions,
Qt::CTRL | Qt::SHIFT | Qt::Key_W,
true);
addCheckableActionToQMenuAndActionHash(spatialAudioMenu, MenuOption::AudioSpatialProcessingRenderPaths,
Qt::CTRL | Qt::SHIFT | Qt::Key_R,
true);
addCheckableActionToQMenuAndActionHash(spatialAudioMenu, MenuOption::AudioSpatialProcessingSlightlyRandomSurfaces,
Qt::CTRL | Qt::SHIFT | Qt::Key_X,
true);
addCheckableActionToQMenuAndActionHash(spatialAudioMenu, MenuOption::AudioSpatialProcessingProcessLocalAudio,
Qt::CTRL | Qt::SHIFT | Qt::Key_A,
true);
addCheckableActionToQMenuAndActionHash(spatialAudioMenu, MenuOption::AudioSpatialProcessingDontDistanceAttenuate,
Qt::CTRL | Qt::SHIFT | Qt::Key_Y,
false);
addCheckableActionToQMenuAndActionHash(spatialAudioMenu, MenuOption::AudioSpatialProcessingAlternateDistanceAttenuate,
Qt::CTRL | Qt::SHIFT | Qt::Key_U,
false);
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioStats,
Qt::CTRL | Qt::Key_A,
false,

View file

@ -346,18 +346,6 @@ namespace MenuOption {
const QString AudioScopeTwentyFrames = "Twenty";
const QString AudioStats = "Audio Stats";
const QString AudioStatsShowInjectedStreams = "Audio Stats Show Injected Streams";
const QString AudioSpatialProcessingAlternateDistanceAttenuate = "Alternate distance attenuation";
const QString AudioSpatialProcessing = "Audio Spatial Processing";
const QString AudioSpatialProcessingDontDistanceAttenuate = "Don't calculate distance attenuation";
const QString AudioSpatialProcessingHeadOriented = "Head Oriented";
const QString AudioSpatialProcessingIncludeOriginal = "Includes Network Original";
const QString AudioSpatialProcessingPreDelay = "Add Pre-Delay";
const QString AudioSpatialProcessingProcessLocalAudio = "Process Local Audio";
const QString AudioSpatialProcessingRenderPaths = "Render Paths";
const QString AudioSpatialProcessingSeparateEars = "Separate Ears";
const QString AudioSpatialProcessingSlightlyRandomSurfaces = "Slightly Random Surfaces";
const QString AudioSpatialProcessingStereoSource = "Stereo Source";
const QString AudioSpatialProcessingWithDiffusions = "With Diffusions";
const QString AudioSourceInject = "Generated Audio";
const QString AudioSourcePinkNoise = "Pink Noise";
const QString AudioSourceSine440 = "Sine 440hz";

View file

@ -522,6 +522,14 @@ bool ModelUploader::addTextures(const QString& texdir, const FBXGeometry& geomet
}
_textureFilenames.insert(part.specularTexture.filename);
}
if (!part.emissiveTexture.filename.isEmpty() && part.emissiveTexture.content.isEmpty() &&
!_textureFilenames.contains(part.emissiveTexture.filename)) {
if (!addPart(texdir + "/" + part.emissiveTexture.filename,
QString("texture%1").arg(++_texturesCount), true)) {
return false;
}
_textureFilenames.insert(part.emissiveTexture.filename);
}
}
}

View file

@ -116,6 +116,7 @@ public:
void _glUseProgram(GLuint program);
void _glUniform1f(GLint location, GLfloat v0);
void _glUniform2f(GLint location, GLfloat v0, GLfloat v1);
void _glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
void _glMatrixMode(GLenum mode);
@ -185,6 +186,7 @@ public:
COMMAND_glUseProgram,
COMMAND_glUniform1f,
COMMAND_glUniform2f,
COMMAND_glUniformMatrix4fv,
COMMAND_glMatrixMode,

View file

@ -53,6 +53,7 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] =
(&::gpu::GLBackend::do_glUseProgram),
(&::gpu::GLBackend::do_glUniform1f),
(&::gpu::GLBackend::do_glUniform2f),
(&::gpu::GLBackend::do_glUniformMatrix4fv),
(&::gpu::GLBackend::do_glMatrixMode),
@ -691,6 +692,23 @@ void GLBackend::do_glUniform1f(Batch& batch, uint32 paramOffset) {
CHECK_GL_ERROR();
}
void Batch::_glUniform2f(GLint location, GLfloat v0, GLfloat v1) {
ADD_COMMAND_GL(glUniform2f);
_params.push_back(v1);
_params.push_back(v0);
_params.push_back(location);
DO_IT_NOW(_glUniform2f, 1);
}
void GLBackend::do_glUniform2f(Batch& batch, uint32 paramOffset) {
glUniform2f(
batch._params[paramOffset + 2]._int,
batch._params[paramOffset + 1]._float,
batch._params[paramOffset + 0]._float);
CHECK_GL_ERROR();
}
void Batch::_glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) {
ADD_COMMAND_GL(glUniformMatrix4fv);

View file

@ -147,6 +147,7 @@ protected:
void do_glUseProgram(Batch& batch, uint32 paramOffset);
void do_glUniform1f(Batch& batch, uint32 paramOffset);
void do_glUniform2f(Batch& batch, uint32 paramOffset);
void do_glUniformMatrix4fv(Batch& batch, uint32 paramOffset);
void do_glMatrixMode(Batch& batch, uint32 paramOffset);

View file

@ -34,6 +34,7 @@ public:
TANGENT,
SKIN_CLUSTER_INDEX,
SKIN_CLUSTER_WEIGHT,
TEXCOORD1,
NUM_INPUT_SLOTS,
};

View file

@ -575,7 +575,8 @@ bool NetworkGeometry::isLoadedWithTextures() const {
foreach (const NetworkMeshPart& part, mesh.parts) {
if ((part.diffuseTexture && !part.diffuseTexture->isLoaded()) ||
(part.normalTexture && !part.normalTexture->isLoaded()) ||
(part.specularTexture && !part.specularTexture->isLoaded())) {
(part.specularTexture && !part.specularTexture->isLoaded()) ||
(part.emissiveTexture && !part.emissiveTexture->isLoaded())) {
return false;
}
}
@ -668,6 +669,9 @@ void NetworkGeometry::setLoadPriority(const QPointer<QObject>& owner, float prio
if (part.specularTexture) {
part.specularTexture->setLoadPriority(owner, priority);
}
if (part.emissiveTexture) {
part.emissiveTexture->setLoadPriority(owner, priority);
}
}
}
}
@ -688,6 +692,9 @@ void NetworkGeometry::setLoadPriorities(const QHash<QPointer<QObject>, float>& p
if (part.specularTexture) {
part.specularTexture->setLoadPriorities(priorities);
}
if (part.emissiveTexture) {
part.emissiveTexture->setLoadPriorities(priorities);
}
}
}
}
@ -708,6 +715,9 @@ void NetworkGeometry::clearLoadPriority(const QPointer<QObject>& owner) {
if (part.specularTexture) {
part.specularTexture->clearLoadPriority(owner);
}
if (part.emissiveTexture) {
part.emissiveTexture->clearLoadPriority(owner);
}
}
}
}
@ -733,6 +743,10 @@ void NetworkGeometry::setTextureWithNameToURL(const QString& name, const QUrl& u
part.specularTexture = Application::getInstance()->getTextureCache()->getTexture(url, DEFAULT_TEXTURE,
false, QByteArray());
part.specularTexture->setLoadPriorities(_loadPriorities);
} else if (part.emissiveTextureName == name) {
part.emissiveTexture = Application::getInstance()->getTextureCache()->getTexture(url, DEFAULT_TEXTURE,
false, QByteArray());
part.emissiveTexture->setLoadPriorities(_loadPriorities);
}
}
}
@ -764,6 +778,11 @@ QStringList NetworkGeometry::getTextureNames() const {
QString textureURL = part.specularTexture->getURL().toString();
result << part.specularTextureName + ":" + textureURL;
}
if (!part.emissiveTextureName.isEmpty()) {
QString textureURL = part.emissiveTexture->getURL().toString();
result << part.emissiveTextureName + ":" + textureURL;
}
}
}
return result;
@ -811,6 +830,8 @@ void GeometryReader::run() {
}
try {
QMetaObject::invokeMethod(geometry.data(), "setGeometry", Q_ARG(const FBXGeometry&,
_url.path().toLower().endsWith(".svo") ? readSVO(_reply->readAll()) : readFBX(_reply->readAll(), _mapping)));
} catch (const QString& error) {
@ -911,6 +932,13 @@ void NetworkGeometry::setGeometry(const FBXGeometry& geometry) {
networkPart.specularTextureName = part.specularTexture.name;
networkPart.specularTexture->setLoadPriorities(_loadPriorities);
}
if (!part.emissiveTexture.filename.isEmpty()) {
networkPart.emissiveTexture = Application::getInstance()->getTextureCache()->getTexture(
_textureBase.resolved(QUrl(part.emissiveTexture.filename)), EMISSIVE_TEXTURE,
false, part.emissiveTexture.content);
networkPart.emissiveTextureName = part.emissiveTexture.name;
networkPart.emissiveTexture->setLoadPriorities(_loadPriorities);
}
networkMesh.parts.append(networkPart);
totalIndices += (part.quadIndices.size() + part.triangleIndices.size());
@ -938,11 +966,11 @@ void NetworkGeometry::setGeometry(const FBXGeometry& geometry) {
int tangentsOffset = normalsOffset + mesh.normals.size() * sizeof(glm::vec3);
int colorsOffset = tangentsOffset + mesh.tangents.size() * sizeof(glm::vec3);
int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3);
int clusterIndicesOffset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2);
int texCoords1Offset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2);
int clusterIndicesOffset = texCoords1Offset + mesh.texCoords1.size() * sizeof(glm::vec2);
int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4);
networkMesh._vertexBuffer->resize(clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4));
//networkMesh.vertexBuffer.allocate(clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4));
networkMesh._vertexBuffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (gpu::Resource::Byte*) mesh.vertices.constData());
networkMesh._vertexBuffer->setSubData(normalsOffset, mesh.normals.size() * sizeof(glm::vec3), (gpu::Resource::Byte*) mesh.normals.constData());
@ -951,6 +979,8 @@ void NetworkGeometry::setGeometry(const FBXGeometry& geometry) {
networkMesh._vertexBuffer->setSubData(colorsOffset, mesh.colors.size() * sizeof(glm::vec3), (gpu::Resource::Byte*) mesh.colors.constData());
networkMesh._vertexBuffer->setSubData(texCoordsOffset,
mesh.texCoords.size() * sizeof(glm::vec2), (gpu::Resource::Byte*) mesh.texCoords.constData());
networkMesh._vertexBuffer->setSubData(texCoords1Offset,
mesh.texCoords1.size() * sizeof(glm::vec2), (gpu::Resource::Byte*) mesh.texCoords1.constData());
networkMesh._vertexBuffer->setSubData(clusterIndicesOffset,
mesh.clusterIndices.size() * sizeof(glm::vec4), (gpu::Resource::Byte*) mesh.clusterIndices.constData());
networkMesh._vertexBuffer->setSubData(clusterWeightsOffset,
@ -963,6 +993,7 @@ void NetworkGeometry::setGeometry(const FBXGeometry& geometry) {
if (mesh.tangents.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, tangentsOffset, sizeof(glm::vec3));
if (mesh.colors.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, colorsOffset, sizeof(glm::vec3));
if (mesh.texCoords.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, texCoordsOffset, sizeof(glm::vec2));
if (mesh.texCoords1.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, texCoords1Offset, sizeof(glm::vec2));
if (mesh.clusterIndices.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, clusterIndicesOffset, sizeof(glm::vec4));
if (mesh.clusterWeights.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, clusterWeightsOffset, sizeof(glm::vec4));
@ -973,6 +1004,7 @@ void NetworkGeometry::setGeometry(const FBXGeometry& geometry) {
if (mesh.tangents.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::TANGENT, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ));
if (mesh.colors.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::COLOR, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RGB));
if (mesh.texCoords.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::TEXCOORD, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV));
if (mesh.texCoords1.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::TEXCOORD1, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV));
if (mesh.clusterIndices.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_INDEX, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW));
if (mesh.clusterWeights.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW));
}

View file

@ -155,7 +155,9 @@ public:
QSharedPointer<NetworkTexture> normalTexture;
QString specularTextureName;
QSharedPointer<NetworkTexture> specularTexture;
QString emissiveTextureName;
QSharedPointer<NetworkTexture> emissiveTexture;
bool isTranslucent() const;
};

View file

@ -70,6 +70,11 @@ ProgramObject Model::_specularMapProgram;
ProgramObject Model::_normalSpecularMapProgram;
ProgramObject Model::_translucentProgram;
ProgramObject Model::_lightmapProgram;
ProgramObject Model::_lightmapNormalMapProgram;
ProgramObject Model::_lightmapSpecularMapProgram;
ProgramObject Model::_lightmapNormalSpecularMapProgram;
ProgramObject Model::_shadowProgram;
ProgramObject Model::_skinProgram;
@ -86,6 +91,11 @@ Model::Locations Model::_specularMapLocations;
Model::Locations Model::_normalSpecularMapLocations;
Model::Locations Model::_translucentLocations;
Model::Locations Model::_lightmapLocations;
Model::Locations Model::_lightmapNormalMapLocations;
Model::Locations Model::_lightmapSpecularMapLocations;
Model::Locations Model::_lightmapNormalSpecularMapLocations;
Model::SkinLocations Model::_skinLocations;
Model::SkinLocations Model::_skinNormalMapLocations;
Model::SkinLocations Model::_skinSpecularMapLocations;
@ -140,17 +150,42 @@ void Model::initProgram(ProgramObject& program, Model::Locations& locations, int
glBindAttribLocation(program.programId(), gpu::Stream::TANGENT, "tangent");
glBindAttribLocation(program.programId(), gpu::Stream::TEXCOORD1, "texcoord1");
glLinkProgram(program.programId());
locations.tangent = program.attributeLocation("tangent");
locations.alphaThreshold = program.uniformLocation("alphaThreshold");
locations.texcoordMatrices = program.uniformLocation("texcoordMatrices");
locations.emissiveParams = program.uniformLocation("emissiveParams");
program.setUniformValue("diffuseMap", 0);
program.setUniformValue("normalMap", 1);
program.setUniformValue("specularMap", specularTextureUnit);
int loc = program.uniformLocation("specularMap");
if (loc >= 0) {
program.setUniformValue("specularMap", 2);
locations.specularTextureUnit = 2;
} else {
locations.specularTextureUnit = -1;
}
loc = program.uniformLocation("emissiveMap");
if (loc >= 0) {
program.setUniformValue("emissiveMap", 3);
locations.emissiveTextureUnit = 3;
} else {
locations.emissiveTextureUnit = -1;
}
if (!program.isLinked()) {
program.release();
}
program.release();
@ -268,6 +303,39 @@ void Model::init() {
_translucentProgram.link();
initProgram(_translucentProgram, _translucentLocations);
// Lightmap
_lightmapProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/model_lightmap.vert");
_lightmapProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + "shaders/model_lightmap.frag");
_lightmapProgram.link();
initProgram(_lightmapProgram, _lightmapLocations);
_lightmapNormalMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
Application::resourcesPath() + "shaders/model_lightmap_normal_map.vert");
_lightmapNormalMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
Application::resourcesPath() + "shaders/model_lightmap_normal_map.frag");
_lightmapNormalMapProgram.link();
initProgram(_lightmapNormalMapProgram, _lightmapNormalMapLocations);
_lightmapSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
Application::resourcesPath() + "shaders/model_lightmap.vert");
_lightmapSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
Application::resourcesPath() + "shaders/model_lightmap_specular_map.frag");
_lightmapSpecularMapProgram.link();
initProgram(_lightmapSpecularMapProgram, _lightmapSpecularMapLocations);
_lightmapNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
Application::resourcesPath() + "shaders/model_lightmap_normal_map.vert");
_lightmapNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
Application::resourcesPath() + "shaders/model_lightmap_normal_specular_map.frag");
_lightmapNormalSpecularMapProgram.link();
initProgram(_lightmapNormalSpecularMapProgram, _lightmapNormalSpecularMapLocations, 2);
// end lightmap
_shadowProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/model_shadow.vert");
_shadowProgram.addShaderFromSourceFile(QGLShader::Fragment,
@ -621,14 +689,19 @@ bool Model::renderCore(float alpha, RenderMode mode, RenderArgs* args) {
//renderMeshes(RenderMode mode, bool translucent, float alphaThreshold, bool hasTangents, bool hasSpecular, book isSkinned, args);
int opaqueMeshPartsRendered = 0;
opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, false, args);
opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, true, args);
opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, false, args);
opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, true, args);
opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, false, args);
opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, true, args);
opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, false, args);
opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, true, args);
opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, false, false, args);
opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, false, true, args);
opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, true, false, args);
opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, true, true, args);
opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, false, false, args);
opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, false, true, args);
opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, true, false, args);
opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, true, true, args);
opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, false, false, args);
opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, true, false, args);
opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, false, false, args);
opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, true, false, args);
// render translucent meshes afterwards
//Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(false, true, true);
@ -642,14 +715,14 @@ bool Model::renderCore(float alpha, RenderMode mode, RenderArgs* args) {
int translucentMeshPartsRendered = 0;
const float MOSTLY_OPAQUE_THRESHOLD = 0.75f;
translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, false, args);
translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, true, args);
translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, false, args);
translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, true, args);
translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, true, false, false, args);
translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, true, false, true, args);
translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, true, true, false, args);
translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, true, true, true, args);
translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, false, false, args);
translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, false, true, args);
translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, true, false, args);
translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, true, true, args);
translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, false, false, args);
translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, false, true, args);
translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, true, false, args);
translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, true, true, args);
GLBATCH(glDisable)(GL_ALPHA_TEST);
GLBATCH(glEnable)(GL_BLEND);
@ -666,14 +739,14 @@ bool Model::renderCore(float alpha, RenderMode mode, RenderArgs* args) {
if (mode == DEFAULT_RENDER_MODE || mode == DIFFUSE_RENDER_MODE) {
const float MOSTLY_TRANSPARENT_THRESHOLD = 0.0f;
translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, false, args);
translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, true, args);
translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, false, args);
translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, true, args);
translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, false, false, args);
translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, false, true, args);
translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, true, false, args);
translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, true, true, args);
translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, false, false, args);
translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, false, true, args);
translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, true, false, args);
translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, true, true, args);
translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, false, false, args);
translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, false, true, args);
translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, true, false, args);
translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, true, true, args);
}
GLBATCH(glDepthMask)(true);
@ -1562,15 +1635,20 @@ void Model::endScene(RenderMode mode, RenderArgs* args) {
int opaqueMeshPartsRendered = 0;
// now, for each model in the scene, render the mesh portions
opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, false, args);
opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, true, args);
opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, false, args);
opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, true, args);
opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, false, args);
opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, true, args);
opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, false, args);
opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, true, args);
opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, false, false, args);
opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, false, true, args);
opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, true, false, args);
opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, true, true, args);
opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, false, false, args);
opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, false, true, args);
opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, true, false, args);
opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, true, true, args);
opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, false, false, args);
opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, true, false, args);
opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, false, false, args);
opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, true, false, args);
// render translucent meshes afterwards
//Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(false, true, true);
{
@ -1583,15 +1661,15 @@ void Model::endScene(RenderMode mode, RenderArgs* args) {
int translucentParts = 0;
const float MOSTLY_OPAQUE_THRESHOLD = 0.75f;
translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, false, args);
translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, true, args);
translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, false, args);
translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, true, args);
translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, true, false, false, args);
translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, true, false, true, args);
translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, true, true, false, args);
translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, true, true, true, args);
translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, false, false, args);
translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, false, true, args);
translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, true, false, args);
translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, true, true, args);
translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, false, false, args);
translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, false, true, args);
translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, true, false, args);
translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, true, true, args);
GLBATCH(glDisable)(GL_ALPHA_TEST);
GLBATCH(glEnable)(GL_BLEND);
GLBATCH(glDepthMask)(false);
@ -1607,14 +1685,14 @@ void Model::endScene(RenderMode mode, RenderArgs* args) {
if (mode == DEFAULT_RENDER_MODE || mode == DIFFUSE_RENDER_MODE) {
const float MOSTLY_TRANSPARENT_THRESHOLD = 0.0f;
translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, false, args);
translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, true, args);
translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, false, args);
translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, true, args);
translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, false, false, args);
translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, false, true, args);
translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, true, false, args);
translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, true, true, args);
translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, false, false, args);
translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, false, true, args);
translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, true, false, args);
translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, true, true, args);
translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, false, false, args);
translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, false, true, args);
translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, true, false, args);
translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, true, true, args);
}
GLBATCH(glDepthMask)(true);
@ -1689,7 +1767,12 @@ void Model::segregateMeshGroups() {
_meshesOpaqueSkinned.clear();
_meshesOpaqueTangentsSpecularSkinned.clear();
_meshesOpaqueSpecularSkinned.clear();
_meshesOpaqueLightmapTangents.clear();
_meshesOpaqueLightmap.clear();
_meshesOpaqueLightmapTangentsSpecular.clear();
_meshesOpaqueLightmapSpecular.clear();
_unsortedMeshesTranslucentTangents.clear();
_unsortedMeshesTranslucent.clear();
_unsortedMeshesTranslucentTangentsSpecular.clear();
@ -1710,6 +1793,11 @@ void Model::segregateMeshGroups() {
_unsortedMeshesOpaqueTangentsSpecularSkinned.clear();
_unsortedMeshesOpaqueSpecularSkinned.clear();
_unsortedMeshesOpaqueLightmapTangents.clear();
_unsortedMeshesOpaqueLightmap.clear();
_unsortedMeshesOpaqueLightmapTangentsSpecular.clear();
_unsortedMeshesOpaqueLightmapSpecular.clear();
const FBXGeometry& geometry = _geometry->getFBXGeometry();
const QVector<NetworkMesh>& networkMeshes = _geometry->getMeshes();
@ -1723,6 +1811,7 @@ void Model::segregateMeshGroups() {
bool translucentMesh = networkMesh.getTranslucentPartCount(mesh) == networkMesh.parts.size();
bool hasTangents = !mesh.tangents.isEmpty();
bool hasSpecular = mesh.hasSpecularTexture();
bool hasLightmap = mesh.hasEmissiveTexture();
bool isSkinned = state.clusterMatrices.size() > 1;
QString materialID;
@ -1741,71 +1830,93 @@ void Model::segregateMeshGroups() {
qDebug() << "materialID:" << materialID << "parts:" << mesh.parts.size();
}
if (translucentMesh && !hasTangents && !hasSpecular && !isSkinned) {
if (!hasLightmap) {
if (translucentMesh && !hasTangents && !hasSpecular && !isSkinned) {
_unsortedMeshesTranslucent.insertMulti(materialID, i);
_unsortedMeshesTranslucent.insertMulti(materialID, i);
} else if (translucentMesh && hasTangents && !hasSpecular && !isSkinned) {
} else if (translucentMesh && hasTangents && !hasSpecular && !isSkinned) {
_unsortedMeshesTranslucentTangents.insertMulti(materialID, i);
_unsortedMeshesTranslucentTangents.insertMulti(materialID, i);
} else if (translucentMesh && hasTangents && hasSpecular && !isSkinned) {
} else if (translucentMesh && hasTangents && hasSpecular && !isSkinned) {
_unsortedMeshesTranslucentTangentsSpecular.insertMulti(materialID, i);
_unsortedMeshesTranslucentTangentsSpecular.insertMulti(materialID, i);
} else if (translucentMesh && !hasTangents && hasSpecular && !isSkinned) {
} else if (translucentMesh && !hasTangents && hasSpecular && !isSkinned) {
_unsortedMeshesTranslucentSpecular.insertMulti(materialID, i);
_unsortedMeshesTranslucentSpecular.insertMulti(materialID, i);
} else if (translucentMesh && hasTangents && !hasSpecular && isSkinned) {
} else if (translucentMesh && hasTangents && !hasSpecular && isSkinned) {
_unsortedMeshesTranslucentTangentsSkinned.insertMulti(materialID, i);
_unsortedMeshesTranslucentTangentsSkinned.insertMulti(materialID, i);
} else if (translucentMesh && !hasTangents && !hasSpecular && isSkinned) {
} else if (translucentMesh && !hasTangents && !hasSpecular && isSkinned) {
_unsortedMeshesTranslucentSkinned.insertMulti(materialID, i);
_unsortedMeshesTranslucentSkinned.insertMulti(materialID, i);
} else if (translucentMesh && hasTangents && hasSpecular && isSkinned) {
} else if (translucentMesh && hasTangents && hasSpecular && isSkinned) {
_unsortedMeshesTranslucentTangentsSpecularSkinned.insertMulti(materialID, i);
_unsortedMeshesTranslucentTangentsSpecularSkinned.insertMulti(materialID, i);
} else if (translucentMesh && !hasTangents && hasSpecular && isSkinned) {
} else if (translucentMesh && !hasTangents && hasSpecular && isSkinned) {
_unsortedMeshesTranslucentSpecularSkinned.insertMulti(materialID, i);
_unsortedMeshesTranslucentSpecularSkinned.insertMulti(materialID, i);
} else if (!translucentMesh && !hasTangents && !hasSpecular && !isSkinned) {
} else if (!translucentMesh && !hasTangents && !hasSpecular && !isSkinned) {
_unsortedMeshesOpaque.insertMulti(materialID, i);
_unsortedMeshesOpaque.insertMulti(materialID, i);
} else if (!translucentMesh && hasTangents && !hasSpecular && !isSkinned) {
} else if (!translucentMesh && hasTangents && !hasSpecular && !isSkinned) {
_unsortedMeshesOpaqueTangents.insertMulti(materialID, i);
_unsortedMeshesOpaqueTangents.insertMulti(materialID, i);
} else if (!translucentMesh && hasTangents && hasSpecular && !isSkinned) {
} else if (!translucentMesh && hasTangents && hasSpecular && !isSkinned) {
_unsortedMeshesOpaqueTangentsSpecular.insertMulti(materialID, i);
_unsortedMeshesOpaqueTangentsSpecular.insertMulti(materialID, i);
} else if (!translucentMesh && !hasTangents && hasSpecular && !isSkinned) {
} else if (!translucentMesh && !hasTangents && hasSpecular && !isSkinned) {
_unsortedMeshesOpaqueSpecular.insertMulti(materialID, i);
_unsortedMeshesOpaqueSpecular.insertMulti(materialID, i);
} else if (!translucentMesh && hasTangents && !hasSpecular && isSkinned) {
} else if (!translucentMesh && hasTangents && !hasSpecular && isSkinned) {
_unsortedMeshesOpaqueTangentsSkinned.insertMulti(materialID, i);
_unsortedMeshesOpaqueTangentsSkinned.insertMulti(materialID, i);
} else if (!translucentMesh && !hasTangents && !hasSpecular && isSkinned) {
} else if (!translucentMesh && !hasTangents && !hasSpecular && isSkinned) {
_unsortedMeshesOpaqueSkinned.insertMulti(materialID, i);
_unsortedMeshesOpaqueSkinned.insertMulti(materialID, i);
} else if (!translucentMesh && hasTangents && hasSpecular && isSkinned) {
} else if (!translucentMesh && hasTangents && hasSpecular && isSkinned) {
_unsortedMeshesOpaqueTangentsSpecularSkinned.insertMulti(materialID, i);
_unsortedMeshesOpaqueTangentsSpecularSkinned.insertMulti(materialID, i);
} else if (!translucentMesh && !hasTangents && hasSpecular && isSkinned) {
} else if (!translucentMesh && !hasTangents && hasSpecular && isSkinned) {
_unsortedMeshesOpaqueSpecularSkinned.insertMulti(materialID, i);
_unsortedMeshesOpaqueSpecularSkinned.insertMulti(materialID, i);
} else {
qDebug() << "unexpected!!! this mesh didn't fall into any or our groups???";
}
} else {
qDebug() << "unexpected!!! this mesh didn't fall into any or our groups???";
if (!translucentMesh && !hasTangents && !hasSpecular && !isSkinned) {
_unsortedMeshesOpaqueLightmap.insertMulti(materialID, i);
} else if (!translucentMesh && hasTangents && !hasSpecular && !isSkinned) {
_unsortedMeshesOpaqueLightmapTangents.insertMulti(materialID, i);
} else if (!translucentMesh && hasTangents && hasSpecular && !isSkinned) {
_unsortedMeshesOpaqueLightmapTangentsSpecular.insertMulti(materialID, i);
} else if (!translucentMesh && !hasTangents && hasSpecular && !isSkinned) {
_unsortedMeshesOpaqueLightmapSpecular.insertMulti(materialID, i);
} else {
qDebug() << "unexpected!!! this mesh didn't fall into any or our groups???";
}
}
}
@ -1873,6 +1984,22 @@ void Model::segregateMeshGroups() {
_meshesOpaqueSpecularSkinned.append(i);
}
foreach(int i, _unsortedMeshesOpaqueLightmap) {
_meshesOpaqueLightmap.append(i);
}
foreach(int i, _unsortedMeshesOpaqueLightmapTangents) {
_meshesOpaqueLightmapTangents.append(i);
}
foreach(int i, _unsortedMeshesOpaqueLightmapTangentsSpecular) {
_meshesOpaqueLightmapTangentsSpecular.append(i);
}
foreach(int i, _unsortedMeshesOpaqueLightmapSpecular) {
_meshesOpaqueLightmapSpecular.append(i);
}
_unsortedMeshesTranslucentTangents.clear();
_unsortedMeshesTranslucent.clear();
_unsortedMeshesTranslucentTangentsSpecular.clear();
@ -1893,10 +2020,15 @@ void Model::segregateMeshGroups() {
_unsortedMeshesOpaqueTangentsSpecularSkinned.clear();
_unsortedMeshesOpaqueSpecularSkinned.clear();
_unsortedMeshesOpaqueLightmapTangents.clear();
_unsortedMeshesOpaqueLightmap.clear();
_unsortedMeshesOpaqueLightmapTangentsSpecular.clear();
_unsortedMeshesOpaqueLightmapSpecular.clear();
_meshGroupsKnown = true;
}
QVector<int>* Model::pickMeshList(bool translucent, float alphaThreshold, bool hasTangents, bool hasSpecular, bool isSkinned) {
QVector<int>* Model::pickMeshList(bool translucent, float alphaThreshold, bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned) {
PROFILE_RANGE(__FUNCTION__);
// depending on which parameters we were called with, pick the correct mesh group to render
@ -1917,22 +2049,33 @@ QVector<int>* Model::pickMeshList(bool translucent, float alphaThreshold, bool h
whichList = &_meshesTranslucentTangentsSpecularSkinned;
} else if (translucent && !hasTangents && hasSpecular && isSkinned) {
whichList = &_meshesTranslucentSpecularSkinned;
} else if (!translucent && !hasTangents && !hasSpecular && !isSkinned) {
} else if (!translucent && !hasLightmap && !hasTangents && !hasSpecular && !isSkinned) {
whichList = &_meshesOpaque;
} else if (!translucent && hasTangents && !hasSpecular && !isSkinned) {
} else if (!translucent && !hasLightmap && hasTangents && !hasSpecular && !isSkinned) {
whichList = &_meshesOpaqueTangents;
} else if (!translucent && hasTangents && hasSpecular && !isSkinned) {
} else if (!translucent && !hasLightmap && hasTangents && hasSpecular && !isSkinned) {
whichList = &_meshesOpaqueTangentsSpecular;
} else if (!translucent && !hasTangents && hasSpecular && !isSkinned) {
} else if (!translucent && !hasLightmap && !hasTangents && hasSpecular && !isSkinned) {
whichList = &_meshesOpaqueSpecular;
} else if (!translucent && hasTangents && !hasSpecular && isSkinned) {
} else if (!translucent && !hasLightmap && hasTangents && !hasSpecular && isSkinned) {
whichList = &_meshesOpaqueTangentsSkinned;
} else if (!translucent && !hasTangents && !hasSpecular && isSkinned) {
} else if (!translucent && !hasLightmap && !hasTangents && !hasSpecular && isSkinned) {
whichList = &_meshesOpaqueSkinned;
} else if (!translucent && hasTangents && hasSpecular && isSkinned) {
} else if (!translucent && !hasLightmap && hasTangents && hasSpecular && isSkinned) {
whichList = &_meshesOpaqueTangentsSpecularSkinned;
} else if (!translucent && !hasTangents && hasSpecular && isSkinned) {
} else if (!translucent && !hasLightmap && !hasTangents && hasSpecular && isSkinned) {
whichList = &_meshesOpaqueSpecularSkinned;
} else if (!translucent && hasLightmap && !hasTangents && !hasSpecular && !isSkinned) {
whichList = &_meshesOpaqueLightmap;
} else if (!translucent && hasLightmap && hasTangents && !hasSpecular && !isSkinned) {
whichList = &_meshesOpaqueLightmapTangents;
} else if (!translucent && hasLightmap && hasTangents && hasSpecular && !isSkinned) {
whichList = &_meshesOpaqueLightmapTangentsSpecular;
} else if (!translucent && hasLightmap && !hasTangents && hasSpecular && !isSkinned) {
whichList = &_meshesOpaqueLightmapSpecular;
} else {
qDebug() << "unexpected!!! this mesh didn't fall into any or our groups???";
}
@ -1940,14 +2083,13 @@ QVector<int>* Model::pickMeshList(bool translucent, float alphaThreshold, bool h
}
void Model::pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold,
bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args,
SkinLocations*& skinLocations, GLenum& specularTextureUnit) {
bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args,
Locations*& locations, SkinLocations*& skinLocations) {
ProgramObject* program = &_program;
Locations* locations = &_locations;
locations = &_locations;
ProgramObject* skinProgram = &_skinProgram;
skinLocations = &_skinLocations;
specularTextureUnit = 0;
if (mode == SHADOW_RENDER_MODE) {
program = &_shadowProgram;
skinProgram = &_skinShadowProgram;
@ -1958,33 +2100,58 @@ void Model::pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, f
skinProgram = &_skinTranslucentProgram;
skinLocations = &_skinTranslucentLocations;
} else if (hasTangents) {
if (hasSpecular) {
program = &_normalSpecularMapProgram;
locations = &_normalSpecularMapLocations;
skinProgram = &_skinNormalSpecularMapProgram;
skinLocations = &_skinNormalSpecularMapLocations;
specularTextureUnit = GL_TEXTURE2;
} else if (hasLightmap) {
if (hasTangents) {
if (hasSpecular) {
program = &_lightmapNormalSpecularMapProgram;
locations = &_lightmapNormalSpecularMapLocations;
skinProgram = NULL;
skinLocations = NULL;
} else {
program = &_lightmapNormalMapProgram;
locations = &_lightmapNormalMapLocations;
skinProgram = NULL;
skinLocations = NULL;
}
} else if (hasSpecular) {
program = &_lightmapSpecularMapProgram;
locations = &_lightmapSpecularMapLocations;
skinProgram = NULL;
skinLocations = NULL;
} else {
program = &_normalMapProgram;
locations = &_normalMapLocations;
skinProgram = &_skinNormalMapProgram;
skinLocations = &_skinNormalMapLocations;
program = &_lightmapProgram;
locations = &_lightmapLocations;
skinProgram = NULL;
skinLocations = NULL;
}
} else if (hasSpecular) {
program = &_specularMapProgram;
locations = &_specularMapLocations;
skinProgram = &_skinSpecularMapProgram;
skinLocations = &_skinSpecularMapLocations;
specularTextureUnit = GL_TEXTURE1;
}
} else {
if (hasTangents) {
if (hasSpecular) {
program = &_normalSpecularMapProgram;
locations = &_normalSpecularMapLocations;
skinProgram = &_skinNormalSpecularMapProgram;
skinLocations = &_skinNormalSpecularMapLocations;
} else {
program = &_normalMapProgram;
locations = &_normalMapLocations;
skinProgram = &_skinNormalMapProgram;
skinLocations = &_skinNormalMapLocations;
}
} else if (hasSpecular) {
program = &_specularMapProgram;
locations = &_specularMapLocations;
skinProgram = &_skinSpecularMapProgram;
skinLocations = &_skinSpecularMapLocations;
}
}
ProgramObject* activeProgram = program;
Locations* activeLocations = locations;
if (isSkinned) {
activeProgram = skinProgram;
activeLocations = skinLocations;
locations = skinLocations;
}
// This code replace the "bind()" on the QGLProgram
if (!activeProgram->isLinked()) {
@ -1996,26 +2163,26 @@ void Model::pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, f
}
int Model::renderMeshesForModelsInScene(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold,
bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args) {
bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args) {
PROFILE_RANGE(__FUNCTION__);
int meshPartsRendered = 0;
bool pickProgramsNeeded = true;
Locations* locations;
SkinLocations* skinLocations;
GLenum specularTextureUnit;
foreach(Model* model, _modelsInScene) {
QVector<int>* whichList = model->pickMeshList(translucent, alphaThreshold, hasTangents, hasSpecular, isSkinned);
QVector<int>* whichList = model->pickMeshList(translucent, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned);
if (whichList) {
QVector<int>& list = *whichList;
if (list.size() > 0) {
if (pickProgramsNeeded) {
pickPrograms(batch, mode, translucent, alphaThreshold, hasTangents, hasSpecular, isSkinned, args, skinLocations, specularTextureUnit);
pickPrograms(batch, mode, translucent, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, args, locations, skinLocations);
pickProgramsNeeded = false;
}
model->setupBatchTransform(batch);
meshPartsRendered += model->renderMeshesFromList(list, batch, mode, translucent, alphaThreshold, args, skinLocations, specularTextureUnit);
meshPartsRendered += model->renderMeshesFromList(list, batch, mode, translucent, alphaThreshold, args, locations, skinLocations);
GLBATCH(glPopMatrix)();
}
}
@ -2028,12 +2195,12 @@ int Model::renderMeshesForModelsInScene(gpu::Batch& batch, RenderMode mode, bool
}
int Model::renderMeshes(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold,
bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args) {
bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args) {
PROFILE_RANGE(__FUNCTION__);
int meshPartsRendered = 0;
QVector<int>* whichList = pickMeshList(translucent, alphaThreshold, hasTangents, hasSpecular, isSkinned);
QVector<int>* whichList = pickMeshList(translucent, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned);
if (!whichList) {
qDebug() << "unexpected!!! we don't know which list of meshes to render...";
@ -2046,10 +2213,10 @@ int Model::renderMeshes(gpu::Batch& batch, RenderMode mode, bool translucent, fl
return 0;
}
Locations* locations;
SkinLocations* skinLocations;
GLenum specularTextureUnit;
pickPrograms(batch, mode, translucent, alphaThreshold, hasTangents, hasSpecular, isSkinned, args, skinLocations, specularTextureUnit);
meshPartsRendered = renderMeshesFromList(list, batch, mode, translucent, alphaThreshold, args, skinLocations, specularTextureUnit);
pickPrograms(batch, mode, translucent, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, args, locations, skinLocations);
meshPartsRendered = renderMeshesFromList(list, batch, mode, translucent, alphaThreshold, args, locations, skinLocations);
GLBATCH(glUseProgram)(0);
return meshPartsRendered;
@ -2057,7 +2224,7 @@ int Model::renderMeshes(gpu::Batch& batch, RenderMode mode, bool translucent, fl
int Model::renderMeshesFromList(QVector<int>& list, gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, RenderArgs* args,
SkinLocations* skinLocations, GLenum specularTextureUnit) {
Locations* locations, SkinLocations* skinLocations) {
PROFILE_RANGE(__FUNCTION__);
bool dontCullOutOfViewMeshParts = Menu::getInstance()->isOptionChecked(MenuOption::DontCullOutOfViewMeshParts);
bool cullTooSmallMeshParts = !Menu::getInstance()->isOptionChecked(MenuOption::DontCullTooSmallMeshParts);
@ -2174,7 +2341,7 @@ int Model::renderMeshesFromList(QVector<int>& list, gpu::Batch& batch, RenderMod
GLBATCH(glMaterialfv)(GL_FRONT, GL_DIFFUSE, (const float*)&diffuse);
GLBATCH(glMaterialfv)(GL_FRONT, GL_SPECULAR, (const float*)&specular);
GLBATCH(glMaterialf)(GL_FRONT, GL_SHININESS, (part.shininess > 128.0f ? 128.0f: part.shininess));
Texture* diffuseMap = networkPart.diffuseTexture.data();
if (mesh.isEye && diffuseMap) {
diffuseMap = (_dilatedTextures[i][j] =
@ -2182,7 +2349,18 @@ int Model::renderMeshesFromList(QVector<int>& list, gpu::Batch& batch, RenderMod
}
GLBATCH(glBindTexture)(GL_TEXTURE_2D, !diffuseMap ?
Application::getInstance()->getTextureCache()->getWhiteTextureID() : diffuseMap->getID());
if (locations->texcoordMatrices >= 0) {
glm::mat4 texcoordTransform[2];
if (!part.diffuseTexture.transform.isIdentity()) {
part.diffuseTexture.transform.getMatrix(texcoordTransform[0]);
}
if (!part.emissiveTexture.transform.isIdentity()) {
part.emissiveTexture.transform.getMatrix(texcoordTransform[1]);
}
GLBATCH(glUniformMatrix4fv)(locations->texcoordMatrices, 2, false, (const float*) &texcoordTransform);
}
if (!mesh.tangents.isEmpty()) {
GLBATCH(glActiveTexture)(GL_TEXTURE1);
Texture* normalMap = networkPart.normalTexture.data();
@ -2191,13 +2369,25 @@ int Model::renderMeshesFromList(QVector<int>& list, gpu::Batch& batch, RenderMod
GLBATCH(glActiveTexture)(GL_TEXTURE0);
}
if (specularTextureUnit) {
GLBATCH(glActiveTexture)(specularTextureUnit);
if (locations->specularTextureUnit >= 0) {
GLBATCH(glActiveTexture)(GL_TEXTURE0 + locations->specularTextureUnit);
Texture* specularMap = networkPart.specularTexture.data();
GLBATCH(glBindTexture)(GL_TEXTURE_2D, !specularMap ?
Application::getInstance()->getTextureCache()->getWhiteTextureID() : specularMap->getID());
GLBATCH(glActiveTexture)(GL_TEXTURE0);
}
if (locations->emissiveTextureUnit >= 0) {
assert(locations->emissiveParams >= 0); // we should have the emissiveParams defined in the shader
GLBATCH(glUniform2f)(locations->emissiveParams, 0.1f, 4.0f);
GLBATCH(glActiveTexture)(GL_TEXTURE0 + locations->emissiveTextureUnit);
Texture* emissiveMap = networkPart.emissiveTexture.data();
GLBATCH(glBindTexture)(GL_TEXTURE_2D, !emissiveMap ?
Application::getInstance()->getTextureCache()->getWhiteTextureID() : emissiveMap->getID());
GLBATCH(glActiveTexture)(GL_TEXTURE0);
}
if (args) {
args->_materialSwitches++;
}
@ -2232,8 +2422,14 @@ int Model::renderMeshesFromList(QVector<int>& list, gpu::Batch& batch, RenderMod
GLBATCH(glActiveTexture)(GL_TEXTURE0);
}
if (specularTextureUnit) {
GLBATCH(glActiveTexture)(specularTextureUnit);
if (locations->specularTextureUnit >= 0) {
GLBATCH(glActiveTexture)(GL_TEXTURE0 + locations->specularTextureUnit);
GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0);
GLBATCH(glActiveTexture)(GL_TEXTURE0);
}
if (locations->emissiveTextureUnit >= 0) {
GLBATCH(glActiveTexture)(GL_TEXTURE0 + locations->emissiveTextureUnit);
GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0);
GLBATCH(glActiveTexture)(GL_TEXTURE0);
}

View file

@ -303,7 +303,12 @@ private:
static ProgramObject _specularMapProgram;
static ProgramObject _normalSpecularMapProgram;
static ProgramObject _translucentProgram;
static ProgramObject _lightmapProgram;
static ProgramObject _lightmapNormalMapProgram;
static ProgramObject _lightmapSpecularMapProgram;
static ProgramObject _lightmapNormalSpecularMapProgram;
static ProgramObject _shadowProgram;
static ProgramObject _skinProgram;
@ -311,7 +316,7 @@ private:
static ProgramObject _skinSpecularMapProgram;
static ProgramObject _skinNormalSpecularMapProgram;
static ProgramObject _skinTranslucentProgram;
static ProgramObject _skinShadowProgram;
static int _normalMapTangentLocation;
@ -321,6 +326,10 @@ private:
public:
int tangent;
int alphaThreshold;
int texcoordMatrices;
int specularTextureUnit;
int emissiveTextureUnit;
int emissiveParams;
};
static Locations _locations;
@ -328,6 +337,11 @@ private:
static Locations _specularMapLocations;
static Locations _normalSpecularMapLocations;
static Locations _translucentLocations;
static Locations _lightmapLocations;
static Locations _lightmapNormalMapLocations;
static Locations _lightmapSpecularMapLocations;
static Locations _lightmapNormalSpecularMapLocations;
static void initProgram(ProgramObject& program, Locations& locations, int specularTextureUnit = 1);
@ -335,7 +349,7 @@ private:
public:
int clusterMatrices;
int clusterIndices;
int clusterWeights;
int clusterWeights;
};
static SkinLocations _skinLocations;
@ -344,7 +358,7 @@ private:
static SkinLocations _skinNormalSpecularMapLocations;
static SkinLocations _skinShadowLocations;
static SkinLocations _skinTranslucentLocations;
static void initSkinProgram(ProgramObject& program, SkinLocations& locations, int specularTextureUnit = 1);
QVector<AABox> _calculatedMeshBoxes;
@ -376,6 +390,11 @@ private:
QMap<QString, int> _unsortedMeshesOpaqueTangentsSpecularSkinned;
QMap<QString, int> _unsortedMeshesOpaqueSpecularSkinned;
QMap<QString, int> _unsortedMeshesOpaqueLightmap;
QMap<QString, int> _unsortedMeshesOpaqueLightmapTangents;
QMap<QString, int> _unsortedMeshesOpaqueLightmapTangentsSpecular;
QMap<QString, int> _unsortedMeshesOpaqueLightmapSpecular;
QVector<int> _meshesTranslucent;
QVector<int> _meshesTranslucentTangents;
QVector<int> _meshesTranslucentTangentsSpecular;
@ -396,6 +415,12 @@ private:
QVector<int> _meshesOpaqueTangentsSpecularSkinned;
QVector<int> _meshesOpaqueSpecularSkinned;
QVector<int> _meshesOpaqueLightmap;
QVector<int> _meshesOpaqueLightmapTangents;
QVector<int> _meshesOpaqueLightmapTangentsSpecular;
QVector<int> _meshesOpaqueLightmapSpecular;
// Scene rendering support
static QVector<Model*> _modelsInScene;
static gpu::Batch _sceneRenderBatch;
@ -407,19 +432,19 @@ private:
void renderSetup(RenderArgs* args);
bool renderCore(float alpha, RenderMode mode, RenderArgs* args);
int renderMeshes(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold,
bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args = NULL);
bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args = NULL);
void setupBatchTransform(gpu::Batch& batch);
QVector<int>* pickMeshList(bool translucent, float alphaThreshold, bool hasTangents, bool hasSpecular, bool isSkinned);
QVector<int>* pickMeshList(bool translucent, float alphaThreshold, bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned);
int renderMeshesFromList(QVector<int>& list, gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold,
RenderArgs* args, SkinLocations* skinLocations, GLenum specularTextureUnit);
RenderArgs* args, Locations* locations, SkinLocations* skinLocations);
static void pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold,
bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args,
SkinLocations*& skinLocations, GLenum& specularTextureUnit);
bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args,
Locations*& locations, SkinLocations*& skinLocations);
static int renderMeshesForModelsInScene(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold,
bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args);
bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args);
};

View file

@ -25,7 +25,7 @@ class NetworkTexture;
typedef QSharedPointer<NetworkTexture> NetworkTexturePointer;
enum TextureType { DEFAULT_TEXTURE, NORMAL_TEXTURE, SPECULAR_TEXTURE, SPLAT_TEXTURE };
enum TextureType { DEFAULT_TEXTURE, NORMAL_TEXTURE, SPECULAR_TEXTURE, EMISSIVE_TEXTURE, SPLAT_TEXTURE };
/// Stores cached textures, including render-to-texture targets.
class TextureCache : public ResourceCache {
@ -67,7 +67,7 @@ public:
/// Returns the ID of the primary framebuffer object's specular texture.
GLuint getPrimarySpecularTextureID();
/// Enables or disables draw buffers on the primary framebuffer. Note: the primary framebuffer must be bound.
void setPrimaryDrawBuffers(bool color, bool normal = false, bool specular = false);

View file

@ -460,7 +460,8 @@ void Stats::display(
VoxelSystem* voxels = Application::getInstance()->getVoxels();
lines = _expanded ? 14 : 3;
if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessing)) {
bool wantSpatialProcessing = false; // Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessing)
if (_expanded && wantSpatialProcessing) {
lines += 10; // spatial audio processing adds 1 spacing line and 8 extra lines of info
}
@ -652,7 +653,7 @@ void Stats::display(
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color);
}
if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessing)) {
if (_expanded && wantSpatialProcessing) {
verticalOffset += STATS_PELS_PER_LINE; // space one line...
const AudioReflector* audioReflector = Application::getInstance()->getAudioReflector();
@ -660,23 +661,24 @@ void Stats::display(
// add some reflection stats
char reflectionsStatus[128];
bool includeOriginal = true; //Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingIncludeOriginal)
bool separateEars = true; //Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingSeparateEars)
bool stereoSource = true; //Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingStereoSource)
bool randomSurfaces = true; //Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingSlightlyRandomSurfaces)
sprintf(reflectionsStatus, "Reflections: %d, Original: %s, Ears: %s, Source: %s, Normals: %s",
audioReflector->getReflections(),
(Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingIncludeOriginal)
? "included" : "silent"),
(Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingSeparateEars)
? "two" : "one"),
(Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingStereoSource)
? "stereo" : "mono"),
(Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingSlightlyRandomSurfaces)
? "random" : "regular")
(includeOriginal ? "included" : "silent"),
(separateEars ? "two" : "one"),
(stereoSource ? "stereo" : "mono"),
(randomSurfaces ? "random" : "regular")
);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, reflectionsStatus, color);
float preDelay = Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingPreDelay) ?
audioReflector->getPreDelay() : 0.0f;
bool wantPreDelay = true; //Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingPreDelay)
float preDelay = wantPreDelay ? audioReflector->getPreDelay() : 0.0f;
sprintf(reflectionsStatus, "Delay: pre: %6.3f, average %6.3f, max %6.3f, min %6.3f, speed: %6.3f",
preDelay,
@ -688,12 +690,12 @@ void Stats::display(
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, reflectionsStatus, color);
bool distanceAttenuationDisabled = Menu::getInstance()->isOptionChecked(
MenuOption::AudioSpatialProcessingDontDistanceAttenuate);
bool alternateDistanceAttenuationEnabled = Menu::getInstance()->isOptionChecked(
MenuOption::AudioSpatialProcessingAlternateDistanceAttenuate);
//Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingDontDistanceAttenuate);
bool distanceAttenuationDisabled = false;
// Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingAlternateDistanceAttenuate);
bool alternateDistanceAttenuationEnabled = false;
sprintf(reflectionsStatus, "Attenuation: average %5.3f, max %5.3f, min %5.3f, %s: %5.3f",
audioReflector->getAverageAttenuation(),
@ -706,15 +708,14 @@ void Stats::display(
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, reflectionsStatus, color);
sprintf(reflectionsStatus, "Local Audio: %s Attenuation: %5.3f",
(Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingProcessLocalAudio)
? "yes" : "no"),
bool localAudio = true; // Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingProcessLocalAudio);
sprintf(reflectionsStatus, "Local Audio: %s Attenuation: %5.3f", (localAudio ? "yes" : "no"),
audioReflector->getLocalAudioAttenuationFactor());
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, reflectionsStatus, color);
bool diffusionEnabled = Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingWithDiffusions);
bool diffusionEnabled = true; //Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingWithDiffusions);
int fanout = diffusionEnabled ? audioReflector->getDiffusionFanout() : 0;
int diffusionPaths = diffusionEnabled ? audioReflector->getDiffusionPathCount() : 0;
sprintf(reflectionsStatus, "Diffusion: %s, Fanout: %d, Paths: %d",

View file

@ -27,7 +27,8 @@ Base3DOverlay::Base3DOverlay() :
_rotation(),
_isSolid(DEFAULT_IS_SOLID),
_isDashedLine(DEFAULT_IS_DASHED_LINE),
_ignoreRayIntersection(false)
_ignoreRayIntersection(false),
_drawInFront(false)
{
}
@ -48,6 +49,13 @@ Base3DOverlay::~Base3DOverlay() {
void Base3DOverlay::setProperties(const QScriptValue& properties) {
Overlay::setProperties(properties);
QScriptValue drawInFront = properties.property("drawInFront");
if (drawInFront.isValid()) {
bool value = drawInFront.toVariant().toBool();
setDrawInFront(value);
}
QScriptValue position = properties.property("position");
// if "position" property was not there, check to see if they included aliases: start, point, p1
@ -151,6 +159,9 @@ QScriptValue Base3DOverlay::getProperty(const QString& property) {
if (property == "ignoreRayIntersection") {
return _ignoreRayIntersection;
}
if (property == "drawInFront") {
return _drawInFront;
}
return Overlay::getProperty(property);
}

View file

@ -36,6 +36,7 @@ public:
bool getIsSolidLine() const { return !_isDashedLine; }
const glm::quat& getRotation() const { return _rotation; }
bool getIgnoreRayIntersection() const { return _ignoreRayIntersection; }
bool getDrawInFront() const { return _drawInFront; }
// setters
void setPosition(const glm::vec3& position) { _position = position; }
@ -44,6 +45,7 @@ public:
void setIsDashedLine(bool isDashedLine) { _isDashedLine = isDashedLine; }
void setRotation(const glm::quat& value) { _rotation = value; }
void setIgnoreRayIntersection(bool value) { _ignoreRayIntersection = value; }
void setDrawInFront(bool value) { _drawInFront = value; }
virtual void setProperties(const QScriptValue& properties);
virtual QScriptValue getProperty(const QString& property);
@ -64,6 +66,7 @@ protected:
bool _isSolid;
bool _isDashedLine;
bool _ignoreRayIntersection;
bool _drawInFront;
};
#endif // hifi_Base3DOverlay_h

View file

@ -93,7 +93,7 @@ void Overlays::render2D() {
}
}
void Overlays::render3D(RenderArgs::RenderMode renderMode, RenderArgs::RenderSide renderSide) {
void Overlays::render3D(bool drawFront, RenderArgs::RenderMode renderMode, RenderArgs::RenderSide renderSide) {
QReadLocker lock(&_lock);
if (_overlays3D.size() == 0) {
return;
@ -112,6 +112,10 @@ void Overlays::render3D(RenderArgs::RenderMode renderMode, RenderArgs::RenderSid
foreach(Overlay* thisOverlay, _overlays3D) {
Base3DOverlay* overlay3D = static_cast<Base3DOverlay*>(thisOverlay);
if (overlay3D->getDrawInFront() != drawFront) {
continue;
}
glPushMatrix();
switch (thisOverlay->getAnchor()) {
case Overlay::MY_AVATAR:
@ -301,6 +305,7 @@ void OverlayPropertyResultFromScriptValue(const QScriptValue& value, OverlayProp
RayToOverlayIntersectionResult Overlays::findRayIntersection(const PickRay& ray) {
float bestDistance = std::numeric_limits<float>::max();
bool bestIsFront = false;
RayToOverlayIntersectionResult result;
QMapIterator<unsigned int, Overlay*> i(_overlays3D);
i.toBack();
@ -313,7 +318,9 @@ RayToOverlayIntersectionResult Overlays::findRayIntersection(const PickRay& ray)
BoxFace thisFace;
QString thisExtraInfo;
if (thisOverlay->findRayIntersectionExtraInfo(ray.origin, ray.direction, thisDistance, thisFace, thisExtraInfo)) {
if (thisDistance < bestDistance) {
bool isDrawInFront = thisOverlay->getDrawInFront();
if (thisDistance < bestDistance && (!bestIsFront || isDrawInFront)) {
bestIsFront = isDrawInFront;
bestDistance = thisDistance;
result.intersects = true;
result.distance = thisDistance;

View file

@ -13,6 +13,7 @@
#include <QString>
#include <QScriptValue>
#include <QSignalMapper>
#include "Overlay.h"
@ -51,7 +52,7 @@ public:
~Overlays();
void init(QGLWidget* parent);
void update(float deltatime);
void render3D(RenderArgs::RenderMode renderMode = RenderArgs::DEFAULT_RENDER_MODE,
void render3D(bool drawFront, RenderArgs::RenderMode renderMode = RenderArgs::DEFAULT_RENDER_MODE,
RenderArgs::RenderSide renderSide = RenderArgs::MONO);
void render2D();

View file

@ -653,7 +653,6 @@ void EntityTree::update() {
}
void EntityTree::updateChangedEntities(quint64 now, QSet<EntityItemID>& entitiesToDelete) {
// TODO: switch these to iterators so we can remove items that get deleted
foreach (EntityItem* thisEntity, _changedEntities) {
// check to see if the lifetime has expired, for immortal entities this is always false
if (thisEntity->lifetimeHasExpired()) {
@ -675,15 +674,17 @@ void EntityTree::updateMovingEntities(quint64 now, QSet<EntityItemID>& entitiesT
{
PerformanceTimer perfTimer("_movingEntities");
// TODO: switch these to iterators so we can remove items that get deleted
for (int i = 0; i < _movingEntities.size(); i++) {
EntityItem* thisEntity = _movingEntities[i];
QList<EntityItem*>::iterator item_itr = _movingEntities.begin();
while (item_itr != _movingEntities.end()) {
EntityItem* thisEntity = *item_itr;
// always check to see if the lifetime has expired, for immortal entities this is always false
if (thisEntity->lifetimeHasExpired()) {
qDebug() << "Lifetime has expired for entity:" << thisEntity->getEntityItemID();
entitiesToDelete << thisEntity->getEntityItemID();
clearEntityState(thisEntity);
// remove thisEntity from the list
item_itr = _movingEntities.erase(item_itr);
thisEntity->setSimulationState(EntityItem::Static);
} else {
AACube oldCube = thisEntity->getMaximumAACube();
thisEntity->update(now);
@ -694,10 +695,22 @@ void EntityTree::updateMovingEntities(quint64 now, QSet<EntityItemID>& entitiesT
if (!domainBounds.touches(newCube)) {
qDebug() << "Entity " << thisEntity->getEntityItemID() << " moved out of domain bounds.";
entitiesToDelete << thisEntity->getEntityItemID();
clearEntityState(thisEntity);
// remove thisEntity from the list
item_itr = _movingEntities.erase(item_itr);
thisEntity->setSimulationState(EntityItem::Static);
} else {
moveOperator.addEntityToMoveList(thisEntity, oldCube, newCube);
updateEntityState(thisEntity);
EntityItem::SimulationState newState = thisEntity->computeSimulationState();
if (newState != EntityItem::Moving) {
if (newState == EntityItem::Mortal) {
_mortalEntities.push_back(thisEntity);
}
// remove thisEntity from the list
item_itr = _movingEntities.erase(item_itr);
thisEntity->setSimulationState(newState);
} else {
++item_itr;
}
}
}
}
@ -710,18 +723,30 @@ void EntityTree::updateMovingEntities(quint64 now, QSet<EntityItemID>& entitiesT
}
void EntityTree::updateMortalEntities(quint64 now, QSet<EntityItemID>& entitiesToDelete) {
// TODO: switch these to iterators so we can remove items that get deleted
for (int i = 0; i < _mortalEntities.size(); i++) {
EntityItem* thisEntity = _mortalEntities[i];
QList<EntityItem*>::iterator item_itr = _mortalEntities.begin();
while (item_itr != _mortalEntities.end()) {
EntityItem* thisEntity = *item_itr;
thisEntity->update(now);
// always check to see if the lifetime has expired, for immortal entities this is always false
if (thisEntity->lifetimeHasExpired()) {
qDebug() << "Lifetime has expired for entity:" << thisEntity->getEntityItemID();
entitiesToDelete << thisEntity->getEntityItemID();
clearEntityState(thisEntity);
// remove thisEntity from the list
item_itr = _mortalEntities.erase(item_itr);
thisEntity->setSimulationState(EntityItem::Static);
} else {
// check to see if this entity is no longer moving
updateEntityState(thisEntity);
EntityItem::SimulationState newState = thisEntity->computeSimulationState();
if (newState != EntityItem::Mortal) {
if (newState == EntityItem::Moving) {
_movingEntities.push_back(thisEntity);
}
// remove thisEntity from the list
item_itr = _mortalEntities.erase(item_itr);
thisEntity->setSimulationState(newState);
} else {
++item_itr;
}
}
}
}

View file

@ -30,8 +30,54 @@
#include "FBXReader.h"
// TOOL: Uncomment the following line to enable the filtering of all the unkwnon fields of a node so we can break point easily while loading a model with problems...
//#define DEBUG_FBXREADER
using namespace std;
struct TextureParam {
glm::vec2 UVTranslation;
glm::vec2 UVScaling;
glm::vec4 cropping;
std::string UVSet;
glm::vec3 translation;
glm::vec3 rotation;
glm::vec3 scaling;
uint8_t alphaSource;
uint8_t currentTextureBlendMode;
bool useMaterial;
template <typename T>
bool assign(T& ref, const T& v) {
if (ref == v) {
return false;
} else {
ref = v;
isDefault = false;
return true;
}
}
bool isDefault;
TextureParam() :
UVTranslation(0.0f),
UVScaling(1.0f),
cropping(0.0f),
UVSet("map1"),
translation(0.0f),
rotation(0.0f),
scaling(1.0f),
alphaSource(0),
currentTextureBlendMode(0),
useMaterial(true),
isDefault(true)
{}
};
bool FBXMesh::hasSpecularTexture() const {
foreach (const FBXMeshPart& part, parts) {
if (!part.specularTexture.filename.isEmpty()) {
@ -41,6 +87,15 @@ bool FBXMesh::hasSpecularTexture() const {
return false;
}
bool FBXMesh::hasEmissiveTexture() const {
foreach (const FBXMeshPart& part, parts) {
if (!part.emissiveTexture.filename.isEmpty()) {
return true;
}
}
return false;
}
QStringList FBXGeometry::getJointNames() const {
QStringList names;
foreach (const FBXJoint& joint, joints) {
@ -709,6 +764,7 @@ class Vertex {
public:
int originalIndex;
glm::vec2 texCoord;
glm::vec2 texCoord1;
};
uint qHash(const Vertex& vertex, uint seed = 0) {
@ -716,7 +772,7 @@ uint qHash(const Vertex& vertex, uint seed = 0) {
}
bool operator==(const Vertex& v1, const Vertex& v2) {
return v1.originalIndex == v2.originalIndex && v1.texCoord == v2.texCoord;
return v1.originalIndex == v2.originalIndex && v1.texCoord == v2.texCoord && v1.texCoord1 == v2.texCoord1;
}
class ExtractedMesh {
@ -725,6 +781,16 @@ public:
QMultiHash<int, int> newIndices;
QVector<QHash<int, int> > blendshapeIndexMaps;
QVector<QPair<int, int> > partMaterialTextures;
QHash<QString, int> texcoordSetMap;
std::map<std::string, int> texcoordSetMap2;
};
class AttributeData {
public:
QVector<glm::vec2> texCoords;
QVector<int> texCoordIndices;
std::string name;
int index;
};
class MeshData {
@ -739,6 +805,8 @@ public:
QVector<int> texCoordIndices;
QHash<Vertex, int> indices;
std::vector<AttributeData> attributes;
};
void appendIndex(MeshData& data, QVector<int>& indices, int index) {
@ -780,6 +848,20 @@ void appendIndex(MeshData& data, QVector<int>& indices, int index) {
vertex.texCoord = data.texCoords.at(texCoordIndex);
}
}
bool hasMoreTexcoords = (data.attributes.size() > 1);
if (hasMoreTexcoords) {
if (data.attributes[1].texCoordIndices.empty()) {
if (index < data.attributes[1].texCoords.size()) {
vertex.texCoord1 = data.attributes[1].texCoords.at(index);
}
} else if (index < data.attributes[1].texCoordIndices.size()) {
int texCoordIndex = data.attributes[1].texCoordIndices.at(index);
if (texCoordIndex >= 0 && texCoordIndex < data.attributes[1].texCoords.size()) {
vertex.texCoord1 = data.attributes[1].texCoords.at(texCoordIndex);
}
}
}
QHash<Vertex, int>::const_iterator it = data.indices.find(vertex);
if (it == data.indices.constEnd()) {
@ -790,7 +872,9 @@ void appendIndex(MeshData& data, QVector<int>& indices, int index) {
data.extracted.mesh.vertices.append(position);
data.extracted.mesh.normals.append(normal);
data.extracted.mesh.texCoords.append(vertex.texCoord);
if (hasMoreTexcoords) {
data.extracted.mesh.texCoords1.append(vertex.texCoord1);
}
} else {
indices.append(*it);
data.extracted.mesh.normals[*it] += normal;
@ -829,13 +913,67 @@ ExtractedMesh extractMesh(const FBXNode& object) {
// hack to work around wacky Makehuman exports
data.normalsByVertex = true;
}
} else if (child.name == "LayerElementUV" && child.properties.at(0).toInt() == 0) {
foreach (const FBXNode& subdata, child.children) {
if (subdata.name == "UV") {
data.texCoords = createVec2Vector(getDoubleVector(subdata));
} else if (child.name == "LayerElementUV") {
if (child.properties.at(0).toInt() == 0) {
AttributeData attrib;
attrib.index = child.properties.at(0).toInt();
foreach (const FBXNode& subdata, child.children) {
if (subdata.name == "UV") {
data.texCoords = createVec2Vector(getDoubleVector(subdata));
attrib.texCoords = createVec2Vector(getDoubleVector(subdata));
} else if (subdata.name == "UVIndex") {
data.texCoordIndices = getIntVector(subdata);
attrib.texCoordIndices = getIntVector(subdata);
} else if (subdata.name == "Name") {
attrib.name = subdata.properties.at(0).toString().toStdString();
}
#if defined(DEBUG_FBXREADER)
else {
int unknown = 0;
std::string subname = subdata.name.data();
if ( (subdata.name == "Version")
|| (subdata.name == "MappingInformationType")
|| (subdata.name == "ReferenceInformationType") ) {
} else {
unknown++;
}
}
#endif
}
data.extracted.texcoordSetMap.insert(QString(attrib.name.c_str()), data.attributes.size());
data.attributes.push_back(attrib);
} else {
AttributeData attrib;
attrib.index = child.properties.at(0).toInt();
foreach (const FBXNode& subdata, child.children) {
if (subdata.name == "UV") {
attrib.texCoords = createVec2Vector(getDoubleVector(subdata));
} else if (subdata.name == "UVIndex") {
attrib.texCoordIndices = getIntVector(subdata);
} else if (subdata.name == "Name") {
attrib.name = subdata.properties.at(0).toString().toStdString();
}
#if defined(DEBUG_FBXREADER)
else {
int unknown = 0;
std::string subname = subdata.name.data();
if ( (subdata.name == "Version")
|| (subdata.name == "MappingInformationType")
|| (subdata.name == "ReferenceInformationType") ) {
} else {
unknown++;
}
}
#endif
}
} else if (subdata.name == "UVIndex") {
data.texCoordIndices = getIntVector(subdata);
QHash<QString, int>::iterator it = data.extracted.texcoordSetMap.find(QString(attrib.name.c_str()));
if (it == data.extracted.texcoordSetMap.end()) {
data.extracted.texcoordSetMap.insert(QString(attrib.name.c_str()), data.attributes.size());
data.attributes.push_back(attrib);
} else {
// WTF same names for different UVs?
qDebug() << "LayerElementUV #" << attrib.index << " is reusing the same name as #" << (*it) << ". Skip this texcoord attribute.";
}
}
} else if (child.name == "LayerElementMaterial") {
@ -1005,11 +1143,25 @@ public:
FBXTexture getTexture(const QString& textureID,
const QHash<QString, QString>& textureNames,
const QHash<QString, QByteArray>& textureFilenames,
const QHash<QByteArray, QByteArray>& textureContent) {
const QHash<QByteArray, QByteArray>& textureContent,
const QHash<QString, TextureParam>& textureParams) {
FBXTexture texture;
texture.filename = textureFilenames.value(textureID);
texture.name = textureNames.value(textureID);
texture.content = textureContent.value(texture.filename);
texture.transform.setIdentity();
texture.texcoordSet = 0;
QHash<QString, TextureParam>::const_iterator it = textureParams.constFind(textureID);
if (it != textureParams.end()) {
const TextureParam& p = (*it);
texture.transform.setTranslation(p.translation);
texture.transform.setRotation(glm::quat(glm::radians(p.rotation)));
texture.transform.setScale(p.scaling);
if ((p.UVSet != "map1") || (p.UVSet != "UVSet0")) {
texture.texcoordSet = 1;
}
texture.texcoordSetName = p.UVSet;
}
return texture;
}
@ -1025,6 +1177,23 @@ bool checkMaterialsHaveTextures(const QHash<QString, Material>& materials,
return false;
}
int matchTextureUVSetToAttributeChannel(const std::string& texUVSetName, const QHash<QString, int>& texcoordChannels) {
if (texUVSetName.empty()) {
return 0;
} else {
QHash<QString, int>::const_iterator tcUnit = texcoordChannels.find(QString(texUVSetName.c_str()));
if (tcUnit != texcoordChannels.end()) {
int channel = (*tcUnit);
if (channel >= 2) {
channel = 0;
}
return channel;
} else {
return 0;
}
}
}
FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) {
QHash<QString, ExtractedMesh> meshes;
QHash<QString, QString> modelIDsToNames;
@ -1039,12 +1208,14 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
QHash<QString, AnimationCurve> animationCurves;
QHash<QString, QString> textureNames;
QHash<QString, QByteArray> textureFilenames;
QHash<QString, TextureParam> textureParams;
QHash<QByteArray, QByteArray> textureContent;
QHash<QString, Material> materials;
QHash<QString, QString> typeFlags;
QHash<QString, QString> diffuseTextures;
QHash<QString, QString> bumpTextures;
QHash<QString, QString> specularTextures;
QHash<QString, QString> emissiveTextures;
QHash<QString, QString> localRotations;
QHash<QString, QString> xComponents;
QHash<QString, QString> yComponents;
@ -1099,7 +1270,9 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
}
}
QMultiHash<QString, WeightedIndex> blendshapeChannelIndices;
#if defined(DEBUG_FBXREADER)
int unknown = 0;
#endif
FBXGeometry geometry;
float unitScaleFactor = 1.0f;
foreach (const FBXNode& child, node.children) {
@ -1302,6 +1475,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
models.insert(getID(object.properties), model);
} else if (object.name == "Texture") {
TextureParam tex;
bool texparam = false;
foreach (const FBXNode& subobject, object.children) {
if (subobject.name == "RelativeFilename") {
// trim off any path information
@ -1313,7 +1488,65 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
QString name = QString(subobject.properties.at(0).toByteArray());
name = name.left(name.indexOf('['));
textureNames.insert(getID(object.properties), name);
} else if (subobject.name == "Texture_Alpha_Source") {
tex.assign<uint8_t>(tex.alphaSource, subobject.properties.at(0).value<int>());
} else if (subobject.name == "ModelUVTranslation") {
tex.assign(tex.UVTranslation, glm::vec2(subobject.properties.at(0).value<double>(),
subobject.properties.at(1).value<double>()));
} else if (subobject.name == "ModelUVScaling") {
tex.assign(tex.UVScaling, glm::vec2(subobject.properties.at(0).value<double>(),
subobject.properties.at(1).value<double>()));
} else if (subobject.name == "Cropping") {
tex.assign(tex.cropping, glm::vec4(subobject.properties.at(0).value<int>(),
subobject.properties.at(1).value<int>(),
subobject.properties.at(2).value<int>(),
subobject.properties.at(3).value<int>()));
} else if (subobject.name == "Properties70") {
QByteArray propertyName;
int index;
propertyName = "P";
index = 4;
foreach (const FBXNode& property, subobject.children) {
if (property.name == propertyName) {
QString v = property.properties.at(0).toString();
if (property.properties.at(0) == "UVSet") {
tex.assign(tex.UVSet, property.properties.at(index).toString().toStdString());
} else if (property.properties.at(0) == "CurrentTextureBlendMode") {
tex.assign<uint8_t>(tex.currentTextureBlendMode, property.properties.at(index).value<int>());
} else if (property.properties.at(0) == "UseMaterial") {
tex.assign<bool>(tex.useMaterial, property.properties.at(index).value<int>());
} else if (property.properties.at(0) == "Translation") {
tex.assign(tex.translation, getVec3(property.properties, index));
} else if (property.properties.at(0) == "Rotation") {
tex.assign(tex.rotation, getVec3(property.properties, index));
} else if (property.properties.at(0) == "Scaling") {
tex.assign(tex.scaling, getVec3(property.properties, index));
}
#if defined(DEBUG_FBXREADER)
else {
std::string propName = v.toStdString();
unknown++;
}
#endif
}
}
}
#if defined(DEBUG_FBXREADER)
else {
if (subobject.name == "Type") {
} else if (subobject.name == "Version") {
} else if (subobject.name == "FileName") {
} else if (subobject.name == "Media") {
} else {
std::string subname = subobject.name.data();
unknown++;
}
}
#endif
}
if (!tex.isDefault) {
textureParams.insert(getID(object.properties), tex);
}
} else if (object.name == "Video") {
QByteArray filename;
@ -1425,6 +1658,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
ooChildToParent.insert(childID, parentID);
}
if (connection.properties.at(0) == "OP") {
int counter = 0;
QByteArray type = connection.properties.at(3).toByteArray().toLower();
if (type.contains("diffuse")) {
diffuseTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
@ -1446,6 +1680,15 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
} else if (type == "d|z") {
zComponents.insert(getID(connection.properties, 2), getID(connection.properties, 1));
} else if (type.contains("shininess")) {
counter++;
} else if (type.contains("emissive")) {
emissiveTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
} else {
counter++;
}
}
parentMap.insert(getID(connection.properties, 1), getID(connection.properties, 2));
@ -1650,33 +1893,63 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
const QString& childID = children.at(i);
if (materials.contains(childID)) {
Material material = materials.value(childID);
bool detectDifferentUVs = false;
FBXTexture diffuseTexture;
QString diffuseTextureID = diffuseTextures.value(childID);
if (!diffuseTextureID.isNull()) {
diffuseTexture = getTexture(diffuseTextureID, textureNames, textureFilenames, textureContent);
diffuseTexture = getTexture(diffuseTextureID, textureNames, textureFilenames, textureContent, textureParams);
// FBX files generated by 3DSMax have an intermediate texture parent, apparently
foreach (const QString& childTextureID, childMap.values(diffuseTextureID)) {
if (textureFilenames.contains(childTextureID)) {
diffuseTexture = getTexture(diffuseTextureID, textureNames, textureFilenames, textureContent);
diffuseTexture = getTexture(diffuseTextureID, textureNames, textureFilenames, textureContent, textureParams);
}
}
diffuseTexture.texcoordSet = matchTextureUVSetToAttributeChannel(diffuseTexture.texcoordSetName, extracted.texcoordSetMap);
detectDifferentUVs = (diffuseTexture.texcoordSet != 0) || (!diffuseTexture.transform.isIdentity());
}
FBXTexture normalTexture;
QString bumpTextureID = bumpTextures.value(childID);
if (!bumpTextureID.isNull()) {
normalTexture = getTexture(bumpTextureID, textureNames, textureFilenames, textureContent);
normalTexture = getTexture(bumpTextureID, textureNames, textureFilenames, textureContent, textureParams);
generateTangents = true;
normalTexture.texcoordSet = matchTextureUVSetToAttributeChannel(normalTexture.texcoordSetName, extracted.texcoordSetMap);
detectDifferentUVs |= (normalTexture.texcoordSet != 0) || (!normalTexture.transform.isIdentity());
}
FBXTexture specularTexture;
QString specularTextureID = specularTextures.value(childID);
if (!specularTextureID.isNull()) {
specularTexture = getTexture(specularTextureID, textureNames, textureFilenames, textureContent);
specularTexture = getTexture(specularTextureID, textureNames, textureFilenames, textureContent, textureParams);
specularTexture.texcoordSet = matchTextureUVSetToAttributeChannel(specularTexture.texcoordSetName, extracted.texcoordSetMap);
detectDifferentUVs |= (specularTexture.texcoordSet != 0) || (!specularTexture.transform.isIdentity());
}
FBXTexture emissiveTexture;
QString emissiveTextureID = emissiveTextures.value(childID);
if (!emissiveTextureID.isNull()) {
emissiveTexture = getTexture(emissiveTextureID, textureNames, textureFilenames, textureContent, textureParams);
// FBX files generated by 3DSMax have an intermediate texture parent, apparently
foreach (const QString& childTextureID, childMap.values(diffuseTextureID)) {
if (textureFilenames.contains(childTextureID)) {
emissiveTexture = getTexture(emissiveTextureID, textureNames, textureFilenames, textureContent, textureParams);
}
}
emissiveTexture.texcoordSet = matchTextureUVSetToAttributeChannel(emissiveTexture.texcoordSetName, extracted.texcoordSetMap);
detectDifferentUVs |= (emissiveTexture.texcoordSet != 0) || (!emissiveTexture.transform.isIdentity());
}
if (detectDifferentUVs) {
detectDifferentUVs = false;
}
for (int j = 0; j < extracted.partMaterialTextures.size(); j++) {
if (extracted.partMaterialTextures.at(j).first == materialIndex) {
FBXMeshPart& part = extracted.mesh.parts[j];
@ -1694,13 +1967,16 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
if (!specularTexture.filename.isNull()) {
part.specularTexture = specularTexture;
}
if (!emissiveTexture.filename.isNull()) {
part.emissiveTexture = emissiveTexture;
}
part.materialID = material.id;
}
}
materialIndex++;
} else if (textureFilenames.contains(childID)) {
FBXTexture texture = getTexture(childID, textureNames, textureFilenames, textureContent);
FBXTexture texture = getTexture(childID, textureNames, textureFilenames, textureContent, textureParams);
for (int j = 0; j < extracted.partMaterialTextures.size(); j++) {
int partTexture = extracted.partMaterialTextures.at(j).second;
if (partTexture == textureIndex && !(partTexture == 0 && materialsHaveTextures)) {

View file

@ -19,6 +19,7 @@
#include <QVector>
#include <Extents.h>
#include <Transform.h>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
@ -102,6 +103,10 @@ public:
QString name;
QByteArray filename;
QByteArray content;
Transform transform;
int texcoordSet;
std::string texcoordSetName;
};
/// A single part of a mesh (with the same material).
@ -120,6 +125,7 @@ public:
FBXTexture diffuseTexture;
FBXTexture normalTexture;
FBXTexture specularTexture;
FBXTexture emissiveTexture;
QString materialID;
};
@ -135,18 +141,20 @@ public:
QVector<glm::vec3> tangents;
QVector<glm::vec3> colors;
QVector<glm::vec2> texCoords;
QVector<glm::vec2> texCoords1;
QVector<glm::vec4> clusterIndices;
QVector<glm::vec4> clusterWeights;
QVector<FBXCluster> clusters;
Extents meshExtents;
bool isEye;
QVector<FBXBlendshape> blendshapes;
bool hasSpecularTexture() const;
bool hasEmissiveTexture() const;
};
/// A single animation frame extracted from an FBX document.

View file

@ -136,7 +136,10 @@ protected:
void invalidCache() const { _flags.set(FLAG_CACHE_INVALID, true); }
void flagTranslation() { _flags.set(FLAG_TRANSLATION, true); }
void unflagTranslation() { _flags.set(FLAG_TRANSLATION, false); }
void flagRotation() { _flags.set(FLAG_ROTATION, true); }
void unflagRotation() { _flags.set(FLAG_ROTATION, false); }
void flagScaling() { _flags.set(FLAG_SCALING, true); }
void unflagScaling() { _flags.set(FLAG_SCALING, false); }
@ -162,17 +165,23 @@ inline const Transform::Vec3& Transform::getTranslation() const {
inline void Transform::setTranslation(const Vec3& translation) {
invalidCache();
flagTranslation();
if (translation == Vec3()) {
unflagTranslation();
} else {
flagTranslation();
}
_translation = translation;
}
inline void Transform::preTranslate(const Vec3& translation) {
if (translation == Vec3() ) return;
invalidCache();
flagTranslation();
_translation += translation;
}
inline void Transform::postTranslate(const Vec3& translation) {
if (translation == Vec3() ) return;
invalidCache();
flagTranslation();
@ -192,11 +201,16 @@ inline const Transform::Quat& Transform::getRotation() const {
inline void Transform::setRotation(const Quat& rotation) {
invalidCache();
flagRotation();
if (rotation == Quat()) {
unflagRotation();
} else {
flagRotation();
}
_rotation = rotation;
}
inline void Transform::preRotate(const Quat& rotation) {
if (rotation == Quat()) return;
invalidCache();
if (isRotating()) {
_rotation = rotation * _rotation;
@ -204,10 +218,12 @@ inline void Transform::preRotate(const Quat& rotation) {
_rotation = rotation;
}
flagRotation();
_translation = glm::rotate(rotation, _translation);
}
inline void Transform::postRotate(const Quat& rotation) {
if (rotation == Quat()) return;
invalidCache();
if (isNonUniform()) {