mirror of
https://github.com/lubosz/overte.git
synced 2025-04-08 10:43:56 +02:00
merge upstream/master into andrew/inertia
Conflicts: libraries/avatars/CMakeLists.txt libraries/entities/CMakeLists.txt
This commit is contained in:
commit
9b7e50858a
95 changed files with 3094 additions and 657 deletions
|
@ -12,6 +12,10 @@ if (POLICY CMP0043)
|
|||
cmake_policy(SET CMP0043 OLD)
|
||||
endif ()
|
||||
|
||||
if (POLICY CMP0042)
|
||||
cmake_policy(SET CMP0042 OLD)
|
||||
endif ()
|
||||
|
||||
project(hifi)
|
||||
add_definitions(-DGLM_FORCE_RADIANS)
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ include_glm()
|
|||
|
||||
# link in the shared libraries
|
||||
link_hifi_libraries(
|
||||
audio avatars octree voxels fbx entities metavoxels
|
||||
audio avatars octree voxels gpu model fbx entities metavoxels
|
||||
networking animation shared script-engine embedded-webserver
|
||||
physics
|
||||
)
|
||||
|
@ -15,4 +15,4 @@ if (UNIX)
|
|||
target_link_libraries(${TARGET_NAME} ${CMAKE_DL_LIBS})
|
||||
endif (UNIX)
|
||||
|
||||
link_shared_dependencies()
|
||||
include_dependency_includes()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#
|
||||
# LinkSharedDependencies.cmake
|
||||
# IncludeDependencyIncludes.cmake
|
||||
# cmake/macros
|
||||
#
|
||||
# Copyright 2014 High Fidelity, Inc.
|
||||
|
@ -9,7 +9,7 @@
|
|||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
|
||||
macro(LINK_SHARED_DEPENDENCIES)
|
||||
macro(INCLUDE_DEPENDENCY_INCLUDES)
|
||||
if (${TARGET_NAME}_DEPENDENCY_INCLUDES)
|
||||
list(REMOVE_DUPLICATES ${TARGET_NAME}_DEPENDENCY_INCLUDES)
|
||||
|
||||
|
@ -19,4 +19,4 @@ macro(LINK_SHARED_DEPENDENCIES)
|
|||
|
||||
# set the property on this target so it can be retreived by targets linking to us
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES DEPENDENCY_INCLUDES "${${TARGET_NAME}_DEPENDENCY_INCLUDES}")
|
||||
endmacro(LINK_SHARED_DEPENDENCIES)
|
||||
endmacro(INCLUDE_DEPENDENCY_INCLUDES)
|
|
@ -1,47 +0,0 @@
|
|||
#
|
||||
# FindGLUT.cmake
|
||||
#
|
||||
# Try to find GLUT library and include path.
|
||||
# Once done this will define
|
||||
#
|
||||
# GLUT_FOUND
|
||||
# GLUT_INCLUDE_DIRS
|
||||
# GLUT_LIBRARIES
|
||||
#
|
||||
# Created on 2/6/2014 by Stephen Birarda
|
||||
# Copyright 2014 High Fidelity, Inc.
|
||||
#
|
||||
# Adapted from FindGLUT.cmake available in tlorach's OpenGLText Repository
|
||||
# https://raw.github.com/tlorach/OpenGLText/master/cmake/FindGLUT.cmake
|
||||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
|
||||
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
|
||||
hifi_library_search_hints("freeglut")
|
||||
|
||||
if (WIN32)
|
||||
set(GLUT_HINT_DIRS "${FREEGLUT_SEARCH_DIRS} ${OPENGL_INCLUDE_DIR}")
|
||||
|
||||
find_path(GLUT_INCLUDE_DIRS GL/glut.h PATH_SUFFIXES include HINTS ${FREEGLUT_SEARCH_DIRS})
|
||||
find_library(GLUT_LIBRARY freeglut PATH_SUFFIXES lib HINTS ${FREEGLUT_SEARCH_DIRS})
|
||||
else ()
|
||||
find_path(GLUT_INCLUDE_DIRS GL/glut.h PATH_SUFFIXES include HINTS ${FREEGLUT_SEARCH_DIRS})
|
||||
find_library(GLUT_LIBRARY glut PATH_SUFFIXES lib HINTS ${FREEGLUT_SEARCH_DIRS})
|
||||
endif ()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
set(GLUT_LIBRARIES "${GLUT_LIBRARY}" "${XMU_LIBRARY}" "${XI_LIBRARY}")
|
||||
|
||||
if (UNIX)
|
||||
find_library(XI_LIBRARY Xi PATH_SUFFIXES lib HINTS ${FREEGLUT_SEARCH_DIRS})
|
||||
find_library(XMU_LIBRARY Xmu PATH_SUFFIXES lib HINTS ${FREEGLUT_SEARCH_DIRS})
|
||||
|
||||
find_package_handle_standard_args(GLUT DEFAULT_MSG GLUT_INCLUDE_DIRS GLUT_LIBRARIES XI_LIBRARY XMU_LIBRARY)
|
||||
else ()
|
||||
find_package_handle_standard_args(GLUT DEFAULT_MSG GLUT_INCLUDE_DIRS GLUT_LIBRARIES)
|
||||
endif ()
|
||||
|
||||
mark_as_advanced(GLUT_INCLUDE_DIRS GLUT_LIBRARIES GLUT_LIBRARY XI_LIBRARY XMU_LIBRARY FREEGLUT_SEARCH_DIRS)
|
|
@ -52,4 +52,4 @@ include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}")
|
|||
# append OpenSSL to our list of libraries to link
|
||||
target_link_libraries(${TARGET_NAME} ${OPENSSL_LIBRARIES})
|
||||
|
||||
link_shared_dependencies()
|
||||
include_dependency_includes()
|
|
@ -549,7 +549,9 @@ void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet<Ass
|
|||
for (Assignment::Type defaultedType = Assignment::AudioMixerType;
|
||||
defaultedType != Assignment::AllTypes;
|
||||
defaultedType = static_cast<Assignment::Type>(static_cast<int>(defaultedType) + 1)) {
|
||||
if (!excludedTypes.contains(defaultedType) && defaultedType != Assignment::AgentType) {
|
||||
if (!excludedTypes.contains(defaultedType)
|
||||
&& defaultedType != Assignment::UNUSED
|
||||
&& defaultedType != Assignment::AgentType) {
|
||||
// type has not been set from a command line or config file config, use the default
|
||||
// by clearing whatever exists and writing a single default assignment with no payload
|
||||
Assignment* newAssignment = new Assignment(Assignment::CreateCommand, (Assignment::Type) defaultedType);
|
||||
|
|
|
@ -15,3 +15,4 @@ Script.load("hydraMove.js");
|
|||
Script.load("headMove.js");
|
||||
Script.load("inspect.js");
|
||||
Script.load("lobby.js");
|
||||
Script.load("notifications.js");
|
||||
|
|
|
@ -13,34 +13,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
(function(){
|
||||
this.oldColor = {};
|
||||
this.oldColorKnown = false;
|
||||
this.storeOldColor = function(entityID) {
|
||||
var oldProperties = Entities.getEntityProperties(entityID);
|
||||
this.oldColor = oldProperties.color;
|
||||
this.oldColorKnown = true;
|
||||
print("storing old color... this.oldColor=" + this.oldColor.red + "," + this.oldColor.green + "," + this.oldColor.blue);
|
||||
};
|
||||
|
||||
this.preload = function(entityID) {
|
||||
print("preload");
|
||||
this.storeOldColor(entityID);
|
||||
};
|
||||
|
||||
this.hoverEnterEntity = function(entityID, mouseEvent) {
|
||||
print("hoverEnterEntity");
|
||||
if (!this.oldColorKnown) {
|
||||
this.storeOldColor(entityID);
|
||||
}
|
||||
Entities.editEntity(entityID, { color: { red: 0, green: 255, blue: 255} });
|
||||
};
|
||||
this.hoverLeaveEntity = function(entityID, mouseEvent) {
|
||||
print("hoverLeaveEntity");
|
||||
if (this.oldColorKnown) {
|
||||
print("leave restoring old color... this.oldColor="
|
||||
+ this.oldColor.red + "," + this.oldColor.green + "," + this.oldColor.blue);
|
||||
Entities.editEntity(entityID, { color: this.oldColor });
|
||||
}
|
||||
};
|
||||
})
|
||||
(function() {
|
||||
Script.include("changeColorOnHoverClass.js");
|
||||
return new ChangeColorOnHover();
|
||||
})
|
||||
|
|
55
examples/entityScripts/changeColorOnHoverClass.js
Normal file
55
examples/entityScripts/changeColorOnHoverClass.js
Normal file
|
@ -0,0 +1,55 @@
|
|||
//
|
||||
// changeColorOnHover.js
|
||||
// examples/entityScripts
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 11/1/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// This is an example of an entity script which when assigned to a non-model entity like a box or sphere, will
|
||||
// change the color of the entity when you hover over it. This script uses the JavaScript prototype/class functionality
|
||||
// to construct an object that has methods for hoverEnterEntity and hoverLeaveEntity;
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
ChangeColorOnHover = function(){
|
||||
this.oldColor = {};
|
||||
this.oldColorKnown = false;
|
||||
};
|
||||
|
||||
ChangeColorOnHover.prototype = {
|
||||
|
||||
storeOldColor: function(entityID) {
|
||||
var oldProperties = Entities.getEntityProperties(entityID);
|
||||
this.oldColor = oldProperties.color;
|
||||
this.oldColorKnown = true;
|
||||
print("storing old color... this.oldColor=" + this.oldColor.red + "," + this.oldColor.green + "," + this.oldColor.blue);
|
||||
},
|
||||
|
||||
preload: function(entityID) {
|
||||
print("preload");
|
||||
this.storeOldColor(entityID);
|
||||
},
|
||||
|
||||
hoverEnterEntity: function(entityID, mouseEvent) {
|
||||
print("hoverEnterEntity");
|
||||
if (!this.oldColorKnown) {
|
||||
this.storeOldColor(entityID);
|
||||
}
|
||||
Entities.editEntity(entityID, { color: { red: 0, green: 255, blue: 255} });
|
||||
},
|
||||
|
||||
|
||||
hoverLeaveEntity: function(entityID, mouseEvent) {
|
||||
print("hoverLeaveEntity");
|
||||
if (this.oldColorKnown) {
|
||||
print("leave restoring old color... this.oldColor="
|
||||
+ this.oldColor.red + "," + this.oldColor.green + "," + this.oldColor.blue);
|
||||
Entities.editEntity(entityID, { color: this.oldColor });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -27,10 +27,12 @@
|
|||
};
|
||||
|
||||
this.enterEntity = function(entityID) {
|
||||
print("enterEntity("+entityID.id+")");
|
||||
playSound();
|
||||
};
|
||||
|
||||
this.leaveEntity = function(entityID) {
|
||||
print("leaveEntity("+entityID.id+")");
|
||||
playSound();
|
||||
};
|
||||
})
|
||||
|
|
|
@ -274,19 +274,11 @@ modelUploader = (function () {
|
|||
}
|
||||
}
|
||||
|
||||
if (view.string(0, 18) === "Kaydara FBX Binary") {
|
||||
previousNodeFilename = "";
|
||||
|
||||
index = 27;
|
||||
while (index < view.byteLength - 39 && !EOF) {
|
||||
parseBinaryFBX();
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
readTextFBX();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function readModel() {
|
||||
|
|
|
@ -1,25 +1,32 @@
|
|||
//
|
||||
// notifications.js
|
||||
// notifications.js
|
||||
// Version 0.801
|
||||
// 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.
|
||||
// This script generates notifications created via a number of ways, such as:
|
||||
// keystroke:
|
||||
//
|
||||
// "q" returns number of users currently online (for debug purposes)
|
||||
|
||||
// CTRL/s for snapshot.
|
||||
// CTRL/m for mic mute and unmute.
|
||||
|
||||
// System generated notifications:
|
||||
// Displays users online at startup.
|
||||
// If Screen is resized.
|
||||
// Triggers notification if @MyUserName is mentioned in chat.
|
||||
// Announces existing user logging out.
|
||||
// Announces new user logging in.
|
||||
// If mic is muted for any reason.
|
||||
//
|
||||
// To add a new System notification type:
|
||||
//
|
||||
// 1. Set the Event Connector at the bottom of the script.
|
||||
|
@ -45,22 +52,23 @@
|
|||
// 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);
|
||||
// }
|
||||
// if (key.text == "q") { //queries number of users online
|
||||
// var numUsers = GlobalServices.onlineUsers.length;
|
||||
// var welcome = "There are " + numUsers + " users online now.";
|
||||
// createNotification(welcome);
|
||||
// }
|
||||
|
||||
|
||||
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 overlayLocationX = (windowDimensions.x - (width + 20.0));// positions window 20px 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 backColor = { red: 2, green: 2, blue: 2}; // background color was 38,38,38
|
||||
var backgroundAlpha = 0;
|
||||
var fontSize = 12.0;
|
||||
var persistTime = 10.0; // time in seconds before notification fades
|
||||
|
@ -115,7 +123,6 @@ function createNotification(text) {
|
|||
color: textColor,
|
||||
backgroundColor: backColor,
|
||||
alpha: backgroundAlpha,
|
||||
backgroundAlpha: backgroundAlpha,
|
||||
topMargin: topMargin,
|
||||
leftMargin: leftMargin,
|
||||
font: {size: fontSize},
|
||||
|
@ -125,8 +132,8 @@ function createNotification(text) {
|
|||
var buttonProperties = {
|
||||
x: buttonLocationX,
|
||||
y: bLevel,
|
||||
width: 15.0,
|
||||
height: 15.0,
|
||||
width: 10.0,
|
||||
height: 10.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},
|
||||
|
@ -161,7 +168,7 @@ function fadeIn(noticeIn, buttonIn) {
|
|||
pauseTimer = Script.setInterval(function() {
|
||||
q++;
|
||||
qFade = q / 10.0;
|
||||
Overlays.editOverlay(noticeIn, {alpha: qFade, backgroundAlpha: qFade});
|
||||
Overlays.editOverlay(noticeIn, {alpha: qFade});
|
||||
Overlays.editOverlay(buttonIn, {alpha: qFade});
|
||||
if (q >= 9.0) {
|
||||
Script.clearInterval(pauseTimer);
|
||||
|
@ -203,41 +210,18 @@ 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 == "q") { //queries number of users online
|
||||
var numUsers = GlobalServices.onlineUsers.length;
|
||||
var welcome = "There are " + numUsers + " users online now.";
|
||||
createNotification(welcome);
|
||||
}
|
||||
|
||||
if (key.text == "s") {
|
||||
if (ctrlIsPressed == true){
|
||||
var noteString = "You have taken a snapshot";
|
||||
var noteString = "Snapshot taken.";
|
||||
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
|
||||
|
@ -275,18 +259,14 @@ function checkSize(){
|
|||
|
||||
// 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");
|
||||
if (last_users.indexOf(users[user]) == -1.0) {
|
||||
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");
|
||||
createNotification(last_users[user] + " has left");
|
||||
}
|
||||
}
|
||||
last_users = users;
|
||||
|
@ -303,8 +283,8 @@ function onIncomingMessage(user, message) {
|
|||
}
|
||||
// Triggers mic mute notification
|
||||
function onMuteStateChanged() {
|
||||
var muteState = AudioDevice.getMuted() ? "Muted" : "Unmuted";
|
||||
var muteString = "Microphone is set to " + muteState;
|
||||
var muteState = AudioDevice.getMuted() ? "muted" : "unmuted";
|
||||
var muteString = "Microphone is now " + muteState;
|
||||
createNotification(muteString);
|
||||
}
|
||||
|
||||
|
@ -345,7 +325,7 @@ function fadeOut(noticeOut, buttonOut, arraysOut) {
|
|||
pauseTimer = Script.setInterval(function() {
|
||||
r--;
|
||||
rFade = r / 10.0;
|
||||
Overlays.editOverlay(noticeOut, {alpha: rFade, backgroundAlpha: rFade});
|
||||
Overlays.editOverlay(noticeOut, {alpha: rFade});
|
||||
Overlays.editOverlay(buttonOut, {alpha: rFade});
|
||||
if (r < 0) {
|
||||
dismiss(noticeOut, buttonOut, arraysOut);
|
||||
|
@ -368,10 +348,17 @@ function dismiss(firstNoteOut, firstButOut, firstOut) {
|
|||
myAlpha.splice(firstOut,1);
|
||||
}
|
||||
|
||||
onMuteStateChanged();
|
||||
// This is meant to show users online at startup but currently shows 0 users.
|
||||
function onConnected() {
|
||||
var numUsers = GlobalServices.onlineUsers.length;
|
||||
var welcome = "Welcome! There are " + numUsers + " users online now.";
|
||||
createNotification(welcome);
|
||||
}
|
||||
|
||||
AudioDevice.muteToggled.connect(onMuteStateChanged);
|
||||
Controller.keyPressEvent.connect(keyPressEvent);
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
GlobalServices.connected.connect(onConnected);
|
||||
GlobalServices.onlineUsersChanged.connect(onOnlineUsersChanged);
|
||||
GlobalServices.incomingMessage.connect(onIncomingMessage);
|
||||
Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
||||
|
|
|
@ -6,4 +6,4 @@ setup_hifi_project(Network)
|
|||
# link the shared hifi libraries
|
||||
link_hifi_libraries(networking shared)
|
||||
|
||||
link_shared_dependencies()
|
||||
include_dependency_includes()
|
|
@ -25,15 +25,15 @@ else ()
|
|||
endif ()
|
||||
|
||||
if (APPLE)
|
||||
set(GL_HEADERS "#include <GLUT/glut.h>\n#include <OpenGL/glext.h>")
|
||||
set(GL_HEADERS "#include <OpenGL/glext.h>")
|
||||
elseif (UNIX)
|
||||
# include the right GL headers for UNIX
|
||||
set(GL_HEADERS "#include <GL/gl.h>\n#include <GL/glut.h>\n#include <GL/glext.h>")
|
||||
set(GL_HEADERS "#include <GL/gl.h>\n#include <GL/glext.h>")
|
||||
elseif (WIN32)
|
||||
add_definitions(-D_USE_MATH_DEFINES) # apparently needed to get M_PI and other defines from cmath/math.h
|
||||
add_definitions(-DWINDOWS_LEAN_AND_MEAN) # needed to make sure windows doesn't go to crazy with its defines
|
||||
|
||||
set(GL_HEADERS "#include <windowshacks.h>\n#include <GL/glew.h>\n#include <GL/glut.h>\n#include <GL/wglew.h>")
|
||||
set(GL_HEADERS "#include <windowshacks.h>\n#include <GL/glew.h>\n#include <GL/wglew.h>")
|
||||
endif ()
|
||||
|
||||
# set up the external glm library
|
||||
|
@ -108,7 +108,7 @@ endif()
|
|||
add_executable(${TARGET_NAME} MACOSX_BUNDLE ${INTERFACE_SRCS} ${QM})
|
||||
|
||||
# link required hifi libraries
|
||||
link_hifi_libraries(shared octree voxels gpu fbx metavoxels networking entities avatars audio animation script-engine physics
|
||||
link_hifi_libraries(shared octree voxels gpu model fbx metavoxels networking entities avatars audio animation script-engine physics
|
||||
render-utils entities-renderer)
|
||||
|
||||
# find any optional and required libraries
|
||||
|
@ -195,11 +195,10 @@ if (APPLE)
|
|||
# link in required OS X frameworks and include the right GL headers
|
||||
find_library(CoreAudio CoreAudio)
|
||||
find_library(CoreFoundation CoreFoundation)
|
||||
find_library(GLUT GLUT)
|
||||
find_library(OpenGL OpenGL)
|
||||
find_library(AppKit AppKit)
|
||||
|
||||
target_link_libraries(${TARGET_NAME} ${CoreAudio} ${CoreFoundation} ${GLUT} ${OpenGL} ${AppKit})
|
||||
target_link_libraries(${TARGET_NAME} ${CoreAudio} ${CoreFoundation} ${OpenGL} ${AppKit})
|
||||
|
||||
# install command for OS X bundle
|
||||
INSTALL(TARGETS ${TARGET_NAME}
|
||||
|
@ -215,15 +214,12 @@ else (APPLE)
|
|||
)
|
||||
|
||||
find_package(OpenGL REQUIRED)
|
||||
find_package(GLUT REQUIRED)
|
||||
|
||||
include_directories(SYSTEM "${GLUT_INCLUDE_DIRS}")
|
||||
|
||||
if (${OPENGL_INCLUDE_DIR})
|
||||
include_directories(SYSTEM "${OPENGL_INCLUDE_DIR}")
|
||||
endif ()
|
||||
|
||||
target_link_libraries(${TARGET_NAME} "${OPENGL_LIBRARY}" "${GLUT_LIBRARIES}")
|
||||
target_link_libraries(${TARGET_NAME} "${OPENGL_LIBRARY}")
|
||||
|
||||
# link target to external libraries
|
||||
if (WIN32)
|
||||
|
@ -233,7 +229,7 @@ else (APPLE)
|
|||
# we're using static GLEW, so define GLEW_STATIC
|
||||
add_definitions(-DGLEW_STATIC)
|
||||
|
||||
target_link_libraries(${TARGET_NAME} "${GLEW_LIBRARIES}" "${NSIGHT_LIBRARIES}" wsock32.lib opengl32.lib)
|
||||
target_link_libraries(${TARGET_NAME} "${GLEW_LIBRARIES}" "${NSIGHT_LIBRARIES}" wsock32.lib opengl32.lib Winmm.lib)
|
||||
|
||||
# try to find the Nsight package and add it to the build if we find it
|
||||
find_package(NSIGHT)
|
||||
|
@ -247,4 +243,4 @@ else (APPLE)
|
|||
endif (APPLE)
|
||||
|
||||
# link any dependencies bubbled up from our linked dependencies
|
||||
link_shared_dependencies()
|
||||
include_dependency_includes()
|
||||
|
|
|
@ -131,7 +131,7 @@ static QTimer* idleTimer = NULL;
|
|||
const QString CHECK_VERSION_URL = "https://highfidelity.io/latestVersion.xml";
|
||||
const QString SKIP_FILENAME = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/hifi.skipversion";
|
||||
|
||||
const QString DEFAULT_SCRIPTS_JS_URL = "http://public.highfidelity.io/scripts/defaultScripts.js";
|
||||
const QString DEFAULT_SCRIPTS_JS_URL = "http://s3.amazonaws.com/hifi-public/scripts/defaultScripts.js";
|
||||
|
||||
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
|
||||
QString logMessage = LogHandler::getInstance().printMessage((LogMsgType) type, context, message);
|
||||
|
@ -313,13 +313,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
// use our MyAvatar position and quat for address manager path
|
||||
addressManager.setPositionGetter(getPositionForPath);
|
||||
addressManager.setOrientationGetter(getOrientationForPath);
|
||||
|
||||
// handle domain change signals from AddressManager
|
||||
connect(&addressManager, &AddressManager::possibleDomainChangeRequiredToHostname,
|
||||
this, &Application::changeDomainHostname);
|
||||
|
||||
connect(&addressManager, &AddressManager::possibleDomainChangeRequiredViaICEForID,
|
||||
&domainHandler, &DomainHandler::setIceServerHostnameAndID);
|
||||
|
||||
_settings = new QSettings(this);
|
||||
_numChangedSettings = 0;
|
||||
|
@ -1437,7 +1430,7 @@ void Application::dropEvent(QDropEvent *event) {
|
|||
SnapshotMetaData* snapshotData = Snapshot::parseSnapshotData(snapshotPath);
|
||||
if (snapshotData) {
|
||||
if (!snapshotData->getDomain().isEmpty()) {
|
||||
changeDomainHostname(snapshotData->getDomain());
|
||||
NodeList::getInstance()->getDomainHandler().setHostnameAndPort(snapshotData->getDomain());
|
||||
}
|
||||
|
||||
_myAvatar->setPosition(snapshotData->getLocation());
|
||||
|
@ -3650,16 +3643,6 @@ void Application::setMenuShortcutsEnabled(bool enabled) {
|
|||
setShortcutsEnabled(_window->menuBar(), enabled);
|
||||
}
|
||||
|
||||
void Application::uploadModel(ModelType modelType) {
|
||||
ModelUploader* uploader = new ModelUploader(modelType);
|
||||
QThread* thread = new QThread();
|
||||
thread->connect(uploader, SIGNAL(destroyed()), SLOT(quit()));
|
||||
thread->connect(thread, SIGNAL(finished()), SLOT(deleteLater()));
|
||||
uploader->connect(thread, SIGNAL(started()), SLOT(send()));
|
||||
|
||||
thread->start();
|
||||
}
|
||||
|
||||
void Application::updateWindowTitle(){
|
||||
|
||||
QString buildVersion = " (build " + applicationVersion() + ")";
|
||||
|
@ -3715,19 +3698,6 @@ void Application::updateLocationInServer() {
|
|||
}
|
||||
}
|
||||
|
||||
void Application::changeDomainHostname(const QString &newDomainHostname) {
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
||||
if (!nodeList->getDomainHandler().isCurrentHostname(newDomainHostname)) {
|
||||
// tell the MyAvatar object to send a kill packet so that it dissapears from its old avatar mixer immediately
|
||||
_myAvatar->sendKillAvatar();
|
||||
|
||||
// call the domain hostname change as a queued connection on the nodelist
|
||||
QMetaObject::invokeMethod(&NodeList::getInstance()->getDomainHandler(), "setHostname",
|
||||
Q_ARG(const QString&, newDomainHostname));
|
||||
}
|
||||
}
|
||||
|
||||
void Application::clearDomainOctreeDetails() {
|
||||
qDebug() << "Clearing domain octree details...";
|
||||
// reset the environment so that we don't erroneously end up with multiple
|
||||
|
@ -4228,15 +4198,19 @@ void Application::toggleRunningScriptsWidget() {
|
|||
}
|
||||
|
||||
void Application::uploadHead() {
|
||||
uploadModel(HEAD_MODEL);
|
||||
ModelUploader::uploadHead();
|
||||
}
|
||||
|
||||
void Application::uploadSkeleton() {
|
||||
uploadModel(SKELETON_MODEL);
|
||||
ModelUploader::uploadSkeleton();
|
||||
}
|
||||
|
||||
void Application::uploadAttachment() {
|
||||
uploadModel(ATTACHMENT_MODEL);
|
||||
ModelUploader::uploadAttachment();
|
||||
}
|
||||
|
||||
void Application::uploadEntity() {
|
||||
ModelUploader::uploadEntity();
|
||||
}
|
||||
|
||||
void Application::openUrl(const QUrl& url) {
|
||||
|
|
|
@ -336,7 +336,6 @@ signals:
|
|||
void importDone();
|
||||
|
||||
public slots:
|
||||
void changeDomainHostname(const QString& newDomainHostname);
|
||||
void domainChanged(const QString& domainHostname);
|
||||
void updateWindowTitle();
|
||||
void updateLocationInServer();
|
||||
|
@ -376,6 +375,7 @@ public slots:
|
|||
void uploadHead();
|
||||
void uploadSkeleton();
|
||||
void uploadAttachment();
|
||||
void uploadEntity();
|
||||
|
||||
void openUrl(const QUrl& url);
|
||||
|
||||
|
@ -462,8 +462,6 @@ private:
|
|||
|
||||
void setMenuShortcutsEnabled(bool enabled);
|
||||
|
||||
void uploadModel(ModelType modelType);
|
||||
|
||||
static void attachNewHeadToNode(Node *newNode);
|
||||
static void* networkReceive(void* args); // network receive thread
|
||||
|
||||
|
|
|
@ -10,11 +10,14 @@
|
|||
//
|
||||
// Creates single flexible verlet-integrated strands that can be used for hair/fur/grass
|
||||
|
||||
#include "Hair.h"
|
||||
#include <gpu/GPUConfig.h>
|
||||
|
||||
#include "Util.h"
|
||||
#include "world.h"
|
||||
|
||||
#include "Hair.h"
|
||||
|
||||
|
||||
const float HAIR_DAMPING = 0.99f;
|
||||
const float CONSTRAINT_RELAXATION = 10.0f;
|
||||
const float HAIR_ACCELERATION_COUPLING = 0.045f;
|
||||
|
|
|
@ -189,10 +189,14 @@ Menu::Menu() :
|
|||
SLOT(toggleAddressBar()));
|
||||
|
||||
addDisabledActionAndSeparator(fileMenu, "Upload Avatar Model");
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadHead, 0, Application::getInstance(), SLOT(uploadHead()));
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadSkeleton, 0, Application::getInstance(), SLOT(uploadSkeleton()));
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadHead, 0,
|
||||
Application::getInstance(), SLOT(uploadHead()));
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadSkeleton, 0,
|
||||
Application::getInstance(), SLOT(uploadSkeleton()));
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadAttachment, 0,
|
||||
Application::getInstance(), SLOT(uploadAttachment()));
|
||||
Application::getInstance(), SLOT(uploadAttachment()));
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadEntity, 0,
|
||||
Application::getInstance(), SLOT(uploadEntity()));
|
||||
addDisabledActionAndSeparator(fileMenu, "Settings");
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::SettingsImport, 0, this, SLOT(importSettings()));
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::SettingsExport, 0, this, SLOT(exportSettings()));
|
||||
|
|
|
@ -482,6 +482,7 @@ namespace MenuOption {
|
|||
const QString TransmitterDrive = "Transmitter Drive";
|
||||
const QString TurnWithHead = "Turn using Head";
|
||||
const QString UploadAttachment = "Upload Attachment Model";
|
||||
const QString UploadEntity = "Upload Entity Model";
|
||||
const QString UploadHead = "Upload Head Model";
|
||||
const QString UploadSkeleton = "Upload Skeleton Model";
|
||||
const QString UserInterface = "User Interface";
|
||||
|
|
|
@ -55,7 +55,8 @@ static const QString MODEL_URL = "/api/v1/models";
|
|||
|
||||
static const QString SETTING_NAME = "LastModelUploadLocation";
|
||||
|
||||
static const int MAX_SIZE = 10 * 1024 * 1024; // 10 MB
|
||||
static const int BYTES_PER_MEGABYTES = 1024 * 1024;
|
||||
static const unsigned long MAX_SIZE = 50 * 1024 * BYTES_PER_MEGABYTES; // 50 GB (Virtually remove limit)
|
||||
static const int MAX_TEXTURE_SIZE = 1024;
|
||||
static const int TIMEOUT = 1000;
|
||||
static const int MAX_CHECK = 30;
|
||||
|
@ -63,6 +64,32 @@ static const int MAX_CHECK = 30;
|
|||
static const int QCOMPRESS_HEADER_POSITION = 0;
|
||||
static const int QCOMPRESS_HEADER_SIZE = 4;
|
||||
|
||||
void ModelUploader::uploadModel(ModelType modelType) {
|
||||
ModelUploader* uploader = new ModelUploader(modelType);
|
||||
QThread* thread = new QThread();
|
||||
thread->connect(uploader, SIGNAL(destroyed()), SLOT(quit()));
|
||||
thread->connect(thread, SIGNAL(finished()), SLOT(deleteLater()));
|
||||
uploader->connect(thread, SIGNAL(started()), SLOT(send()));
|
||||
|
||||
thread->start();
|
||||
}
|
||||
|
||||
void ModelUploader::uploadHead() {
|
||||
uploadModel(HEAD_MODEL);
|
||||
}
|
||||
|
||||
void ModelUploader::uploadSkeleton() {
|
||||
uploadModel(SKELETON_MODEL);
|
||||
}
|
||||
|
||||
void ModelUploader::uploadAttachment() {
|
||||
uploadModel(ATTACHMENT_MODEL);
|
||||
}
|
||||
|
||||
void ModelUploader::uploadEntity() {
|
||||
uploadModel(ENTITY_MODEL);
|
||||
}
|
||||
|
||||
ModelUploader::ModelUploader(ModelType modelType) :
|
||||
_lodCount(-1),
|
||||
_texturesCount(-1),
|
||||
|
@ -148,6 +175,91 @@ bool ModelUploader::zip() {
|
|||
FBXGeometry geometry = readFBX(fbxContents, QVariantHash());
|
||||
|
||||
// make sure we have some basic mappings
|
||||
populateBasicMapping(mapping, filename, geometry);
|
||||
|
||||
// open the dialog to configure the rest
|
||||
ModelPropertiesDialog properties(_modelType, mapping, basePath, geometry);
|
||||
if (properties.exec() == QDialog::Rejected) {
|
||||
return false;
|
||||
}
|
||||
mapping = properties.getMapping();
|
||||
|
||||
QByteArray nameField = mapping.value(NAME_FIELD).toByteArray();
|
||||
QString urlBase;
|
||||
if (!nameField.isEmpty()) {
|
||||
QHttpPart textPart;
|
||||
textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"model_name\"");
|
||||
textPart.setBody(nameField);
|
||||
_dataMultiPart->append(textPart);
|
||||
urlBase = S3_URL + "/models/" + MODEL_TYPE_NAMES[_modelType] + "/" + nameField;
|
||||
_url = urlBase + ".fst";
|
||||
|
||||
} else {
|
||||
QMessageBox::warning(NULL,
|
||||
QString("ModelUploader::zip()"),
|
||||
QString("Model name is missing in the .fst file."),
|
||||
QMessageBox::Ok);
|
||||
qDebug() << "[Warning] " << QString("Model name is missing in the .fst file.");
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray texdirField = mapping.value(TEXDIR_FIELD).toByteArray();
|
||||
QString texDir;
|
||||
_textureBase = urlBase + "/textures/";
|
||||
if (!texdirField.isEmpty()) {
|
||||
texDir = basePath + "/" + texdirField;
|
||||
QFileInfo texInfo(texDir);
|
||||
if (!texInfo.exists() || !texInfo.isDir()) {
|
||||
QMessageBox::warning(NULL,
|
||||
QString("ModelUploader::zip()"),
|
||||
QString("Texture directory could not be found."),
|
||||
QMessageBox::Ok);
|
||||
qDebug() << "[Warning] " << QString("Texture directory could not be found.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
QVariantHash lodField = mapping.value(LOD_FIELD).toHash();
|
||||
for (QVariantHash::const_iterator it = lodField.constBegin(); it != lodField.constEnd(); it++) {
|
||||
QFileInfo lod(basePath + "/" + it.key());
|
||||
if (!lod.exists() || !lod.isFile()) { // Check existence
|
||||
QMessageBox::warning(NULL,
|
||||
QString("ModelUploader::zip()"),
|
||||
QString("LOD file %1 could not be found.").arg(lod.fileName()),
|
||||
QMessageBox::Ok);
|
||||
qDebug() << "[Warning] " << QString("FBX file %1 could not be found.").arg(lod.fileName());
|
||||
}
|
||||
// Compress and copy
|
||||
if (!addPart(lod.filePath(), QString("lod%1").arg(++_lodCount))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Write out, compress and copy the fst
|
||||
if (!addPart(*fst, writeMapping(mapping), QString("fst"))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compress and copy the fbx
|
||||
if (!addPart(fbx, fbxContents, "fbx")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!addTextures(texDir, geometry)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QHttpPart textPart;
|
||||
textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;"
|
||||
" name=\"model_category\"");
|
||||
textPart.setBody(MODEL_TYPE_NAMES[_modelType]);
|
||||
_dataMultiPart->append(textPart);
|
||||
|
||||
_readyToSend = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ModelUploader::populateBasicMapping(QVariantHash& mapping, QString filename, FBXGeometry geometry) {
|
||||
if (!mapping.contains(NAME_FIELD)) {
|
||||
mapping.insert(NAME_FIELD, QFileInfo(filename).baseName());
|
||||
}
|
||||
|
@ -162,11 +274,11 @@ bool ModelUploader::zip() {
|
|||
QVariantHash joints = mapping.value(JOINT_FIELD).toHash();
|
||||
if (!joints.contains("jointEyeLeft")) {
|
||||
joints.insert("jointEyeLeft", geometry.jointIndices.contains("jointEyeLeft") ? "jointEyeLeft" :
|
||||
(geometry.jointIndices.contains("EyeLeft") ? "EyeLeft" : "LeftEye"));
|
||||
(geometry.jointIndices.contains("EyeLeft") ? "EyeLeft" : "LeftEye"));
|
||||
}
|
||||
if (!joints.contains("jointEyeRight")) {
|
||||
joints.insert("jointEyeRight", geometry.jointIndices.contains("jointEyeRight") ? "jointEyeRight" :
|
||||
geometry.jointIndices.contains("EyeRight") ? "EyeRight" : "RightEye");
|
||||
geometry.jointIndices.contains("EyeRight") ? "EyeRight" : "RightEye");
|
||||
}
|
||||
if (!joints.contains("jointNeck")) {
|
||||
joints.insert("jointNeck", geometry.jointIndices.contains("jointNeck") ? "jointNeck" : "Neck");
|
||||
|
@ -250,87 +362,6 @@ bool ModelUploader::zip() {
|
|||
blendshapes.insertMulti("Sneer", QVariantList() << "Squint_Right" << 0.5);
|
||||
mapping.insert(BLENDSHAPE_FIELD, blendshapes);
|
||||
}
|
||||
|
||||
// open the dialog to configure the rest
|
||||
ModelPropertiesDialog properties(_modelType, mapping, basePath, geometry);
|
||||
if (properties.exec() == QDialog::Rejected) {
|
||||
return false;
|
||||
}
|
||||
mapping = properties.getMapping();
|
||||
|
||||
QByteArray nameField = mapping.value(NAME_FIELD).toByteArray();
|
||||
QString urlBase;
|
||||
if (!nameField.isEmpty()) {
|
||||
QHttpPart textPart;
|
||||
textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"model_name\"");
|
||||
textPart.setBody(nameField);
|
||||
_dataMultiPart->append(textPart);
|
||||
urlBase = S3_URL + "/models/" + MODEL_TYPE_NAMES[_modelType] + "/" + nameField;
|
||||
_url = urlBase + ".fst";
|
||||
|
||||
} else {
|
||||
QMessageBox::warning(NULL,
|
||||
QString("ModelUploader::zip()"),
|
||||
QString("Model name is missing in the .fst file."),
|
||||
QMessageBox::Ok);
|
||||
qDebug() << "[Warning] " << QString("Model name is missing in the .fst file.");
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray texdirField = mapping.value(TEXDIR_FIELD).toByteArray();
|
||||
QString texDir;
|
||||
_textureBase = urlBase + "/textures/";
|
||||
if (!texdirField.isEmpty()) {
|
||||
texDir = basePath + "/" + texdirField;
|
||||
QFileInfo texInfo(texDir);
|
||||
if (!texInfo.exists() || !texInfo.isDir()) {
|
||||
QMessageBox::warning(NULL,
|
||||
QString("ModelUploader::zip()"),
|
||||
QString("Texture directory could not be found."),
|
||||
QMessageBox::Ok);
|
||||
qDebug() << "[Warning] " << QString("Texture directory could not be found.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
QVariantHash lodField = mapping.value(LOD_FIELD).toHash();
|
||||
for (QVariantHash::const_iterator it = lodField.constBegin(); it != lodField.constEnd(); it++) {
|
||||
QFileInfo lod(basePath + "/" + it.key());
|
||||
if (!lod.exists() || !lod.isFile()) { // Check existence
|
||||
QMessageBox::warning(NULL,
|
||||
QString("ModelUploader::zip()"),
|
||||
QString("LOD file %1 could not be found.").arg(lod.fileName()),
|
||||
QMessageBox::Ok);
|
||||
qDebug() << "[Warning] " << QString("FBX file %1 could not be found.").arg(lod.fileName());
|
||||
}
|
||||
// Compress and copy
|
||||
if (!addPart(lod.filePath(), QString("lod%1").arg(++_lodCount))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Write out, compress and copy the fst
|
||||
if (!addPart(*fst, writeMapping(mapping), QString("fst"))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compress and copy the fbx
|
||||
if (!addPart(fbx, fbxContents, "fbx")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!addTextures(texDir, geometry)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QHttpPart textPart;
|
||||
textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;"
|
||||
" name=\"model_category\"");
|
||||
textPart.setBody(MODEL_TYPE_NAMES[_modelType]);
|
||||
_dataMultiPart->append(textPart);
|
||||
|
||||
_readyToSend = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ModelUploader::send() {
|
||||
|
@ -590,9 +621,9 @@ bool ModelUploader::addPart(const QFile& file, const QByteArray& contents, const
|
|||
if (_totalSize > MAX_SIZE) {
|
||||
QMessageBox::warning(NULL,
|
||||
QString("ModelUploader::zip()"),
|
||||
QString("Model too big, over %1 Bytes.").arg(MAX_SIZE),
|
||||
QString("Model too big, over %1 MB.").arg(MAX_SIZE / BYTES_PER_MEGABYTES),
|
||||
QMessageBox::Ok);
|
||||
qDebug() << "[Warning] " << QString("Model too big, over %1 Bytes.").arg(MAX_SIZE);
|
||||
qDebug() << "[Warning] " << QString("Model too big, over %1 MB.").arg(MAX_SIZE / BYTES_PER_MEGABYTES);
|
||||
return false;
|
||||
}
|
||||
qDebug() << "Current model size: " << _totalSize;
|
||||
|
@ -613,8 +644,8 @@ ModelPropertiesDialog::ModelPropertiesDialog(ModelType modelType, const QVariant
|
|||
_modelType(modelType),
|
||||
_originalMapping(originalMapping),
|
||||
_basePath(basePath),
|
||||
_geometry(geometry) {
|
||||
|
||||
_geometry(geometry)
|
||||
{
|
||||
setWindowTitle("Set Model Properties");
|
||||
|
||||
QFormLayout* form = new QFormLayout();
|
||||
|
@ -629,33 +660,35 @@ ModelPropertiesDialog::ModelPropertiesDialog(ModelType modelType, const QVariant
|
|||
_scale->setMaximum(FLT_MAX);
|
||||
_scale->setSingleStep(0.01);
|
||||
|
||||
if (_modelType == ATTACHMENT_MODEL) {
|
||||
QHBoxLayout* translation = new QHBoxLayout();
|
||||
form->addRow("Translation:", translation);
|
||||
translation->addWidget(_translationX = createTranslationBox());
|
||||
translation->addWidget(_translationY = createTranslationBox());
|
||||
translation->addWidget(_translationZ = createTranslationBox());
|
||||
form->addRow("Pivot About Center:", _pivotAboutCenter = new QCheckBox());
|
||||
form->addRow("Pivot Joint:", _pivotJoint = createJointBox());
|
||||
connect(_pivotAboutCenter, SIGNAL(toggled(bool)), SLOT(updatePivotJoint()));
|
||||
_pivotAboutCenter->setChecked(true);
|
||||
|
||||
} else {
|
||||
form->addRow("Left Eye Joint:", _leftEyeJoint = createJointBox());
|
||||
form->addRow("Right Eye Joint:", _rightEyeJoint = createJointBox());
|
||||
form->addRow("Neck Joint:", _neckJoint = createJointBox());
|
||||
}
|
||||
if (_modelType == SKELETON_MODEL) {
|
||||
form->addRow("Root Joint:", _rootJoint = createJointBox());
|
||||
form->addRow("Lean Joint:", _leanJoint = createJointBox());
|
||||
form->addRow("Head Joint:", _headJoint = createJointBox());
|
||||
form->addRow("Left Hand Joint:", _leftHandJoint = createJointBox());
|
||||
form->addRow("Right Hand Joint:", _rightHandJoint = createJointBox());
|
||||
|
||||
form->addRow("Free Joints:", _freeJoints = new QVBoxLayout());
|
||||
QPushButton* newFreeJoint = new QPushButton("New Free Joint");
|
||||
_freeJoints->addWidget(newFreeJoint);
|
||||
connect(newFreeJoint, SIGNAL(clicked(bool)), SLOT(createNewFreeJoint()));
|
||||
if (_modelType != ENTITY_MODEL) {
|
||||
if (_modelType == ATTACHMENT_MODEL) {
|
||||
QHBoxLayout* translation = new QHBoxLayout();
|
||||
form->addRow("Translation:", translation);
|
||||
translation->addWidget(_translationX = createTranslationBox());
|
||||
translation->addWidget(_translationY = createTranslationBox());
|
||||
translation->addWidget(_translationZ = createTranslationBox());
|
||||
form->addRow("Pivot About Center:", _pivotAboutCenter = new QCheckBox());
|
||||
form->addRow("Pivot Joint:", _pivotJoint = createJointBox());
|
||||
connect(_pivotAboutCenter, SIGNAL(toggled(bool)), SLOT(updatePivotJoint()));
|
||||
_pivotAboutCenter->setChecked(true);
|
||||
|
||||
} else {
|
||||
form->addRow("Left Eye Joint:", _leftEyeJoint = createJointBox());
|
||||
form->addRow("Right Eye Joint:", _rightEyeJoint = createJointBox());
|
||||
form->addRow("Neck Joint:", _neckJoint = createJointBox());
|
||||
}
|
||||
if (_modelType == SKELETON_MODEL) {
|
||||
form->addRow("Root Joint:", _rootJoint = createJointBox());
|
||||
form->addRow("Lean Joint:", _leanJoint = createJointBox());
|
||||
form->addRow("Head Joint:", _headJoint = createJointBox());
|
||||
form->addRow("Left Hand Joint:", _leftHandJoint = createJointBox());
|
||||
form->addRow("Right Hand Joint:", _rightHandJoint = createJointBox());
|
||||
|
||||
form->addRow("Free Joints:", _freeJoints = new QVBoxLayout());
|
||||
QPushButton* newFreeJoint = new QPushButton("New Free Joint");
|
||||
_freeJoints->addWidget(newFreeJoint);
|
||||
connect(newFreeJoint, SIGNAL(clicked(bool)), SLOT(createNewFreeJoint()));
|
||||
}
|
||||
}
|
||||
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok |
|
||||
|
@ -683,38 +716,40 @@ QVariantHash ModelPropertiesDialog::getMapping() const {
|
|||
}
|
||||
mapping.insert(JOINT_INDEX_FIELD, jointIndices);
|
||||
|
||||
QVariantHash joints = mapping.value(JOINT_FIELD).toHash();
|
||||
if (_modelType == ATTACHMENT_MODEL) {
|
||||
glm::vec3 pivot;
|
||||
if (_pivotAboutCenter->isChecked()) {
|
||||
pivot = (_geometry.meshExtents.minimum + _geometry.meshExtents.maximum) * 0.5f;
|
||||
|
||||
} else if (_pivotJoint->currentIndex() != 0) {
|
||||
pivot = extractTranslation(_geometry.joints.at(_pivotJoint->currentIndex() - 1).transform);
|
||||
if (_modelType != ENTITY_MODEL) {
|
||||
QVariantHash joints = mapping.value(JOINT_FIELD).toHash();
|
||||
if (_modelType == ATTACHMENT_MODEL) {
|
||||
glm::vec3 pivot;
|
||||
if (_pivotAboutCenter->isChecked()) {
|
||||
pivot = (_geometry.meshExtents.minimum + _geometry.meshExtents.maximum) * 0.5f;
|
||||
|
||||
} else if (_pivotJoint->currentIndex() != 0) {
|
||||
pivot = extractTranslation(_geometry.joints.at(_pivotJoint->currentIndex() - 1).transform);
|
||||
}
|
||||
mapping.insert(TRANSLATION_X_FIELD, -pivot.x * _scale->value() + _translationX->value());
|
||||
mapping.insert(TRANSLATION_Y_FIELD, -pivot.y * _scale->value() + _translationY->value());
|
||||
mapping.insert(TRANSLATION_Z_FIELD, -pivot.z * _scale->value() + _translationZ->value());
|
||||
|
||||
} else {
|
||||
insertJointMapping(joints, "jointEyeLeft", _leftEyeJoint->currentText());
|
||||
insertJointMapping(joints, "jointEyeRight", _rightEyeJoint->currentText());
|
||||
insertJointMapping(joints, "jointNeck", _neckJoint->currentText());
|
||||
}
|
||||
mapping.insert(TRANSLATION_X_FIELD, -pivot.x * _scale->value() + _translationX->value());
|
||||
mapping.insert(TRANSLATION_Y_FIELD, -pivot.y * _scale->value() + _translationY->value());
|
||||
mapping.insert(TRANSLATION_Z_FIELD, -pivot.z * _scale->value() + _translationZ->value());
|
||||
|
||||
} else {
|
||||
insertJointMapping(joints, "jointEyeLeft", _leftEyeJoint->currentText());
|
||||
insertJointMapping(joints, "jointEyeRight", _rightEyeJoint->currentText());
|
||||
insertJointMapping(joints, "jointNeck", _neckJoint->currentText());
|
||||
}
|
||||
if (_modelType == SKELETON_MODEL) {
|
||||
insertJointMapping(joints, "jointRoot", _rootJoint->currentText());
|
||||
insertJointMapping(joints, "jointLean", _leanJoint->currentText());
|
||||
insertJointMapping(joints, "jointHead", _headJoint->currentText());
|
||||
insertJointMapping(joints, "jointLeftHand", _leftHandJoint->currentText());
|
||||
insertJointMapping(joints, "jointRightHand", _rightHandJoint->currentText());
|
||||
|
||||
mapping.remove(FREE_JOINT_FIELD);
|
||||
for (int i = 0; i < _freeJoints->count() - 1; i++) {
|
||||
QComboBox* box = static_cast<QComboBox*>(_freeJoints->itemAt(i)->widget()->layout()->itemAt(0)->widget());
|
||||
mapping.insertMulti(FREE_JOINT_FIELD, box->currentText());
|
||||
if (_modelType == SKELETON_MODEL) {
|
||||
insertJointMapping(joints, "jointRoot", _rootJoint->currentText());
|
||||
insertJointMapping(joints, "jointLean", _leanJoint->currentText());
|
||||
insertJointMapping(joints, "jointHead", _headJoint->currentText());
|
||||
insertJointMapping(joints, "jointLeftHand", _leftHandJoint->currentText());
|
||||
insertJointMapping(joints, "jointRightHand", _rightHandJoint->currentText());
|
||||
|
||||
mapping.remove(FREE_JOINT_FIELD);
|
||||
for (int i = 0; i < _freeJoints->count() - 1; i++) {
|
||||
QComboBox* box = static_cast<QComboBox*>(_freeJoints->itemAt(i)->widget()->layout()->itemAt(0)->widget());
|
||||
mapping.insertMulti(FREE_JOINT_FIELD, box->currentText());
|
||||
}
|
||||
}
|
||||
mapping.insert(JOINT_FIELD, joints);
|
||||
}
|
||||
mapping.insert(JOINT_FIELD, joints);
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
@ -729,32 +764,35 @@ void ModelPropertiesDialog::reset() {
|
|||
_scale->setValue(_originalMapping.value(SCALE_FIELD).toDouble());
|
||||
|
||||
QVariantHash jointHash = _originalMapping.value(JOINT_FIELD).toHash();
|
||||
if (_modelType == ATTACHMENT_MODEL) {
|
||||
_translationX->setValue(_originalMapping.value(TRANSLATION_X_FIELD).toDouble());
|
||||
_translationY->setValue(_originalMapping.value(TRANSLATION_Y_FIELD).toDouble());
|
||||
_translationZ->setValue(_originalMapping.value(TRANSLATION_Z_FIELD).toDouble());
|
||||
_pivotAboutCenter->setChecked(true);
|
||||
_pivotJoint->setCurrentIndex(0);
|
||||
|
||||
} else {
|
||||
setJointText(_leftEyeJoint, jointHash.value("jointEyeLeft").toString());
|
||||
setJointText(_rightEyeJoint, jointHash.value("jointEyeRight").toString());
|
||||
setJointText(_neckJoint, jointHash.value("jointNeck").toString());
|
||||
}
|
||||
if (_modelType == SKELETON_MODEL) {
|
||||
setJointText(_rootJoint, jointHash.value("jointRoot").toString());
|
||||
setJointText(_leanJoint, jointHash.value("jointLean").toString());
|
||||
setJointText(_headJoint, jointHash.value("jointHead").toString());
|
||||
setJointText(_leftHandJoint, jointHash.value("jointLeftHand").toString());
|
||||
setJointText(_rightHandJoint, jointHash.value("jointRightHand").toString());
|
||||
|
||||
while (_freeJoints->count() > 1) {
|
||||
delete _freeJoints->itemAt(0)->widget();
|
||||
|
||||
if (_modelType != ENTITY_MODEL) {
|
||||
if (_modelType == ATTACHMENT_MODEL) {
|
||||
_translationX->setValue(_originalMapping.value(TRANSLATION_X_FIELD).toDouble());
|
||||
_translationY->setValue(_originalMapping.value(TRANSLATION_Y_FIELD).toDouble());
|
||||
_translationZ->setValue(_originalMapping.value(TRANSLATION_Z_FIELD).toDouble());
|
||||
_pivotAboutCenter->setChecked(true);
|
||||
_pivotJoint->setCurrentIndex(0);
|
||||
|
||||
} else {
|
||||
setJointText(_leftEyeJoint, jointHash.value("jointEyeLeft").toString());
|
||||
setJointText(_rightEyeJoint, jointHash.value("jointEyeRight").toString());
|
||||
setJointText(_neckJoint, jointHash.value("jointNeck").toString());
|
||||
}
|
||||
foreach (const QVariant& joint, _originalMapping.values(FREE_JOINT_FIELD)) {
|
||||
QString jointName = joint.toString();
|
||||
if (_geometry.jointIndices.contains(jointName)) {
|
||||
createNewFreeJoint(jointName);
|
||||
if (_modelType == SKELETON_MODEL) {
|
||||
setJointText(_rootJoint, jointHash.value("jointRoot").toString());
|
||||
setJointText(_leanJoint, jointHash.value("jointLean").toString());
|
||||
setJointText(_headJoint, jointHash.value("jointHead").toString());
|
||||
setJointText(_leftHandJoint, jointHash.value("jointLeftHand").toString());
|
||||
setJointText(_rightHandJoint, jointHash.value("jointRightHand").toString());
|
||||
|
||||
while (_freeJoints->count() > 1) {
|
||||
delete _freeJoints->itemAt(0)->widget();
|
||||
}
|
||||
foreach (const QVariant& joint, _originalMapping.values(FREE_JOINT_FIELD)) {
|
||||
QString jointName = joint.toString();
|
||||
if (_geometry.jointIndices.contains(jointName)) {
|
||||
createNewFreeJoint(jointName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,13 +33,15 @@ class ModelUploader : public QObject {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ModelUploader(ModelType type);
|
||||
~ModelUploader();
|
||||
static void uploadModel(ModelType modelType);
|
||||
|
||||
public slots:
|
||||
void send();
|
||||
static void uploadHead();
|
||||
static void uploadSkeleton();
|
||||
static void uploadAttachment();
|
||||
static void uploadEntity();
|
||||
|
||||
private slots:
|
||||
void send();
|
||||
void checkJSON(QNetworkReply& requestReply);
|
||||
void uploadUpdate(qint64 bytesSent, qint64 bytesTotal);
|
||||
void uploadSuccess(QNetworkReply& requestReply);
|
||||
|
@ -48,12 +50,21 @@ private slots:
|
|||
void processCheck();
|
||||
|
||||
private:
|
||||
ModelUploader(ModelType type);
|
||||
~ModelUploader();
|
||||
|
||||
void populateBasicMapping(QVariantHash& mapping, QString filename, FBXGeometry geometry);
|
||||
bool zip();
|
||||
bool addTextures(const QString& texdir, const FBXGeometry& geometry);
|
||||
bool addPart(const QString& path, const QString& name, bool isTexture = false);
|
||||
bool addPart(const QFile& file, const QByteArray& contents, const QString& name, bool isTexture = false);
|
||||
|
||||
QString _url;
|
||||
QString _textureBase;
|
||||
QSet<QByteArray> _textureFilenames;
|
||||
int _lodCount;
|
||||
int _texturesCount;
|
||||
int _totalSize;
|
||||
unsigned long _totalSize;
|
||||
ModelType _modelType;
|
||||
bool _readyToSend;
|
||||
|
||||
|
@ -64,12 +75,6 @@ private:
|
|||
|
||||
QDialog* _progressDialog;
|
||||
QProgressBar* _progressBar;
|
||||
|
||||
|
||||
bool zip();
|
||||
bool addTextures(const QString& texdir, const FBXGeometry& geometry);
|
||||
bool addPart(const QString& path, const QString& name, bool isTexture = false);
|
||||
bool addPart(const QFile& file, const QByteArray& contents, const QString& name, bool isTexture = false);
|
||||
};
|
||||
|
||||
/// A dialog that allows customization of various model properties.
|
||||
|
|
|
@ -669,6 +669,49 @@ glm::vec3 Avatar::getDisplayNamePosition() {
|
|||
return namePosition;
|
||||
}
|
||||
|
||||
float Avatar::calculateDisplayNameScaleFactor(const glm::vec3& textPosition, bool inHMD) {
|
||||
|
||||
// We need to compute the scale factor such as the text remains with fixed size respect to window coordinates
|
||||
// We project a unit vector and check the difference in screen coordinates, to check which is the
|
||||
// correction scale needed
|
||||
// save the matrices for later scale correction factor
|
||||
// The up vector must be relative to the rotation current rotation matrix:
|
||||
// we set the identity
|
||||
glm::vec3 testPoint0 = textPosition;
|
||||
glm::vec3 testPoint1 = textPosition + (Application::getInstance()->getCamera()->getRotation() * IDENTITY_UP);
|
||||
|
||||
double textWindowHeight;
|
||||
|
||||
GLCanvas::SharedPointer glCanvas = DependencyManager::get<GLCanvas>();
|
||||
float windowSizeX = glCanvas->getDeviceWidth();
|
||||
float windowSizeY = glCanvas->getDeviceHeight();
|
||||
|
||||
glm::dmat4 modelViewMatrix;
|
||||
glm::dmat4 projectionMatrix;
|
||||
Application::getInstance()->getModelViewMatrix(&modelViewMatrix);
|
||||
Application::getInstance()->getProjectionMatrix(&projectionMatrix);
|
||||
|
||||
glm::dvec4 p0 = modelViewMatrix * glm::dvec4(testPoint0, 1.0);
|
||||
p0 = projectionMatrix * p0;
|
||||
glm::dvec2 result0 = glm::vec2(windowSizeX * (p0.x / p0.w + 1.0f) * 0.5f, windowSizeY * (p0.y / p0.w + 1.0f) * 0.5f);
|
||||
|
||||
glm::dvec4 p1 = modelViewMatrix * glm::dvec4(testPoint1, 1.0);
|
||||
p1 = projectionMatrix * p1;
|
||||
glm::vec2 result1 = glm::vec2(windowSizeX * (p1.x / p1.w + 1.0f) * 0.5f, windowSizeY * (p1.y / p1.w + 1.0f) * 0.5f);
|
||||
textWindowHeight = abs(result1.y - result0.y);
|
||||
|
||||
// need to scale to compensate for the font resolution due to the device
|
||||
float scaleFactor = QApplication::desktop()->windowHandle()->devicePixelRatio() *
|
||||
((textWindowHeight > EPSILON) ? 1.0f / textWindowHeight : 1.0f);
|
||||
if (inHMD) {
|
||||
const float HMDMODE_NAME_SCALE = 0.65f;
|
||||
scaleFactor *= HMDMODE_NAME_SCALE;
|
||||
} else {
|
||||
scaleFactor *= Application::getInstance()->getRenderResolutionScale();
|
||||
}
|
||||
return scaleFactor;
|
||||
}
|
||||
|
||||
void Avatar::renderDisplayName() {
|
||||
|
||||
if (_displayName.isEmpty() || _displayNameAlpha == 0.0f) {
|
||||
|
@ -700,78 +743,39 @@ void Avatar::renderDisplayName() {
|
|||
frontAxis = glm::normalize(glm::vec3(frontAxis.z, 0.0f, -frontAxis.x));
|
||||
float angle = acos(frontAxis.x) * ((frontAxis.z < 0) ? 1.0f : -1.0f);
|
||||
glRotatef(glm::degrees(angle), 0.0f, 1.0f, 0.0f);
|
||||
|
||||
// We need to compute the scale factor such as the text remains with fixed size respect to window coordinates
|
||||
// We project a unit vector and check the difference in screen coordinates, to check which is the
|
||||
// correction scale needed
|
||||
// save the matrices for later scale correction factor
|
||||
glm::dmat4 modelViewMatrix;
|
||||
glm::dmat4 projectionMatrix;
|
||||
GLint viewportMatrix[4];
|
||||
Application::getInstance()->getModelViewMatrix(&modelViewMatrix);
|
||||
Application::getInstance()->getProjectionMatrix(&projectionMatrix);
|
||||
glGetIntegerv(GL_VIEWPORT, viewportMatrix);
|
||||
GLdouble result0[3], result1[3];
|
||||
|
||||
// The up vector must be relative to the rotation current rotation matrix:
|
||||
// we set the identity
|
||||
glm::dvec3 testPoint0 = glm::dvec3(textPosition);
|
||||
glm::dvec3 testPoint1 = glm::dvec3(textPosition) + glm::dvec3(Application::getInstance()->getCamera()->getRotation() * IDENTITY_UP);
|
||||
|
||||
bool success;
|
||||
success = gluProject(testPoint0.x, testPoint0.y, testPoint0.z,
|
||||
(GLdouble*)&modelViewMatrix, (GLdouble*)&projectionMatrix, viewportMatrix,
|
||||
&result0[0], &result0[1], &result0[2]);
|
||||
success = success &&
|
||||
gluProject(testPoint1.x, testPoint1.y, testPoint1.z,
|
||||
(GLdouble*)&modelViewMatrix, (GLdouble*)&projectionMatrix, viewportMatrix,
|
||||
&result1[0], &result1[1], &result1[2]);
|
||||
float scaleFactor = calculateDisplayNameScaleFactor(textPosition, inHMD);
|
||||
glScalef(scaleFactor, scaleFactor, 1.0);
|
||||
|
||||
glScalef(1.0f, -1.0f, 1.0f); // TextRenderer::draw paints the text upside down in y axis
|
||||
|
||||
if (success) {
|
||||
double textWindowHeight = abs(result1[1] - result0[1]);
|
||||
// need to scale to compensate for the font resolution due to the device
|
||||
float scaleFactor = QApplication::desktop()->windowHandle()->devicePixelRatio() *
|
||||
((textWindowHeight > EPSILON) ? 1.0f / textWindowHeight : 1.0f);
|
||||
if (inHMD) {
|
||||
const float HMDMODE_NAME_SCALE = 0.65f;
|
||||
scaleFactor *= HMDMODE_NAME_SCALE;
|
||||
} else {
|
||||
scaleFactor *= Application::getInstance()->getRenderResolutionScale();
|
||||
}
|
||||
glScalef(scaleFactor, scaleFactor, 1.0);
|
||||
|
||||
glScalef(1.0f, -1.0f, 1.0f); // TextRenderer::draw paints the text upside down in y axis
|
||||
int text_x = -_displayNameBoundingRect.width() / 2;
|
||||
int text_y = -_displayNameBoundingRect.height() / 2;
|
||||
|
||||
int text_x = -_displayNameBoundingRect.width() / 2;
|
||||
int text_y = -_displayNameBoundingRect.height() / 2;
|
||||
// draw a gray background
|
||||
int left = text_x + _displayNameBoundingRect.x();
|
||||
int right = left + _displayNameBoundingRect.width();
|
||||
int bottom = text_y + _displayNameBoundingRect.y();
|
||||
int top = bottom + _displayNameBoundingRect.height();
|
||||
const int border = 8;
|
||||
bottom -= border;
|
||||
left -= border;
|
||||
top += border;
|
||||
right += border;
|
||||
|
||||
// draw a gray background
|
||||
int left = text_x + _displayNameBoundingRect.x();
|
||||
int right = left + _displayNameBoundingRect.width();
|
||||
int bottom = text_y + _displayNameBoundingRect.y();
|
||||
int top = bottom + _displayNameBoundingRect.height();
|
||||
const int border = 8;
|
||||
bottom -= border;
|
||||
left -= border;
|
||||
top += border;
|
||||
right += border;
|
||||
// We are drawing coplanar textures with depth: need the polygon offset
|
||||
glEnable(GL_POLYGON_OFFSET_FILL);
|
||||
glPolygonOffset(1.0f, 1.0f);
|
||||
|
||||
// We are drawing coplanar textures with depth: need the polygon offset
|
||||
glEnable(GL_POLYGON_OFFSET_FILL);
|
||||
glPolygonOffset(1.0f, 1.0f);
|
||||
|
||||
glColor4f(0.2f, 0.2f, 0.2f, _displayNameAlpha * DISPLAYNAME_BACKGROUND_ALPHA / DISPLAYNAME_ALPHA);
|
||||
renderBevelCornersRect(left, bottom, right - left, top - bottom, 3);
|
||||
|
||||
glColor4f(0.93f, 0.93f, 0.93f, _displayNameAlpha);
|
||||
QByteArray ba = _displayName.toLocal8Bit();
|
||||
const char* text = ba.data();
|
||||
|
||||
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||
textRenderer(DISPLAYNAME)->draw(text_x, text_y, text);
|
||||
|
||||
|
||||
}
|
||||
glColor4f(0.2f, 0.2f, 0.2f, _displayNameAlpha * DISPLAYNAME_BACKGROUND_ALPHA / DISPLAYNAME_ALPHA);
|
||||
renderBevelCornersRect(left, bottom, right - left, top - bottom, 3);
|
||||
|
||||
glColor4f(0.93f, 0.93f, 0.93f, _displayNameAlpha);
|
||||
QByteArray ba = _displayName.toLocal8Bit();
|
||||
const char* text = ba.data();
|
||||
|
||||
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||
textRenderer(DISPLAYNAME)->draw(text_x, text_y, text);
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
|
|
|
@ -230,6 +230,7 @@ protected:
|
|||
float getPelvisFloatingHeight() const;
|
||||
glm::vec3 getDisplayNamePosition();
|
||||
|
||||
float calculateDisplayNameScaleFactor(const glm::vec3& textPosition, bool inHMD);
|
||||
void renderDisplayName();
|
||||
virtual void renderBody(RenderMode renderMode, bool postLighting, float glowLevel = 0.0f);
|
||||
virtual bool shouldRenderHead(const glm::vec3& cameraPosition, RenderMode renderMode) const;
|
||||
|
|
|
@ -582,7 +582,7 @@ void OculusManager::renderDistortionMesh(ovrPosef eyeRenderPose[ovrEye_Count]) {
|
|||
|
||||
glLoadIdentity();
|
||||
GLCanvas::SharedPointer glCanvas = DependencyManager::get<GLCanvas>();
|
||||
gluOrtho2D(0, glCanvas->getDeviceWidth(), 0, glCanvas->getDeviceHeight());
|
||||
glOrtho(0, glCanvas->getDeviceWidth(), 0, glCanvas->getDeviceHeight(), -1.0, 1.0);
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
||||
|
|
|
@ -180,7 +180,7 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) {
|
|||
|
||||
glPushMatrix(); {
|
||||
glLoadIdentity();
|
||||
gluOrtho2D(0, glCanvas->width(), glCanvas->height(), 0);
|
||||
glOrtho(0, glCanvas->width(), glCanvas->height(), 0, -1.0, 1.0);
|
||||
|
||||
renderAudioMeter();
|
||||
|
||||
|
@ -224,7 +224,7 @@ void ApplicationOverlay::displayOverlayTexture() {
|
|||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix(); {
|
||||
glLoadIdentity();
|
||||
gluOrtho2D(0, glCanvas->getDeviceWidth(), glCanvas->getDeviceHeight(), 0);
|
||||
glOrtho(0, glCanvas->getDeviceWidth(), glCanvas->getDeviceHeight(), 0, -1.0, 1.0);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_LIGHTING);
|
||||
glEnable(GL_BLEND);
|
||||
|
|
|
@ -12,16 +12,19 @@
|
|||
#include <QDialog>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QGridLayout>
|
||||
#include <QFileInfo>
|
||||
#include <QHeaderView>
|
||||
#include <QLineEdit>
|
||||
#include <QMessageBox>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <QThread>
|
||||
#include <QUrl>
|
||||
#include <qurlquery.h>
|
||||
#include <QUrlQuery>
|
||||
#include <QXmlStreamReader>
|
||||
|
||||
#include <NetworkAccessManager.h>
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
#include "ModelsBrowser.h"
|
||||
|
||||
const char* MODEL_TYPE_NAMES[] = { "entities", "heads", "skeletons", "attachments" };
|
||||
|
|
|
@ -128,7 +128,7 @@ void RearMirrorTools::displayIcon(QRect bounds, QRect iconBounds, GLuint texture
|
|||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
|
||||
gluOrtho2D(bounds.left(), bounds.right(), bounds.bottom(), bounds.top());
|
||||
glOrtho(bounds.left(), bounds.right(), bounds.bottom(), bounds.top(), -1.0, 1.0);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_LIGHTING);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
|
|
|
@ -100,26 +100,31 @@ void ImageOverlay::render(RenderArgs* args) {
|
|||
float w = fromImage.width() / imageWidth; // ?? is this what we want? not sure
|
||||
float h = fromImage.height() / imageHeight;
|
||||
|
||||
int left = _bounds.left();
|
||||
int right = _bounds.right() + 1;
|
||||
int top = _bounds.top();
|
||||
int bottom = _bounds.bottom() + 1;
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
if (_renderImage) {
|
||||
glTexCoord2f(x, 1.0f - y);
|
||||
}
|
||||
glVertex2f(_bounds.left(), _bounds.top());
|
||||
glVertex2f(left, top);
|
||||
|
||||
if (_renderImage) {
|
||||
glTexCoord2f(x + w, 1.0f - y);
|
||||
}
|
||||
glVertex2f(_bounds.right(), _bounds.top());
|
||||
glVertex2f(right, top);
|
||||
|
||||
if (_renderImage) {
|
||||
glTexCoord2f(x + w, 1.0f - (y + h));
|
||||
}
|
||||
glVertex2f(_bounds.right(), _bounds.bottom());
|
||||
glVertex2f(right, bottom);
|
||||
|
||||
if (_renderImage) {
|
||||
glTexCoord2f(x, 1.0f - (y + h));
|
||||
}
|
||||
glVertex2f(_bounds.left(), _bounds.bottom());
|
||||
glVertex2f(left, bottom);
|
||||
glEnd();
|
||||
|
||||
if (_renderImage) {
|
||||
|
|
|
@ -70,11 +70,16 @@ void TextOverlay::render(RenderArgs* args) {
|
|||
glColor4f(backgroundColor.red / MAX_COLOR, backgroundColor.green / MAX_COLOR, backgroundColor.blue / MAX_COLOR,
|
||||
getBackgroundAlpha());
|
||||
|
||||
int left = _bounds.left();
|
||||
int right = _bounds.right() + 1;
|
||||
int top = _bounds.top();
|
||||
int bottom = _bounds.bottom() + 1;
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
glVertex2f(_bounds.left(), _bounds.top());
|
||||
glVertex2f(_bounds.right(), _bounds.top());
|
||||
glVertex2f(_bounds.right(), _bounds.bottom());
|
||||
glVertex2f(_bounds.left(), _bounds.bottom());
|
||||
glVertex2f(left, top);
|
||||
glVertex2f(right, top);
|
||||
glVertex2f(right, bottom);
|
||||
glVertex2f(left, bottom);
|
||||
glEnd();
|
||||
|
||||
// Same font properties as textSize()
|
||||
|
|
|
@ -3,7 +3,7 @@ set(TARGET_NAME animation)
|
|||
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
||||
setup_hifi_library(Network Script)
|
||||
|
||||
link_hifi_libraries(shared fbx)
|
||||
link_hifi_libraries(shared gpu model fbx)
|
||||
|
||||
# call macro to link our dependencies and bubble them up via a property on our target
|
||||
link_shared_dependencies()
|
||||
# call macro to include our dependency includes and bubble them up via a property on our target
|
||||
include_dependency_includes()
|
|
@ -7,5 +7,5 @@ include_glm()
|
|||
|
||||
link_hifi_libraries(networking shared)
|
||||
|
||||
# call macro to link our dependencies and bubble them up via a property on our target
|
||||
link_shared_dependencies()
|
||||
# call macro to include our dependency includes and bubble them up via a property on our target
|
||||
include_dependency_includes()
|
|
@ -5,8 +5,7 @@ setup_hifi_library(Network Script)
|
|||
|
||||
include_glm()
|
||||
|
||||
link_hifi_libraries(shared octree voxels networking)
|
||||
include_hifi_library_headers(fbx)
|
||||
link_hifi_libraries(audio shared octree voxels networking gpu model fbx)
|
||||
|
||||
# call macro to link our dependencies and bubble them up via a property on our target
|
||||
link_shared_dependencies()
|
||||
# call macro to include our dependency includes and bubble them up via a property on our target
|
||||
include_dependency_includes()
|
||||
|
|
|
@ -443,7 +443,14 @@ int AvatarData::parseDataAtOffset(const QByteArray& packet, int offset) {
|
|||
|
||||
// key state, stored as a semi-nibble in the bitItems
|
||||
_keyState = (KeyState)getSemiNibbleAt(bitItems,KEY_STATE_START_BIT);
|
||||
|
||||
// hand state, stored as a semi-nibble plus a bit in the bitItems
|
||||
// we store the hand state as well as other items in a shared bitset. The hand state is an octal, but is split
|
||||
// into two sections to maintain backward compatibility. The bits are ordered as such (0-7 left to right).
|
||||
// +---+-----+-----+--+
|
||||
// |x,x|H0,H1|x,x,x|H2|
|
||||
// +---+-----+-----+--+
|
||||
// Hand state - H0,H1,H2 is found in the 3rd, 4th, and 8th bits
|
||||
_handState = getSemiNibbleAt(bitItems, HAND_STATE_START_BIT)
|
||||
+ (oneAtBit(bitItems, HAND_STATE_FINGER_POINTING_BIT) ? IS_FINGER_POINTING_FLAG : 0);
|
||||
|
||||
|
|
|
@ -76,7 +76,17 @@ const quint32 AVATAR_MOTION_SCRIPTABLE_BITS =
|
|||
AVATAR_MOTION_STAND_ON_NEARBY_FLOORS;
|
||||
|
||||
|
||||
// First bitset
|
||||
// Bitset of state flags - we store the key state, hand state, faceshift, chat circling, and existance of
|
||||
// referential data in this bit set. The hand state is an octal, but is split into two sections to maintain
|
||||
// backward compatibility. The bits are ordered as such (0-7 left to right).
|
||||
// +-----+-----+-+-+-+--+
|
||||
// |K0,K1|H0,H1|F|C|R|H2|
|
||||
// +-----+-----+-+-+-+--+
|
||||
// Key state - K0,K1 is found in the 1st and 2nd bits
|
||||
// Hand state - H0,H1,H2 is found in the 3rd, 4th, and 8th bits
|
||||
// Faceshift - F is found in the 5th bit
|
||||
// Chat Circling - C is found in the 6th bit
|
||||
// Referential Data - R is found in the 7th bit
|
||||
const int KEY_STATE_START_BIT = 0; // 1st and 2nd bits
|
||||
const int HAND_STATE_START_BIT = 2; // 3rd and 4th bits
|
||||
const int IS_FACESHIFT_CONNECTED = 4; // 5th bit
|
||||
|
|
|
@ -3,5 +3,5 @@ set(TARGET_NAME embedded-webserver)
|
|||
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
||||
setup_hifi_library(Network)
|
||||
|
||||
# call macro to link our dependencies and bubble them up via a property on our target
|
||||
link_shared_dependencies()
|
||||
# call macro to include our dependency includes and bubble them up via a property on our target
|
||||
include_dependency_includes()
|
|
@ -5,7 +5,7 @@ setup_hifi_library(Widgets OpenGL Network Script)
|
|||
|
||||
include_glm()
|
||||
|
||||
link_hifi_libraries(shared gpu script-engine)
|
||||
link_hifi_libraries(shared gpu script-engine render-utils)
|
||||
|
||||
# call macro to link our dependencies and bubble them up via a property on our target
|
||||
link_shared_dependencies()
|
||||
# call macro to include our dependency includes and bubble them up via a property on our target
|
||||
include_dependency_includes()
|
||||
|
|
|
@ -62,6 +62,7 @@ EntityTreeRenderer::~EntityTreeRenderer() {
|
|||
}
|
||||
|
||||
void EntityTreeRenderer::clear() {
|
||||
leaveAllEntities();
|
||||
foreach (const EntityItemID& entityID, _entityScripts.keys()) {
|
||||
checkAndCallUnload(entityID);
|
||||
}
|
||||
|
@ -82,8 +83,7 @@ void EntityTreeRenderer::init() {
|
|||
|
||||
// make sure our "last avatar position" is something other than our current position, so that on our
|
||||
// first chance, we'll check for enter/leave entity events.
|
||||
glm::vec3 avatarPosition = _viewState->getAvatarPosition();
|
||||
_lastAvatarPosition = avatarPosition + glm::vec3(1.0f, 1.0f, 1.0f);
|
||||
_lastAvatarPosition = _viewState->getAvatarPosition() + glm::vec3(1.0f, 1.0f, 1.0f);
|
||||
|
||||
connect(entityTree, &EntityTree::deletingEntity, this, &EntityTreeRenderer::deletingEntity);
|
||||
connect(entityTree, &EntityTree::addingEntity, this, &EntityTreeRenderer::checkAndCallPreload);
|
||||
|
@ -97,13 +97,15 @@ QScriptValue EntityTreeRenderer::loadEntityScript(const EntityItemID& entityItem
|
|||
}
|
||||
|
||||
|
||||
QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorText) {
|
||||
QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorText, bool& isURL) {
|
||||
QUrl url(scriptMaybeURLorText);
|
||||
|
||||
// If the url is not valid, this must be script text...
|
||||
if (!url.isValid()) {
|
||||
isURL = false;
|
||||
return scriptMaybeURLorText;
|
||||
}
|
||||
isURL = true;
|
||||
|
||||
QString scriptContents; // assume empty
|
||||
|
||||
|
@ -173,7 +175,8 @@ QScriptValue EntityTreeRenderer::loadEntityScript(EntityItem* entity) {
|
|||
return QScriptValue(); // no script
|
||||
}
|
||||
|
||||
QString scriptContents = loadScriptContents(entityScript);
|
||||
bool isURL = false; // loadScriptContents() will tell us if this is a URL or just text.
|
||||
QString scriptContents = loadScriptContents(entityScript, isURL);
|
||||
|
||||
QScriptSyntaxCheckResult syntaxCheck = QScriptEngine::checkSyntax(scriptContents);
|
||||
if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) {
|
||||
|
@ -184,6 +187,9 @@ QScriptValue EntityTreeRenderer::loadEntityScript(EntityItem* entity) {
|
|||
return QScriptValue(); // invalid script
|
||||
}
|
||||
|
||||
if (isURL) {
|
||||
_entitiesScriptEngine->setParentURL(entity->getScript());
|
||||
}
|
||||
QScriptValue entityScriptConstructor = _entitiesScriptEngine->evaluate(scriptContents);
|
||||
|
||||
if (!entityScriptConstructor.isFunction()) {
|
||||
|
@ -197,6 +203,10 @@ QScriptValue EntityTreeRenderer::loadEntityScript(EntityItem* entity) {
|
|||
EntityScriptDetails newDetails = { entityScript, entityScriptObject };
|
||||
_entityScripts[entityID] = newDetails;
|
||||
|
||||
if (isURL) {
|
||||
_entitiesScriptEngine->setParentURL("");
|
||||
}
|
||||
|
||||
return entityScriptObject; // newly constructed
|
||||
}
|
||||
|
||||
|
@ -287,6 +297,27 @@ void EntityTreeRenderer::checkEnterLeaveEntities() {
|
|||
}
|
||||
}
|
||||
|
||||
void EntityTreeRenderer::leaveAllEntities() {
|
||||
if (_tree) {
|
||||
_tree->lockForWrite(); // so that our scripts can do edits if they want
|
||||
|
||||
// for all of our previous containing entities, if they are no longer containing then send them a leave event
|
||||
foreach(const EntityItemID& entityID, _currentEntitiesInside) {
|
||||
emit leaveEntity(entityID);
|
||||
QScriptValueList entityArgs = createEntityArgs(entityID);
|
||||
QScriptValue entityScript = loadEntityScript(entityID);
|
||||
if (entityScript.property("leaveEntity").isValid()) {
|
||||
entityScript.property("leaveEntity").call(entityScript, entityArgs);
|
||||
}
|
||||
}
|
||||
_currentEntitiesInside.clear();
|
||||
|
||||
// make sure our "last avatar position" is something other than our current position, so that on our
|
||||
// first chance, we'll check for enter/leave entity events.
|
||||
_lastAvatarPosition = _viewState->getAvatarPosition() + glm::vec3(1.0f, 1.0f, 1.0f);
|
||||
_tree->unlock();
|
||||
}
|
||||
}
|
||||
void EntityTreeRenderer::render(RenderArgs::RenderMode renderMode, RenderArgs::RenderSide renderSide) {
|
||||
if (_tree) {
|
||||
Model::startScene(renderSide);
|
||||
|
|
|
@ -129,6 +129,7 @@ private:
|
|||
|
||||
QScriptValueList createEntityArgs(const EntityItemID& entityID);
|
||||
void checkEnterLeaveEntities();
|
||||
void leaveAllEntities();
|
||||
glm::vec3 _lastAvatarPosition;
|
||||
QVector<EntityItemID> _currentEntitiesInside;
|
||||
|
||||
|
@ -138,7 +139,7 @@ private:
|
|||
QScriptValue loadEntityScript(EntityItem* entity);
|
||||
QScriptValue loadEntityScript(const EntityItemID& entityItemID);
|
||||
QScriptValue getPreviouslyLoadedEntityScript(const EntityItemID& entityItemID);
|
||||
QString loadScriptContents(const QString& scriptMaybeURLorText);
|
||||
QString loadScriptContents(const QString& scriptMaybeURLorText, bool& isURL);
|
||||
QScriptValueList createMouseEventArgs(const EntityItemID& entityID, QMouseEvent* event, unsigned int deviceID);
|
||||
QScriptValueList createMouseEventArgs(const EntityItemID& entityID, const MouseEvent& mouseEvent);
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ setup_hifi_library(Network Script)
|
|||
|
||||
include_glm()
|
||||
|
||||
link_hifi_libraries(shared octree fbx networking animation)
|
||||
link_hifi_libraries(avatars shared octree gpu model fbx networking animation)
|
||||
|
||||
# call macro to link our dependencies and bubble them up via a property on our target
|
||||
link_shared_dependencies()
|
||||
# call macro to include our dependency includes and bubble them up via a property on our target
|
||||
include_dependency_includes()
|
||||
|
|
|
@ -144,6 +144,13 @@ void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) {
|
|||
penetration = collision->_penetration;
|
||||
entityB = static_cast<EntityItem*>(collision->_extraData);
|
||||
|
||||
// The collision _extraData should be a valid entity, but if for some reason
|
||||
// it's NULL then continue with a warning.
|
||||
if (!entityB) {
|
||||
qDebug() << "UNEXPECTED - we have a collision with missing _extraData. Something went wrong down below!";
|
||||
continue; // skip this loop pass if the entity is NULL
|
||||
}
|
||||
|
||||
// don't collide entities with unknown IDs,
|
||||
if (!entityB->isKnownID()) {
|
||||
continue; // skip this loop pass if the entity has an unknown ID
|
||||
|
|
|
@ -585,8 +585,12 @@ bool EntityTreeElement::findShapeCollisions(const Shape* shape, CollisionList& c
|
|||
if (shape != otherCollisionShape && !ignoreForCollisions) {
|
||||
if (ShapeCollider::collideShapes(shape, otherCollisionShape, collisions)) {
|
||||
CollisionInfo* lastCollision = collisions.getLastCollision();
|
||||
lastCollision->_extraData = entity;
|
||||
atLeastOneCollision = true;
|
||||
if (lastCollision) {
|
||||
lastCollision->_extraData = entity;
|
||||
atLeastOneCollision = true;
|
||||
} else {
|
||||
qDebug() << "UNEXPECTED - ShapeCollider::collideShapes() returned true, but no lastCollision.";
|
||||
}
|
||||
}
|
||||
}
|
||||
++entityItr;
|
||||
|
|
|
@ -5,11 +5,11 @@ setup_hifi_library()
|
|||
|
||||
include_glm()
|
||||
|
||||
link_hifi_libraries(shared networking octree voxels)
|
||||
link_hifi_libraries(shared gpu model networking octree voxels)
|
||||
|
||||
find_package(ZLIB REQUIRED)
|
||||
include_directories(SYSTEM "${ZLIB_INCLUDE_DIRS}")
|
||||
target_link_libraries(${TARGET_NAME} ${ZLIB_LIBRARIES})
|
||||
|
||||
# call macro to link our dependencies and bubble them up via a property on our target
|
||||
link_shared_dependencies()
|
||||
# call macro to include our dependency includes and bubble them up via a property on our target
|
||||
include_dependency_includes()
|
||||
|
|
|
@ -24,8 +24,10 @@
|
|||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
class QIODevice;
|
||||
#include <model/Geometry.h>
|
||||
#include <model/Material.h>
|
||||
|
||||
class QIODevice;
|
||||
class FBXNode;
|
||||
|
||||
typedef QList<FBXNode> FBXNodeList;
|
||||
|
@ -131,6 +133,7 @@ public:
|
|||
FBXTexture emissiveTexture;
|
||||
|
||||
QString materialID;
|
||||
model::MaterialPointer _material;
|
||||
};
|
||||
|
||||
/// A single mesh (with optional blendshapes) extracted from an FBX document.
|
||||
|
@ -159,6 +162,8 @@ public:
|
|||
|
||||
bool hasSpecularTexture() const;
|
||||
bool hasEmissiveTexture() const;
|
||||
|
||||
model::Mesh _mesh;
|
||||
};
|
||||
|
||||
/// A single animation frame extracted from an FBX document.
|
||||
|
|
|
@ -19,7 +19,7 @@ elseif (WIN32)
|
|||
# we're using static GLEW, so define GLEW_STATIC
|
||||
add_definitions(-DGLEW_STATIC)
|
||||
|
||||
target_link_libraries(${TARGET_NAME} "${GLEW_LIBRARIES}" opengl32.lib)
|
||||
target_link_libraries(${TARGET_NAME} ${GLEW_LIBRARIES} opengl32.lib)
|
||||
|
||||
# need to bubble up the GLEW_INCLUDE_DIRS
|
||||
list(APPEND ${TARGET_NAME}_DEPENDENCY_INCLUDES "${GLEW_INCLUDE_DIRS}")
|
||||
|
@ -44,5 +44,5 @@ else ()
|
|||
list(APPEND ${TARGET_NAME}_DEPENDENCY_INCLUDES "${OPENGL_INCLUDE_DIR}")
|
||||
endif (APPLE)
|
||||
|
||||
# call macro to link our dependencies and bubble them up via a property on our target
|
||||
link_shared_dependencies()
|
||||
# call macro to include our dependency includes and bubble them up via a property on our target
|
||||
include_dependency_includes()
|
||||
|
|
|
@ -107,6 +107,19 @@ void Batch::setInputFormat(const Stream::FormatPointer& format) {
|
|||
_params.push_back(_streamFormats.cache(format));
|
||||
}
|
||||
|
||||
void Batch::setInputBuffer(Slot channel, const BufferPointer& buffer, Offset offset, Offset stride) {
|
||||
ADD_COMMAND(setInputBuffer);
|
||||
|
||||
_params.push_back(stride);
|
||||
_params.push_back(offset);
|
||||
_params.push_back(_buffers.cache(buffer));
|
||||
_params.push_back(channel);
|
||||
}
|
||||
|
||||
void Batch::setInputBuffer(Slot channel, const BufferView& view) {
|
||||
setInputBuffer(channel, view._buffer, view._offset, Offset(view._stride));
|
||||
}
|
||||
|
||||
void Batch::setInputStream(Slot startChannel, const BufferStream& stream) {
|
||||
if (stream.getNumBuffers()) {
|
||||
const Buffers& buffers = stream.getBuffers();
|
||||
|
@ -118,15 +131,6 @@ void Batch::setInputStream(Slot startChannel, const BufferStream& stream) {
|
|||
}
|
||||
}
|
||||
|
||||
void Batch::setInputBuffer(Slot channel, const BufferPointer& buffer, Offset offset, Offset stride) {
|
||||
ADD_COMMAND(setInputBuffer);
|
||||
|
||||
_params.push_back(stride);
|
||||
_params.push_back(offset);
|
||||
_params.push_back(_buffers.cache(buffer));
|
||||
_params.push_back(channel);
|
||||
}
|
||||
|
||||
void Batch::setIndexBuffer(Type type, const BufferPointer& buffer, Offset offset) {
|
||||
ADD_COMMAND(setIndexBuffer);
|
||||
|
||||
|
@ -153,3 +157,17 @@ void Batch::setProjectionTransform(const Transform& proj) {
|
|||
_params.push_back(_transforms.cache(proj));
|
||||
}
|
||||
|
||||
void Batch::setUniformBuffer(uint32 slot, const BufferPointer& buffer, Offset offset, Offset size) {
|
||||
ADD_COMMAND(setUniformBuffer);
|
||||
|
||||
_params.push_back(size);
|
||||
_params.push_back(offset);
|
||||
_params.push_back(_buffers.cache(buffer));
|
||||
_params.push_back(slot);
|
||||
}
|
||||
|
||||
void Batch::setUniformBuffer(uint32 slot, const BufferView& view) {
|
||||
setUniformBuffer(slot, view._buffer, view._offset, view._size);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -72,8 +72,9 @@ public:
|
|||
// IndexBuffer
|
||||
void setInputFormat(const Stream::FormatPointer& format);
|
||||
|
||||
void setInputStream(Slot startChannel, const BufferStream& stream); // not a command, just unroll into a loop of setInputBuffer
|
||||
void setInputBuffer(Slot channel, const BufferPointer& buffer, Offset offset, Offset stride);
|
||||
void setInputBuffer(Slot channel, const BufferView& buffer); // not a command, just a shortcut from a BufferView
|
||||
void setInputStream(Slot startChannel, const BufferStream& stream); // not a command, just unroll into a loop of setInputBuffer
|
||||
|
||||
void setIndexBuffer(Type type, const BufferPointer& buffer, Offset offset);
|
||||
|
||||
|
@ -87,6 +88,9 @@ public:
|
|||
void setViewTransform(const Transform& view);
|
||||
void setProjectionTransform(const Transform& proj);
|
||||
|
||||
// Shader Stage
|
||||
void setUniformBuffer(uint32 slot, const BufferPointer& buffer, Offset offset, Offset size);
|
||||
void setUniformBuffer(uint32 slot, const BufferView& view); // not a command, just a shortcut from a BufferView
|
||||
|
||||
// TODO: As long as we have gl calls explicitely issued from interface
|
||||
// code, we need to be able to record and batch these calls. THe long
|
||||
|
@ -117,6 +121,7 @@ public:
|
|||
void _glUseProgram(GLuint program);
|
||||
void _glUniform1f(GLint location, GLfloat v0);
|
||||
void _glUniform2f(GLint location, GLfloat v0, GLfloat v1);
|
||||
void _glUniform4fv(GLint location, GLsizei count, const GLfloat* value);
|
||||
void _glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
|
||||
|
||||
void _glMatrixMode(GLenum mode);
|
||||
|
@ -161,6 +166,8 @@ public:
|
|||
COMMAND_setViewTransform,
|
||||
COMMAND_setProjectionTransform,
|
||||
|
||||
COMMAND_setUniformBuffer,
|
||||
|
||||
// TODO: As long as we have gl calls explicitely issued from interface
|
||||
// code, we need to be able to record and batch these calls. THe long
|
||||
// term strategy is to get rid of any GL calls in favor of the HIFI GPU API
|
||||
|
@ -187,6 +194,7 @@ public:
|
|||
COMMAND_glUseProgram,
|
||||
COMMAND_glUniform1f,
|
||||
COMMAND_glUniform2f,
|
||||
COMMAND_glUniform4fv,
|
||||
COMMAND_glUniformMatrix4fv,
|
||||
|
||||
COMMAND_glMatrixMode,
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#define hifi_gpu_Context_h
|
||||
|
||||
#include <assert.h>
|
||||
#include "GPUConfig.h"
|
||||
|
||||
#include "Resource.h"
|
||||
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
#define hifi_gpu_Format_h
|
||||
|
||||
#include <assert.h>
|
||||
#include "GPUConfig.h"
|
||||
|
||||
|
||||
namespace gpu {
|
||||
|
||||
|
@ -94,7 +92,8 @@ static const int DIMENSION_COUNT[NUM_DIMENSIONS] = {
|
|||
// Semantic of an Element
|
||||
// Provide information on how to use the element
|
||||
enum Semantic {
|
||||
RGB = 0,
|
||||
RAW = 0, // used as RAW memory
|
||||
RGB,
|
||||
RGBA,
|
||||
XYZ,
|
||||
XYZW,
|
||||
|
@ -104,6 +103,8 @@ enum Semantic {
|
|||
DIR_XYZ,
|
||||
UV,
|
||||
R8,
|
||||
INDEX, //used by index buffer of a mesh
|
||||
PART, // used by part buffer of a mesh
|
||||
|
||||
NUM_SEMANTICS,
|
||||
};
|
||||
|
@ -119,7 +120,7 @@ public:
|
|||
_type(type)
|
||||
{}
|
||||
Element() :
|
||||
_semantic(R8),
|
||||
_semantic(RAW),
|
||||
_dimension(SCALAR),
|
||||
_type(INT8)
|
||||
{}
|
||||
|
|
|
@ -31,6 +31,8 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] =
|
|||
(&::gpu::GLBackend::do_setViewTransform),
|
||||
(&::gpu::GLBackend::do_setProjectionTransform),
|
||||
|
||||
(&::gpu::GLBackend::do_setUniformBuffer),
|
||||
|
||||
(&::gpu::GLBackend::do_glEnable),
|
||||
(&::gpu::GLBackend::do_glDisable),
|
||||
|
||||
|
@ -54,6 +56,7 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] =
|
|||
(&::gpu::GLBackend::do_glUseProgram),
|
||||
(&::gpu::GLBackend::do_glUniform1f),
|
||||
(&::gpu::GLBackend::do_glUniform2f),
|
||||
(&::gpu::GLBackend::do_glUniform4fv),
|
||||
(&::gpu::GLBackend::do_glUniformMatrix4fv),
|
||||
|
||||
(&::gpu::GLBackend::do_glMatrixMode),
|
||||
|
@ -483,6 +486,25 @@ void GLBackend::updateTransform() {
|
|||
}
|
||||
}
|
||||
|
||||
void GLBackend::do_setUniformBuffer(Batch& batch, uint32 paramOffset) {
|
||||
GLuint slot = batch._params[paramOffset + 3]._uint;
|
||||
BufferPointer uniformBuffer = batch._buffers.get(batch._params[paramOffset + 2]._uint);
|
||||
GLintptr rangeStart = batch._params[paramOffset + 1]._uint;
|
||||
GLsizeiptr rangeSize = batch._params[paramOffset + 0]._uint;
|
||||
#if defined(Q_OS_MAC)
|
||||
GLfloat* data = (GLfloat*) (uniformBuffer->getData() + rangeStart);
|
||||
glUniform4fv(slot, rangeSize / sizeof(GLfloat[4]), data);
|
||||
#else
|
||||
GLuint bo = getBufferID(*uniformBuffer);
|
||||
glBindBufferRange(GL_UNIFORM_BUFFER, slot, bo, rangeStart, rangeSize);
|
||||
|
||||
// glUniformBufferEXT(_shader._program, slot, bo);
|
||||
|
||||
//glBindBufferBase(GL_UNIFORM_BUFFER, slot, bo);
|
||||
#endif
|
||||
CHECK_GL_ERROR();
|
||||
}
|
||||
|
||||
// TODO: As long as we have gl calls explicitely issued from interface
|
||||
// code, we need to be able to record and batch these calls. THe long
|
||||
// term strategy is to get rid of any GL calls in favor of the HIFI GPU API
|
||||
|
@ -672,7 +694,10 @@ void Batch::_glUseProgram(GLuint program) {
|
|||
DO_IT_NOW(_glUseProgram, 1);
|
||||
}
|
||||
void GLBackend::do_glUseProgram(Batch& batch, uint32 paramOffset) {
|
||||
glUseProgram(batch._params[paramOffset]._uint);
|
||||
|
||||
_shader._program = batch._params[paramOffset]._uint;
|
||||
glUseProgram(_shader._program);
|
||||
|
||||
CHECK_GL_ERROR();
|
||||
}
|
||||
|
||||
|
@ -708,6 +733,25 @@ void GLBackend::do_glUniform2f(Batch& batch, uint32 paramOffset) {
|
|||
CHECK_GL_ERROR();
|
||||
}
|
||||
|
||||
void Batch::_glUniform4fv(GLint location, GLsizei count, const GLfloat* value) {
|
||||
ADD_COMMAND_GL(glUniform4fv);
|
||||
|
||||
const int VEC4_SIZE = 4 * sizeof(float);
|
||||
_params.push_back(cacheData(count * VEC4_SIZE, value));
|
||||
_params.push_back(count);
|
||||
_params.push_back(location);
|
||||
|
||||
DO_IT_NOW(_glUniform4fv, 3);
|
||||
}
|
||||
void GLBackend::do_glUniform4fv(Batch& batch, uint32 paramOffset) {
|
||||
glUniform4fv(
|
||||
batch._params[paramOffset + 2]._int,
|
||||
batch._params[paramOffset + 1]._uint,
|
||||
(const GLfloat*)batch.editData(batch._params[paramOffset + 0]._uint));
|
||||
|
||||
CHECK_GL_ERROR();
|
||||
}
|
||||
|
||||
void Batch::_glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) {
|
||||
ADD_COMMAND_GL(glUniformMatrix4fv);
|
||||
|
||||
|
|
|
@ -122,6 +122,19 @@ protected:
|
|||
_lastMode(GL_TEXTURE) {}
|
||||
} _transform;
|
||||
|
||||
// Shader Stage
|
||||
void do_setUniformBuffer(Batch& batch, uint32 paramOffset);
|
||||
|
||||
void updateShader();
|
||||
struct ShaderStageState {
|
||||
|
||||
GLuint _program;
|
||||
|
||||
ShaderStageState() :
|
||||
_program(0) {}
|
||||
} _shader;
|
||||
|
||||
|
||||
// TODO: As long as we have gl calls explicitely issued from interface
|
||||
// code, we need to be able to record and batch these calls. THe long
|
||||
// term strategy is to get rid of any GL calls in favor of the HIFI GPU API
|
||||
|
@ -148,6 +161,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_glUniform4fv(Batch& batch, uint32 paramOffset);
|
||||
void do_glUniformMatrix4fv(Batch& batch, uint32 paramOffset);
|
||||
|
||||
void do_glMatrixMode(Batch& batch, uint32 paramOffset);
|
||||
|
|
|
@ -67,6 +67,26 @@ Resource::Sysmem::Sysmem(Size size, const Byte* bytes) :
|
|||
}
|
||||
}
|
||||
|
||||
Resource::Sysmem::Sysmem(const Sysmem& sysmem) :
|
||||
_stamp(0),
|
||||
_size(0),
|
||||
_data(NULL)
|
||||
{
|
||||
if (sysmem.getSize() > 0) {
|
||||
_size = allocateMemory(&_data, sysmem.getSize());
|
||||
if (_size >= sysmem.getSize()) {
|
||||
if (sysmem.readData()) {
|
||||
memcpy(_data, sysmem.readData(), sysmem.getSize());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Resource::Sysmem& Resource::Sysmem::operator=(const Sysmem& sysmem) {
|
||||
setData(sysmem.getSize(), sysmem.readData());
|
||||
return (*this);
|
||||
}
|
||||
|
||||
Resource::Sysmem::~Sysmem() {
|
||||
deallocateMemory( _data, _size );
|
||||
_data = NULL;
|
||||
|
@ -75,7 +95,7 @@ Resource::Sysmem::~Sysmem() {
|
|||
|
||||
Resource::Size Resource::Sysmem::allocate(Size size) {
|
||||
if (size != _size) {
|
||||
Byte* newData = 0;
|
||||
Byte* newData = NULL;
|
||||
Size newSize = 0;
|
||||
if (size > 0) {
|
||||
Size allocated = allocateMemory(&newData, size);
|
||||
|
@ -96,7 +116,7 @@ Resource::Size Resource::Sysmem::allocate(Size size) {
|
|||
|
||||
Resource::Size Resource::Sysmem::resize(Size size) {
|
||||
if (size != _size) {
|
||||
Byte* newData = 0;
|
||||
Byte* newData = NULL;
|
||||
Size newSize = 0;
|
||||
if (size > 0) {
|
||||
Size allocated = allocateMemory(&newData, size);
|
||||
|
@ -152,19 +172,35 @@ Resource::Size Resource::Sysmem::append(Size size, const Byte* bytes) {
|
|||
|
||||
Buffer::Buffer() :
|
||||
Resource(),
|
||||
_sysmem(NULL),
|
||||
_sysmem(new Sysmem()),
|
||||
_gpuObject(NULL) {
|
||||
_sysmem = new Sysmem();
|
||||
}
|
||||
|
||||
Buffer::Buffer(Size size, const Byte* bytes) :
|
||||
Resource(),
|
||||
_sysmem(new Sysmem(size, bytes)),
|
||||
_gpuObject(NULL) {
|
||||
}
|
||||
|
||||
Buffer::Buffer(const Buffer& buf) :
|
||||
Resource(),
|
||||
_sysmem(new Sysmem(buf.getSysmem())),
|
||||
_gpuObject(NULL) {
|
||||
}
|
||||
|
||||
Buffer& Buffer::operator=(const Buffer& buf) {
|
||||
(*_sysmem) = buf.getSysmem();
|
||||
return (*this);
|
||||
}
|
||||
|
||||
Buffer::~Buffer() {
|
||||
if (_sysmem) {
|
||||
delete _sysmem;
|
||||
_sysmem = 0;
|
||||
_sysmem = NULL;
|
||||
}
|
||||
if (_gpuObject) {
|
||||
delete _gpuObject;
|
||||
_gpuObject = 0;
|
||||
_gpuObject = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,13 +12,15 @@
|
|||
#define hifi_gpu_Resource_h
|
||||
|
||||
#include <assert.h>
|
||||
#include "GPUConfig.h"
|
||||
|
||||
#include "Format.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <QSharedPointer>
|
||||
#ifdef _DEBUG
|
||||
#include <QDebug>
|
||||
#endif
|
||||
|
||||
namespace gpu {
|
||||
|
||||
|
@ -29,7 +31,7 @@ typedef int Stamp;
|
|||
class Resource {
|
||||
public:
|
||||
typedef unsigned char Byte;
|
||||
typedef unsigned int Size;
|
||||
typedef unsigned int Size;
|
||||
|
||||
static const Size NOT_ALLOCATED = -1;
|
||||
|
||||
|
@ -47,6 +49,8 @@ protected:
|
|||
|
||||
Sysmem();
|
||||
Sysmem(Size size, const Byte* bytes);
|
||||
Sysmem(const Sysmem& sysmem); // deep copy of the sysmem buffer
|
||||
Sysmem& operator=(const Sysmem& sysmem); // deep copy of the sysmem buffer
|
||||
~Sysmem();
|
||||
|
||||
Size getSize() const { return _size; }
|
||||
|
@ -78,10 +82,8 @@ protected:
|
|||
inline const Byte* readData() const { return _data; }
|
||||
inline Byte* editData() { _stamp++; return _data; }
|
||||
|
||||
template< typename T >
|
||||
const T* read() const { return reinterpret_cast< T* > ( _data ); }
|
||||
template< typename T >
|
||||
T* edit() { _stamp++; return reinterpret_cast< T* > ( _data ); }
|
||||
template< typename T > const T* read() const { return reinterpret_cast< T* > ( _data ); }
|
||||
template< typename T > T* edit() { _stamp++; return reinterpret_cast< T* > ( _data ); }
|
||||
|
||||
// Access the current version of the sysmem, used to compare if copies are in sync
|
||||
inline Stamp getStamp() const { return _stamp; }
|
||||
|
@ -90,9 +92,6 @@ protected:
|
|||
static void deallocateMemory(Byte* memDeallocated, Size size);
|
||||
|
||||
private:
|
||||
Sysmem(const Sysmem& sysmem) {}
|
||||
Sysmem &operator=(const Sysmem& other) {return *this;}
|
||||
|
||||
Stamp _stamp;
|
||||
Size _size;
|
||||
Byte* _data;
|
||||
|
@ -104,12 +103,15 @@ class Buffer : public Resource {
|
|||
public:
|
||||
|
||||
Buffer();
|
||||
Buffer(const Buffer& buf);
|
||||
Buffer(Size size, const Byte* bytes);
|
||||
Buffer(const Buffer& buf); // deep copy of the sysmem buffer
|
||||
Buffer& operator=(const Buffer& buf); // deep copy of the sysmem buffer
|
||||
~Buffer();
|
||||
|
||||
// The size in bytes of data stored in the buffer
|
||||
Size getSize() const { return getSysmem().getSize(); }
|
||||
const Byte* getData() const { return getSysmem().readData(); }
|
||||
Byte* editData() { return editSysmem().editData(); }
|
||||
|
||||
// Resize the buffer
|
||||
// Keep previous data [0 to min(pSize, mSize)]
|
||||
|
@ -130,7 +132,7 @@ public:
|
|||
|
||||
// Access the sysmem object.
|
||||
const Sysmem& getSysmem() const { assert(_sysmem); return (*_sysmem); }
|
||||
|
||||
Sysmem& editSysmem() { assert(_sysmem); return (*_sysmem); }
|
||||
|
||||
protected:
|
||||
|
||||
|
@ -138,8 +140,6 @@ protected:
|
|||
|
||||
mutable GPUObject* _gpuObject;
|
||||
|
||||
Sysmem& editSysmem() { assert(_sysmem); return (*_sysmem); }
|
||||
|
||||
// This shouldn't be used by anything else than the Backend class with the proper casting.
|
||||
void setGPUObject(GPUObject* gpuObject) const { _gpuObject = gpuObject; }
|
||||
GPUObject* getGPUObject() const { return _gpuObject; }
|
||||
|
@ -149,6 +149,277 @@ protected:
|
|||
|
||||
typedef QSharedPointer<Buffer> BufferPointer;
|
||||
typedef std::vector< BufferPointer > Buffers;
|
||||
|
||||
|
||||
class BufferView {
|
||||
public:
|
||||
typedef Resource::Size Size;
|
||||
typedef int Index;
|
||||
|
||||
BufferPointer _buffer;
|
||||
Size _offset;
|
||||
Size _size;
|
||||
Element _element;
|
||||
uint16 _stride;
|
||||
|
||||
BufferView() :
|
||||
_buffer(NULL),
|
||||
_offset(0),
|
||||
_size(0),
|
||||
_element(gpu::SCALAR, gpu::UINT8, gpu::RAW),
|
||||
_stride(1)
|
||||
{};
|
||||
|
||||
BufferView(const Element& element) :
|
||||
_buffer(NULL),
|
||||
_offset(0),
|
||||
_size(0),
|
||||
_element(element),
|
||||
_stride(uint16(element.getSize()))
|
||||
{};
|
||||
|
||||
// create the BufferView and own the Buffer
|
||||
BufferView(Buffer* newBuffer, const Element& element = Element(gpu::SCALAR, gpu::UINT8, gpu::RAW)) :
|
||||
_buffer(newBuffer),
|
||||
_offset(0),
|
||||
_size(newBuffer->getSize()),
|
||||
_element(element),
|
||||
_stride(uint16(element.getSize()))
|
||||
{};
|
||||
BufferView(const BufferPointer& buffer, const Element& element = Element(gpu::SCALAR, gpu::UINT8, gpu::RAW)) :
|
||||
_buffer(buffer),
|
||||
_offset(0),
|
||||
_size(buffer->getSize()),
|
||||
_element(element),
|
||||
_stride(uint16(element.getSize()))
|
||||
{};
|
||||
BufferView(const BufferPointer& buffer, Size offset, Size size, const Element& element = Element(gpu::SCALAR, gpu::UINT8, gpu::RAW)) :
|
||||
_buffer(buffer),
|
||||
_offset(offset),
|
||||
_size(size),
|
||||
_element(element),
|
||||
_stride(uint16(element.getSize()))
|
||||
{};
|
||||
~BufferView() {}
|
||||
BufferView(const BufferView& view) = default;
|
||||
BufferView& operator=(const BufferView& view) = default;
|
||||
|
||||
Size getNumElements() const { return _size / _element.getSize(); }
|
||||
|
||||
//Template iterator with random access on the buffer sysmem
|
||||
template<typename T>
|
||||
class Iterator : public std::iterator<std::random_access_iterator_tag, T, Index, T*, T&>
|
||||
{
|
||||
public:
|
||||
|
||||
Iterator(T* ptr = NULL) { _ptr = ptr; }
|
||||
Iterator(const Iterator<T>& iterator) = default;
|
||||
~Iterator() {}
|
||||
|
||||
Iterator<T>& operator=(const Iterator<T>& iterator) = default;
|
||||
Iterator<T>& operator=(T* ptr) {
|
||||
_ptr = ptr;
|
||||
return (*this);
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
if(_ptr)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator==(const Iterator<T>& iterator) const { return (_ptr == iterator.getConstPtr()); }
|
||||
bool operator!=(const Iterator<T>& iterator) const { return (_ptr != iterator.getConstPtr()); }
|
||||
|
||||
Iterator<T>& operator+=(const Index& movement) {
|
||||
_ptr += movement;
|
||||
return (*this);
|
||||
}
|
||||
Iterator<T>& operator-=(const Index& movement) {
|
||||
_ptr -= movement;
|
||||
return (*this);
|
||||
}
|
||||
Iterator<T>& operator++() {
|
||||
++_ptr;
|
||||
return (*this);
|
||||
}
|
||||
Iterator<T>& operator--() {
|
||||
--_ptr;
|
||||
return (*this);
|
||||
}
|
||||
Iterator<T> operator++(Index) {
|
||||
auto temp(*this);
|
||||
++_ptr;
|
||||
return temp;
|
||||
}
|
||||
Iterator<T> operator--(Index) {
|
||||
auto temp(*this);
|
||||
--_ptr;
|
||||
return temp;
|
||||
}
|
||||
Iterator<T> operator+(const Index& movement) {
|
||||
auto oldPtr = _ptr;
|
||||
_ptr += movement;
|
||||
auto temp(*this);
|
||||
_ptr = oldPtr;
|
||||
return temp;
|
||||
}
|
||||
Iterator<T> operator-(const Index& movement) {
|
||||
auto oldPtr = _ptr;
|
||||
_ptr -= movement;
|
||||
auto temp(*this);
|
||||
_ptr = oldPtr;
|
||||
return temp;
|
||||
}
|
||||
|
||||
Index operator-(const Iterator<T>& iterator) { return (iterator.getPtr() - this->getPtr())/sizeof(T); }
|
||||
|
||||
T& operator*(){return *_ptr;}
|
||||
const T& operator*()const{return *_ptr;}
|
||||
T* operator->(){return _ptr;}
|
||||
|
||||
T* getPtr()const{return _ptr;}
|
||||
const T* getConstPtr()const{return _ptr;}
|
||||
|
||||
protected:
|
||||
|
||||
T* _ptr;
|
||||
};
|
||||
|
||||
template <typename T> Iterator<T> begin() { return Iterator<T>(&edit<T>(0)); }
|
||||
template <typename T> Iterator<T> end() { return Iterator<T>(&edit<T>(getNum<T>())); }
|
||||
template <typename T> Iterator<const T> cbegin() const { return Iterator<const T>(&get<T>(0)); }
|
||||
template <typename T> Iterator<const T> cend() const { return Iterator<const T>(&get<T>(getNum<T>())); }
|
||||
|
||||
// the number of elements of the specified type fitting in the view size
|
||||
template <typename T> Index getNum() const {
|
||||
return Index(_size / sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T> const T& get() const {
|
||||
#if _DEBUG
|
||||
if (_buffer.isNull()) {
|
||||
qDebug() << "Accessing null gpu::buffer!";
|
||||
}
|
||||
if (sizeof(T) > (_buffer->getSize() - _offset)) {
|
||||
qDebug() << "Accessing buffer in non allocated memory, element size = " << sizeof(T) << " available space in buffer at offset is = " << (_buffer->getSize() - _offset);
|
||||
}
|
||||
if (sizeof(T) > _size) {
|
||||
qDebug() << "Accessing buffer outside the BufferView range, element size = " << sizeof(T) << " when bufferView size = " << _size;
|
||||
}
|
||||
#endif
|
||||
const T* t = (reinterpret_cast<const T*> (_buffer->getData() + _offset));
|
||||
return *(t);
|
||||
}
|
||||
|
||||
template <typename T> T& edit() {
|
||||
#if _DEBUG
|
||||
if (_buffer.isNull()) {
|
||||
qDebug() << "Accessing null gpu::buffer!";
|
||||
}
|
||||
if (sizeof(T) > (_buffer->getSize() - _offset)) {
|
||||
qDebug() << "Accessing buffer in non allocated memory, element size = " << sizeof(T) << " available space in buffer at offset is = " << (_buffer->getSize() - _offset);
|
||||
}
|
||||
if (sizeof(T) > _size) {
|
||||
qDebug() << "Accessing buffer outside the BufferView range, element size = " << sizeof(T) << " when bufferView size = " << _size;
|
||||
}
|
||||
#endif
|
||||
T* t = (reinterpret_cast<T*> (_buffer->editData() + _offset));
|
||||
return *(t);
|
||||
}
|
||||
|
||||
template <typename T> const T& get(const Index index) const {
|
||||
Resource::Size elementOffset = index * sizeof(T) + _offset;
|
||||
#if _DEBUG
|
||||
if (_buffer.isNull()) {
|
||||
qDebug() << "Accessing null gpu::buffer!";
|
||||
}
|
||||
if (sizeof(T) > (_buffer->getSize() - elementOffset)) {
|
||||
qDebug() << "Accessing buffer in non allocated memory, index = " << index << ", element size = " << sizeof(T) << " available space in buffer at offset is = " << (_buffer->getSize() - elementOffset);
|
||||
}
|
||||
if (index > getNum<T>()) {
|
||||
qDebug() << "Accessing buffer outside the BufferView range, index = " << index << " number elements = " << getNum<T>();
|
||||
}
|
||||
#endif
|
||||
return *(reinterpret_cast<const T*> (_buffer->getData() + elementOffset));
|
||||
}
|
||||
|
||||
template <typename T> T& edit(const Index index) const {
|
||||
Resource::Size elementOffset = index * sizeof(T) + _offset;
|
||||
#if _DEBUG
|
||||
if (_buffer.isNull()) {
|
||||
qDebug() << "Accessing null gpu::buffer!";
|
||||
}
|
||||
if (sizeof(T) > (_buffer->getSize() - elementOffset)) {
|
||||
qDebug() << "Accessing buffer in non allocated memory, index = " << index << ", element size = " << sizeof(T) << " available space in buffer at offset is = " << (_buffer->getSize() - elementOffset);
|
||||
}
|
||||
if (index > getNum<T>()) {
|
||||
qDebug() << "Accessing buffer outside the BufferView range, index = " << index << " number elements = " << getNum<T>();
|
||||
}
|
||||
#endif
|
||||
return *(reinterpret_cast<T*> (_buffer->editData() + elementOffset));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// TODO: For now TextureView works with Buffer as a place holder for the Texture.
|
||||
// The overall logic should be about the same except that the Texture will be a real GL Texture under the hood
|
||||
class TextureView {
|
||||
public:
|
||||
typedef Resource::Size Size;
|
||||
typedef int Index;
|
||||
|
||||
BufferPointer _buffer;
|
||||
Size _offset;
|
||||
Size _size;
|
||||
Element _element;
|
||||
uint16 _stride;
|
||||
|
||||
TextureView() :
|
||||
_buffer(NULL),
|
||||
_offset(0),
|
||||
_size(0),
|
||||
_element(gpu::VEC3, gpu::UINT8, gpu::RGB),
|
||||
_stride(1)
|
||||
{};
|
||||
|
||||
TextureView(const Element& element) :
|
||||
_buffer(NULL),
|
||||
_offset(0),
|
||||
_size(0),
|
||||
_element(element),
|
||||
_stride(uint16(element.getSize()))
|
||||
{};
|
||||
|
||||
// create the BufferView and own the Buffer
|
||||
TextureView(Buffer* newBuffer, const Element& element) :
|
||||
_buffer(newBuffer),
|
||||
_offset(0),
|
||||
_size(newBuffer->getSize()),
|
||||
_element(element),
|
||||
_stride(uint16(element.getSize()))
|
||||
{};
|
||||
TextureView(const BufferPointer& buffer, const Element& element) :
|
||||
_buffer(buffer),
|
||||
_offset(0),
|
||||
_size(buffer->getSize()),
|
||||
_element(element),
|
||||
_stride(uint16(element.getSize()))
|
||||
{};
|
||||
TextureView(const BufferPointer& buffer, Size offset, Size size, const Element& element) :
|
||||
_buffer(buffer),
|
||||
_offset(offset),
|
||||
_size(size),
|
||||
_element(element),
|
||||
_stride(uint16(element.getSize()))
|
||||
{};
|
||||
~TextureView() {}
|
||||
TextureView(const TextureView& view) = default;
|
||||
TextureView& operator=(const TextureView& view) = default;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ void Stream::Format::evaluateCache() {
|
|||
_elementTotalSize = 0;
|
||||
for(AttributeMap::iterator it = _attributes.begin(); it != _attributes.end(); it++) {
|
||||
Attribute& attrib = (*it).second;
|
||||
Channel& channel = _channels[attrib._channel];
|
||||
ChannelInfo& channel = _channels[attrib._channel];
|
||||
channel._slots.push_back(attrib._slot);
|
||||
channel._stride = std::max(channel._stride, attrib.getSize() + attrib._offset);
|
||||
channel._netSize += attrib.getSize();
|
||||
|
@ -41,7 +41,7 @@ BufferStream::BufferStream() :
|
|||
BufferStream::~BufferStream() {
|
||||
}
|
||||
|
||||
void BufferStream::addBuffer(BufferPointer& buffer, Offset offset, Offset stride) {
|
||||
void BufferStream::addBuffer(const BufferPointer& buffer, Offset offset, Offset stride) {
|
||||
_buffers.push_back(buffer);
|
||||
_offsets.push_back(offset);
|
||||
_strides.push_back(stride);
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#define hifi_gpu_Stream_h
|
||||
|
||||
#include <assert.h>
|
||||
#include "GPUConfig.h"
|
||||
|
||||
#include "Resource.h"
|
||||
#include "Format.h"
|
||||
|
@ -83,16 +82,16 @@ public:
|
|||
public:
|
||||
typedef std::map< Slot, Attribute > AttributeMap;
|
||||
|
||||
class Channel {
|
||||
class ChannelInfo {
|
||||
public:
|
||||
std::vector< Slot > _slots;
|
||||
std::vector< Offset > _offsets;
|
||||
Offset _stride;
|
||||
uint32 _netSize;
|
||||
|
||||
Channel() : _stride(0), _netSize(0) {}
|
||||
ChannelInfo() : _stride(0), _netSize(0) {}
|
||||
};
|
||||
typedef std::map< Slot, Channel > ChannelMap;
|
||||
typedef std::map< Slot, ChannelInfo > ChannelMap;
|
||||
|
||||
Format() :
|
||||
_attributes(),
|
||||
|
@ -104,6 +103,7 @@ public:
|
|||
|
||||
uint8 getNumChannels() const { return _channels.size(); }
|
||||
const ChannelMap& getChannels() const { return _channels; }
|
||||
const Offset getChannelStride(Slot channel) const { return _channels.at(channel)._stride; }
|
||||
|
||||
uint32 getElementTotalSize() const { return _elementTotalSize; }
|
||||
|
||||
|
@ -131,7 +131,7 @@ public:
|
|||
BufferStream();
|
||||
~BufferStream();
|
||||
|
||||
void addBuffer(BufferPointer& buffer, Offset offset, Offset stride);
|
||||
void addBuffer(const BufferPointer& buffer, Offset offset, Offset stride);
|
||||
|
||||
const Buffers& getBuffers() const { return _buffers; }
|
||||
const Offsets& getOffsets() const { return _offsets; }
|
||||
|
|
|
@ -10,5 +10,5 @@ link_hifi_libraries(shared networking)
|
|||
|
||||
include_glm()
|
||||
|
||||
# call macro to link our dependencies and bubble them up via a property on our target
|
||||
link_shared_dependencies()
|
||||
# call macro to include our dependency includes and bubble them up via a property on our target
|
||||
include_dependency_includes()
|
11
libraries/model/CMakeLists.txt
Executable file
11
libraries/model/CMakeLists.txt
Executable file
|
@ -0,0 +1,11 @@
|
|||
set(TARGET_NAME model)
|
||||
|
||||
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
||||
setup_hifi_library()
|
||||
|
||||
include_glm()
|
||||
|
||||
link_hifi_libraries(shared gpu)
|
||||
|
||||
# call macro to link our dependencies and bubble them up via a property on our target
|
||||
include_dependency_includes()
|
141
libraries/model/src/model/Geometry.cpp
Executable file
141
libraries/model/src/model/Geometry.cpp
Executable file
|
@ -0,0 +1,141 @@
|
|||
//
|
||||
// Geometry.cpp
|
||||
// libraries/model/src/model
|
||||
//
|
||||
// Created by Sam Gateau on 12/5/2014.
|
||||
// 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
|
||||
//
|
||||
|
||||
#include "Geometry.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
using namespace model;
|
||||
|
||||
Mesh::Mesh() :
|
||||
_vertexBuffer(gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)),
|
||||
_indexBuffer(gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::INDEX)),
|
||||
_partBuffer(gpu::Element(gpu::VEC4, gpu::UINT32, gpu::PART)) {
|
||||
}
|
||||
|
||||
Mesh::Mesh(const Mesh& mesh) :
|
||||
_vertexFormat(mesh._vertexFormat),
|
||||
_vertexBuffer(mesh._vertexBuffer),
|
||||
_attributeBuffers(mesh._attributeBuffers),
|
||||
_indexBuffer(mesh._indexBuffer),
|
||||
_partBuffer(mesh._partBuffer) {
|
||||
}
|
||||
|
||||
Mesh::~Mesh() {
|
||||
}
|
||||
|
||||
void Mesh::setVertexBuffer(const BufferView& buffer) {
|
||||
_vertexBuffer = buffer;
|
||||
evalVertexFormat();
|
||||
}
|
||||
|
||||
void Mesh::addAttribute(Slot slot, const BufferView& buffer) {
|
||||
_attributeBuffers[slot] = buffer;
|
||||
evalVertexFormat();
|
||||
}
|
||||
|
||||
void Mesh::evalVertexFormat() {
|
||||
VertexFormat vf;
|
||||
int channelNum = 0;
|
||||
if (hasVertexData()) {
|
||||
vf.setAttribute(gpu::Stream::POSITION, channelNum, _vertexBuffer._element, 0);
|
||||
channelNum++;
|
||||
}
|
||||
for (auto attrib : _attributeBuffers) {
|
||||
vf.setAttribute(attrib.first, channelNum, attrib.second._element, 0);
|
||||
channelNum++;
|
||||
}
|
||||
|
||||
_vertexFormat = vf;
|
||||
}
|
||||
|
||||
void Mesh::setIndexBuffer(const BufferView& buffer) {
|
||||
_indexBuffer = buffer;
|
||||
}
|
||||
|
||||
void Mesh::setPartBuffer(const BufferView& buffer) {
|
||||
_partBuffer = buffer;
|
||||
}
|
||||
|
||||
const Box Mesh::evalPartBound(int partNum) const {
|
||||
Box box;
|
||||
if (partNum < _partBuffer.getNum<Part>()) {
|
||||
const Part& part = _partBuffer.get<Part>(partNum);
|
||||
auto index = _indexBuffer.cbegin<Index>();
|
||||
index += part._startIndex;
|
||||
auto endIndex = index;
|
||||
endIndex += part._numIndices;
|
||||
auto vertices = &_vertexBuffer.get<Vec3>(part._baseVertex);
|
||||
for (;index != endIndex; index++) {
|
||||
// skip primitive restart indices
|
||||
if ((*index) != PRIMITIVE_RESTART_INDEX) {
|
||||
box += vertices[(*index)];
|
||||
}
|
||||
}
|
||||
}
|
||||
return box;
|
||||
}
|
||||
|
||||
const Box Mesh::evalPartBounds(int partStart, int partEnd, Boxes& bounds) const {
|
||||
Box totalBound;
|
||||
auto part = _partBuffer.cbegin<Part>() + partStart;
|
||||
auto partItEnd = _partBuffer.cbegin<Part>() + partEnd;
|
||||
|
||||
for (;part != partItEnd; part++) {
|
||||
|
||||
Box partBound;
|
||||
auto index = _indexBuffer.cbegin<uint>() + (*part)._startIndex;
|
||||
auto endIndex = index + (*part)._numIndices;
|
||||
auto vertices = &_vertexBuffer.get<Vec3>((*part)._baseVertex);
|
||||
for (;index != endIndex; index++) {
|
||||
// skip primitive restart indices
|
||||
if ((*index) != PRIMITIVE_RESTART_INDEX) {
|
||||
partBound += vertices[(*index)];
|
||||
}
|
||||
}
|
||||
|
||||
totalBound += partBound;
|
||||
}
|
||||
return totalBound;
|
||||
}
|
||||
|
||||
const gpu::BufferStream Mesh::makeBufferStream() const {
|
||||
gpu::BufferStream stream;
|
||||
|
||||
int channelNum = 0;
|
||||
if (hasVertexData()) {
|
||||
stream.addBuffer(_vertexBuffer._buffer, _vertexBuffer._offset, _vertexFormat.getChannelStride(channelNum));
|
||||
channelNum++;
|
||||
}
|
||||
for (auto attrib : _attributeBuffers) {
|
||||
BufferView& view = attrib.second;
|
||||
stream.addBuffer(view._buffer, view._offset, _vertexFormat.getChannelStride(channelNum));
|
||||
channelNum++;
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
Geometry::Geometry() {
|
||||
}
|
||||
|
||||
Geometry::Geometry(const Geometry& geometry):
|
||||
_mesh(geometry._mesh),
|
||||
_boxes(geometry._boxes) {
|
||||
}
|
||||
|
||||
Geometry::~Geometry() {
|
||||
}
|
||||
|
||||
void Geometry::setMesh(const MeshPointer& mesh) {
|
||||
_mesh = mesh;
|
||||
}
|
||||
|
150
libraries/model/src/model/Geometry.h
Executable file
150
libraries/model/src/model/Geometry.h
Executable file
|
@ -0,0 +1,150 @@
|
|||
//
|
||||
// Geometry.h
|
||||
// libraries/model/src/model
|
||||
//
|
||||
// Created by Sam Gateau on 12/5/2014.
|
||||
// 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
|
||||
//
|
||||
#ifndef hifi_model_Geometry_h
|
||||
#define hifi_model_Geometry_h
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "AABox.h"
|
||||
|
||||
#include "gpu/Resource.h"
|
||||
#include "gpu/Stream.h"
|
||||
|
||||
namespace model {
|
||||
typedef gpu::BufferView::Index Index;
|
||||
typedef gpu::BufferView BufferView;
|
||||
typedef AABox Box;
|
||||
typedef std::vector< Box > Boxes;
|
||||
|
||||
class Mesh {
|
||||
public:
|
||||
const static Index PRIMITIVE_RESTART_INDEX = -1;
|
||||
|
||||
typedef gpu::BufferView BufferView;
|
||||
typedef std::vector< BufferView > BufferViews;
|
||||
|
||||
typedef gpu::Stream::Slot Slot;
|
||||
typedef gpu::Stream::Format VertexFormat;
|
||||
typedef std::map< Slot, BufferView > BufferViewMap;
|
||||
|
||||
typedef glm::vec3 Vec3;
|
||||
|
||||
Mesh();
|
||||
Mesh(const Mesh& mesh);
|
||||
Mesh& operator= (const Mesh& mesh) = default;
|
||||
virtual ~Mesh();
|
||||
|
||||
// Vertex buffer
|
||||
void setVertexBuffer(const BufferView& buffer);
|
||||
const BufferView& getVertexBuffer() const { return _vertexBuffer; }
|
||||
uint getNumVertices() const { return _vertexBuffer.getNumElements(); }
|
||||
bool hasVertexData() const { return !_vertexBuffer._buffer.isNull(); }
|
||||
|
||||
// Attribute Buffers
|
||||
int getNumAttributes() const { return _attributeBuffers.size(); }
|
||||
void addAttribute(Slot slot, const BufferView& buffer);
|
||||
|
||||
// Stream format
|
||||
const VertexFormat& getVertexFormat() const { return _vertexFormat; }
|
||||
|
||||
// Index Buffer
|
||||
void setIndexBuffer(const BufferView& buffer);
|
||||
const BufferView& getIndexBuffer() const { return _indexBuffer; }
|
||||
uint getNumIndices() const { return _indexBuffer.getNumElements(); }
|
||||
|
||||
// Access vertex position value
|
||||
const Vec3& getPos3(Index index) const { return _vertexBuffer.get<Vec3>(index); }
|
||||
|
||||
enum Topology {
|
||||
POINTS = 0,
|
||||
LINES,
|
||||
LINE_STRIP,
|
||||
TRIANGLES,
|
||||
TRIANGLE_STRIP,
|
||||
QUADS,
|
||||
QUAD_STRIP,
|
||||
|
||||
NUM_TOPOLOGIES,
|
||||
};
|
||||
|
||||
// Subpart of a mesh, describing the toplogy of the surface
|
||||
class Part {
|
||||
public:
|
||||
Index _startIndex;
|
||||
Index _numIndices;
|
||||
Index _baseVertex;
|
||||
Topology _topology;
|
||||
|
||||
Part() :
|
||||
_startIndex(0),
|
||||
_numIndices(0),
|
||||
_baseVertex(0),
|
||||
_topology(TRIANGLES)
|
||||
{}
|
||||
Part(Index startIndex, Index numIndices, Index baseVertex, Topology topology) :
|
||||
_startIndex(startIndex),
|
||||
_numIndices(numIndices),
|
||||
_baseVertex(baseVertex),
|
||||
_topology(topology)
|
||||
{}
|
||||
};
|
||||
|
||||
void setPartBuffer(const BufferView& buffer);
|
||||
const BufferView& getPartBuffer() const { return _partBuffer; }
|
||||
uint getNumParts() const { return _partBuffer.getNumElements(); }
|
||||
|
||||
// evaluate the bounding box of A part
|
||||
const Box evalPartBound(int partNum) const;
|
||||
// evaluate the bounding boxes of the parts in the range [start, end[ and fill the bounds parameter
|
||||
// the returned box is the bounding box of ALL the evaluated part bounds.
|
||||
const Box evalPartBounds(int partStart, int partEnd, Boxes& bounds) const;
|
||||
|
||||
|
||||
// Generate a BufferStream on the mesh vertices and attributes
|
||||
const gpu::BufferStream makeBufferStream() const;
|
||||
|
||||
protected:
|
||||
|
||||
VertexFormat _vertexFormat;
|
||||
|
||||
BufferView _vertexBuffer;
|
||||
BufferViewMap _attributeBuffers;
|
||||
|
||||
BufferView _indexBuffer;
|
||||
|
||||
BufferView _partBuffer;
|
||||
|
||||
void evalVertexFormat();
|
||||
|
||||
};
|
||||
typedef QSharedPointer< Mesh > MeshPointer;
|
||||
|
||||
|
||||
class Geometry {
|
||||
public:
|
||||
|
||||
Geometry();
|
||||
Geometry(const Geometry& geometry);
|
||||
~Geometry();
|
||||
|
||||
void setMesh(const MeshPointer& mesh);
|
||||
const MeshPointer& getMesh() const { return _mesh; }
|
||||
|
||||
protected:
|
||||
|
||||
MeshPointer _mesh;
|
||||
BufferView _boxes;
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
92
libraries/model/src/model/Material.cpp
Executable file
92
libraries/model/src/model/Material.cpp
Executable file
|
@ -0,0 +1,92 @@
|
|||
//
|
||||
// Material.cpp
|
||||
// libraries/model/src/model
|
||||
//
|
||||
// Created by Sam Gateau on 12/10/2014.
|
||||
// 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
|
||||
//
|
||||
#include "Material.h"
|
||||
|
||||
using namespace model;
|
||||
|
||||
Material::Material() :
|
||||
_flags(0),
|
||||
_schemaBuffer(),
|
||||
_textureMap() {
|
||||
|
||||
// only if created from nothing shall we create the Buffer to store the properties
|
||||
Schema schema;
|
||||
_schemaBuffer = gpu::BufferView(new gpu::Buffer(sizeof(Schema), (const gpu::Buffer::Byte*) &schema));
|
||||
|
||||
|
||||
}
|
||||
|
||||
Material::Material(const Material& material) :
|
||||
_flags(material._flags),
|
||||
_schemaBuffer(material._schemaBuffer),
|
||||
_textureMap(material._textureMap) {
|
||||
}
|
||||
|
||||
Material& Material::operator= (const Material& material) {
|
||||
_flags = (material._flags);
|
||||
_schemaBuffer = (material._schemaBuffer);
|
||||
_textureMap = (material._textureMap);
|
||||
|
||||
return (*this);
|
||||
}
|
||||
|
||||
Material::~Material() {
|
||||
}
|
||||
|
||||
void Material::setDiffuse(const Color& diffuse) {
|
||||
if (glm::any(glm::greaterThan(diffuse, Color(0.0f)))) {
|
||||
_flags.set(DIFFUSE_BIT);
|
||||
} else {
|
||||
_flags.reset(DIFFUSE_BIT);
|
||||
}
|
||||
_schemaBuffer.edit<Schema>()._diffuse = diffuse;
|
||||
}
|
||||
|
||||
void Material::setSpecular(const Color& specular) {
|
||||
if (glm::any(glm::greaterThan(specular, Color(0.0f)))) {
|
||||
_flags.set(SPECULAR_BIT);
|
||||
} else {
|
||||
_flags.reset(SPECULAR_BIT);
|
||||
}
|
||||
_schemaBuffer.edit<Schema>()._specular = specular;
|
||||
}
|
||||
|
||||
void Material::setEmissive(const Color& emissive) {
|
||||
if (glm::any(glm::greaterThan(emissive, Color(0.0f)))) {
|
||||
_flags.set(EMISSIVE_BIT);
|
||||
} else {
|
||||
_flags.reset(EMISSIVE_BIT);
|
||||
}
|
||||
_schemaBuffer.edit<Schema>()._emissive = emissive;
|
||||
}
|
||||
|
||||
void Material::setShininess(float shininess) {
|
||||
if (shininess > 0.0f) {
|
||||
_flags.set(SHININESS_BIT);
|
||||
} else {
|
||||
_flags.reset(SHININESS_BIT);
|
||||
}
|
||||
_schemaBuffer.edit<Schema>()._shininess = shininess;
|
||||
}
|
||||
|
||||
void Material::setOpacity(float opacity) {
|
||||
if (opacity >= 1.0f) {
|
||||
_flags.reset(TRANSPARENT_BIT);
|
||||
} else {
|
||||
_flags.set(TRANSPARENT_BIT);
|
||||
}
|
||||
_schemaBuffer.edit<Schema>()._opacity = opacity;
|
||||
}
|
||||
|
||||
void Material::setTextureView(MapChannel channel, const TextureView& view) {
|
||||
_flags.set(DIFFUSE_MAP_BIT + channel);
|
||||
_textureMap[channel] = view;
|
||||
}
|
114
libraries/model/src/model/Material.h
Executable file
114
libraries/model/src/model/Material.h
Executable file
|
@ -0,0 +1,114 @@
|
|||
//
|
||||
// Material.h
|
||||
// libraries/model/src/model
|
||||
//
|
||||
// Created by Sam Gateau on 12/10/2014.
|
||||
// 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
|
||||
//
|
||||
#ifndef hifi_model_Material_h
|
||||
#define hifi_model_Material_h
|
||||
|
||||
#include <bitset>
|
||||
#include <map>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "gpu/Resource.h"
|
||||
|
||||
namespace model {
|
||||
typedef gpu::BufferView UniformBufferView;
|
||||
typedef gpu::TextureView TextureView;
|
||||
|
||||
class Material {
|
||||
public:
|
||||
|
||||
typedef glm::vec3 Color;
|
||||
|
||||
enum MapChannel {
|
||||
DIFFUSE_MAP = 0,
|
||||
SPECULAR_MAP,
|
||||
SHININESS_MAP,
|
||||
EMISSIVE_MAP,
|
||||
OPACITY_MAP,
|
||||
NORMAL_MAP,
|
||||
|
||||
NUM_MAPS,
|
||||
};
|
||||
typedef std::map<MapChannel, TextureView> TextureMap;
|
||||
|
||||
enum FlagBit {
|
||||
DIFFUSE_BIT = 0,
|
||||
SPECULAR_BIT,
|
||||
SHININESS_BIT,
|
||||
EMISSIVE_BIT,
|
||||
TRANSPARENT_BIT,
|
||||
|
||||
DIFFUSE_MAP_BIT,
|
||||
SPECULAR_MAP_BIT,
|
||||
SHININESS_MAP_BIT,
|
||||
EMISSIVE_MAP_BIT,
|
||||
OPACITY_MAP_BIT,
|
||||
NORMAL_MAP_BIT,
|
||||
|
||||
NUM_FLAGS,
|
||||
};
|
||||
typedef std::bitset<NUM_FLAGS> Flags;
|
||||
|
||||
Material();
|
||||
Material(const Material& material);
|
||||
Material& operator= (const Material& material);
|
||||
virtual ~Material();
|
||||
|
||||
const Color& getEmissive() const { return _schemaBuffer.get<Schema>()._emissive; }
|
||||
const Color& getDiffuse() const { return _schemaBuffer.get<Schema>()._diffuse; }
|
||||
const Color& getSpecular() const { return _schemaBuffer.get<Schema>()._specular; }
|
||||
float getShininess() const { return _schemaBuffer.get<Schema>()._shininess; }
|
||||
float getOpacity() const { return _schemaBuffer.get<Schema>()._opacity; }
|
||||
|
||||
void setDiffuse(const Color& diffuse);
|
||||
void setSpecular(const Color& specular);
|
||||
void setEmissive(const Color& emissive);
|
||||
void setShininess(float shininess);
|
||||
void setOpacity(float opacity);
|
||||
|
||||
// Schema to access the attribute values of the material
|
||||
class Schema {
|
||||
public:
|
||||
|
||||
Color _diffuse;
|
||||
float _opacity;
|
||||
Color _specular;
|
||||
float _shininess;
|
||||
Color _emissive;
|
||||
float _spare0;
|
||||
|
||||
Schema() :
|
||||
_diffuse(0.5f),
|
||||
_opacity(1.0f),
|
||||
_specular(0.03f),
|
||||
_shininess(0.1f),
|
||||
_emissive(0.0f)
|
||||
{}
|
||||
};
|
||||
|
||||
const UniformBufferView& getSchemaBuffer() const { return _schemaBuffer; }
|
||||
|
||||
void setTextureView(MapChannel channel, const TextureView& texture);
|
||||
const TextureMap& getTextureMap() const { return _textureMap; }
|
||||
|
||||
const Schema* getSchema() const { return &_schemaBuffer.get<Schema>(); }
|
||||
protected:
|
||||
|
||||
Flags _flags;
|
||||
UniformBufferView _schemaBuffer;
|
||||
TextureMap _textureMap;
|
||||
|
||||
};
|
||||
typedef QSharedPointer< Material > MaterialPointer;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -28,5 +28,5 @@ target_link_libraries(${TARGET_NAME} ${OPENSSL_LIBRARIES} ${TBB_LIBRARIES})
|
|||
# append libcuckoo includes to our list of includes to bubble
|
||||
list(APPEND ${TARGET_NAME}_DEPENDENCY_INCLUDES "${TBB_INCLUDE_DIRS}")
|
||||
|
||||
# call macro to link our dependencies and bubble them up via a property on our target
|
||||
link_shared_dependencies()
|
||||
# call macro to include our dependency includes and bubble them up via a property on our target
|
||||
include_dependency_includes()
|
|
@ -107,7 +107,8 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl) {
|
|||
if (!handleUsername(lookupUrl.authority())) {
|
||||
// we're assuming this is either a network address or global place name
|
||||
// check if it is a network address first
|
||||
if (!handleNetworkAddress(lookupUrl.host())) {
|
||||
if (!handleNetworkAddress(lookupUrl.host()
|
||||
+ (lookupUrl.port() == -1 ? "" : ":" + QString::number(lookupUrl.port())))) {
|
||||
// wasn't an address - lookup the place name
|
||||
attemptPlaceNameLookup(lookupUrl.host());
|
||||
}
|
||||
|
@ -172,7 +173,7 @@ void AddressManager::goToAddressFromObject(const QVariantMap& addressMap) {
|
|||
if (domainObject.contains(DOMAIN_NETWORK_ADDRESS_KEY)) {
|
||||
QString domainHostname = domainObject[DOMAIN_NETWORK_ADDRESS_KEY].toString();
|
||||
|
||||
emit possibleDomainChangeRequiredToHostname(domainHostname);
|
||||
emit possibleDomainChangeRequired(domainHostname, DEFAULT_DOMAIN_SERVER_PORT);
|
||||
} else {
|
||||
QString iceServerAddress = domainObject[DOMAIN_ICE_SERVER_ADDRESS_KEY].toString();
|
||||
|
||||
|
@ -240,30 +241,40 @@ void AddressManager::attemptPlaceNameLookup(const QString& lookupString) {
|
|||
}
|
||||
|
||||
bool AddressManager::handleNetworkAddress(const QString& lookupString) {
|
||||
const QString IP_ADDRESS_REGEX_STRING = "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}"
|
||||
"([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(:\\d{1,5})?$";
|
||||
const QString IP_ADDRESS_REGEX_STRING = "^((?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}"
|
||||
"(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))(?::(\\d{1,5}))?$";
|
||||
|
||||
const QString HOSTNAME_REGEX_STRING = "^((?:[A-Z0-9]|[A-Z0-9][A-Z0-9\\-]{0,61}[A-Z0-9])"
|
||||
"(?:\\.(?:[A-Z0-9]|[A-Z0-9][A-Z0-9\\-]{0,61}[A-Z0-9]))+|localhost)(:{1}\\d{1,5})?$";
|
||||
|
||||
QRegExp hostnameRegex(HOSTNAME_REGEX_STRING, Qt::CaseInsensitive);
|
||||
|
||||
if (hostnameRegex.indexIn(lookupString) != -1) {
|
||||
QString domainHostname = hostnameRegex.cap(0);
|
||||
|
||||
emit lookupResultsFinished();
|
||||
setDomainHostnameAndName(domainHostname);
|
||||
|
||||
return true;
|
||||
}
|
||||
"(?:\\.(?:[A-Z0-9]|[A-Z0-9][A-Z0-9\\-]{0,61}[A-Z0-9]))+|localhost)(?::(\\d{1,5}))?$";
|
||||
|
||||
QRegExp ipAddressRegex(IP_ADDRESS_REGEX_STRING);
|
||||
|
||||
if (ipAddressRegex.indexIn(lookupString) != -1) {
|
||||
QString domainIPString = ipAddressRegex.cap(0);
|
||||
QString domainIPString = ipAddressRegex.cap(1);
|
||||
|
||||
qint16 domainPort = DEFAULT_DOMAIN_SERVER_PORT;
|
||||
if (!ipAddressRegex.cap(2).isEmpty()) {
|
||||
domainPort = (qint16) ipAddressRegex.cap(2).toInt();
|
||||
}
|
||||
|
||||
emit lookupResultsFinished();
|
||||
setDomainHostnameAndName(domainIPString);
|
||||
setDomainInfo(domainIPString, domainPort);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QRegExp hostnameRegex(HOSTNAME_REGEX_STRING, Qt::CaseInsensitive);
|
||||
|
||||
if (hostnameRegex.indexIn(lookupString) != -1) {
|
||||
QString domainHostname = hostnameRegex.cap(1);
|
||||
|
||||
qint16 domainPort = DEFAULT_DOMAIN_SERVER_PORT;
|
||||
if (!hostnameRegex.cap(2).isEmpty()) {
|
||||
domainPort = (qint16) hostnameRegex.cap(2).toInt();
|
||||
}
|
||||
|
||||
emit lookupResultsFinished();
|
||||
setDomainInfo(domainHostname, domainPort);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -339,9 +350,9 @@ bool AddressManager::handleUsername(const QString& lookupString) {
|
|||
}
|
||||
|
||||
|
||||
void AddressManager::setDomainHostnameAndName(const QString& hostname, const QString& domainName) {
|
||||
void AddressManager::setDomainInfo(const QString& hostname, quint16 port, const QString& domainName) {
|
||||
_currentDomain = domainName.isEmpty() ? hostname : domainName;
|
||||
emit possibleDomainChangeRequiredToHostname(hostname);
|
||||
emit possibleDomainChangeRequired(hostname, port);
|
||||
}
|
||||
|
||||
void AddressManager::goToUser(const QString& username) {
|
||||
|
|
|
@ -59,7 +59,7 @@ signals:
|
|||
void lookupResultsFinished();
|
||||
void lookupResultIsOffline();
|
||||
void lookupResultIsNotFound();
|
||||
void possibleDomainChangeRequiredToHostname(const QString& newHostname);
|
||||
void possibleDomainChangeRequired(const QString& newHostname, quint16 newPort);
|
||||
void possibleDomainChangeRequiredViaICEForID(const QString& iceServerHostname, const QUuid& domainID);
|
||||
void locationChangeRequired(const glm::vec3& newPosition,
|
||||
bool hasOrientationChange, const glm::quat& newOrientation,
|
||||
|
@ -70,7 +70,7 @@ private slots:
|
|||
private:
|
||||
AddressManager();
|
||||
|
||||
void setDomainHostnameAndName(const QString& hostname, const QString& domainName = QString());
|
||||
void setDomainInfo(const QString& hostname, quint16 port, const QString& domainName = QString());
|
||||
|
||||
const JSONCallbackParameters& apiCallbackParameters();
|
||||
|
||||
|
|
|
@ -97,46 +97,32 @@ void DomainHandler::setUUID(const QUuid& uuid) {
|
|||
}
|
||||
}
|
||||
|
||||
QString DomainHandler::hostnameWithoutPort(const QString& hostname) {
|
||||
int colonIndex = hostname.indexOf(':');
|
||||
return colonIndex > 0 ? hostname.left(colonIndex) : hostname;
|
||||
}
|
||||
|
||||
bool DomainHandler::isCurrentHostname(const QString& hostname) {
|
||||
return hostnameWithoutPort(hostname) == _hostname;
|
||||
}
|
||||
|
||||
void DomainHandler::setHostname(const QString& hostname) {
|
||||
void DomainHandler::setHostnameAndPort(const QString& hostname, quint16 port) {
|
||||
|
||||
if (hostname != _hostname) {
|
||||
if (hostname != _hostname || _sockAddr.getPort() != port) {
|
||||
// re-set the domain info so that auth information is reloaded
|
||||
hardReset();
|
||||
|
||||
int colonIndex = hostname.indexOf(':');
|
||||
|
||||
if (colonIndex > 0) {
|
||||
// the user has included a custom DS port with the hostname
|
||||
|
||||
// the new hostname is everything up to the colon
|
||||
_hostname = hostname.left(colonIndex);
|
||||
|
||||
// grab the port by reading the string after the colon
|
||||
_sockAddr.setPort(atoi(hostname.mid(colonIndex + 1, hostname.size()).toLocal8Bit().constData()));
|
||||
|
||||
qDebug() << "Updated hostname to" << _hostname << "and port to" << _sockAddr.getPort();
|
||||
|
||||
} else {
|
||||
// no port included with the hostname, simply set the member variable and reset the domain server port to default
|
||||
if (hostname != _hostname) {
|
||||
// set the new hostname
|
||||
_hostname = hostname;
|
||||
_sockAddr.setPort(DEFAULT_DOMAIN_SERVER_PORT);
|
||||
|
||||
qDebug() << "Updated domain hostname to" << _hostname;
|
||||
|
||||
// re-set the sock addr to null and fire off a lookup of the IP address for this domain-server's hostname
|
||||
qDebug("Looking up DS hostname %s.", _hostname.toLocal8Bit().constData());
|
||||
QHostInfo::lookupHost(_hostname, this, SLOT(completedHostnameLookup(const QHostInfo&)));
|
||||
|
||||
UserActivityLogger::getInstance().changedDomain(_hostname);
|
||||
emit hostnameChanged(_hostname);
|
||||
}
|
||||
|
||||
// re-set the sock addr to null and fire off a lookup of the IP address for this domain-server's hostname
|
||||
qDebug("Looking up DS hostname %s.", _hostname.toLocal8Bit().constData());
|
||||
QHostInfo::lookupHost(_hostname, this, SLOT(completedHostnameLookup(const QHostInfo&)));
|
||||
if (_sockAddr.getPort() != port) {
|
||||
qDebug() << "Updated domain port to" << port;
|
||||
}
|
||||
|
||||
UserActivityLogger::getInstance().changedDomain(_hostname);
|
||||
emit hostnameChanged(_hostname);
|
||||
// grab the port by reading the string after the colon
|
||||
_sockAddr.setPort(port);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,9 +37,7 @@ public:
|
|||
|
||||
const QUuid& getUUID() const { return _uuid; }
|
||||
void setUUID(const QUuid& uuid);
|
||||
|
||||
static QString hostnameWithoutPort(const QString& hostname);
|
||||
bool isCurrentHostname(const QString& hostname);
|
||||
|
||||
const QString& getHostname() const { return _hostname; }
|
||||
|
||||
const QHostAddress& getIP() const { return _sockAddr.getAddress(); }
|
||||
|
@ -75,7 +73,7 @@ public:
|
|||
|
||||
void softReset();
|
||||
public slots:
|
||||
void setHostname(const QString& hostname);
|
||||
void setHostnameAndPort(const QString& hostname, quint16 port = DEFAULT_DOMAIN_SERVER_PORT);
|
||||
void setIceServerHostnameAndID(const QString& iceServerHostname, const QUuid& id);
|
||||
|
||||
private slots:
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <LogHandler.h>
|
||||
|
||||
#include "AccountManager.h"
|
||||
#include "AddressManager.h"
|
||||
#include "Assignment.h"
|
||||
#include "HifiSockAddr.h"
|
||||
#include "NodeList.h"
|
||||
|
@ -62,6 +63,15 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned
|
|||
_hasCompletedInitialSTUNFailure(false),
|
||||
_stunRequestsSinceSuccess(0)
|
||||
{
|
||||
AddressManager& addressManager = AddressManager::getInstance();
|
||||
|
||||
// handle domain change signals from AddressManager
|
||||
connect(&addressManager, &AddressManager::possibleDomainChangeRequired,
|
||||
&_domainHandler, &DomainHandler::setHostnameAndPort);
|
||||
|
||||
connect(&addressManager, &AddressManager::possibleDomainChangeRequiredViaICEForID,
|
||||
&_domainHandler, &DomainHandler::setIceServerHostnameAndID);
|
||||
|
||||
// clear our NodeList when the domain changes
|
||||
connect(&_domainHandler, &DomainHandler::disconnectedFromDomain, this, &NodeList::reset);
|
||||
|
||||
|
|
|
@ -15,5 +15,5 @@ include_directories(SYSTEM "${ZLIB_INCLUDE_DIRS}")
|
|||
# append ZLIB and OpenSSL to our list of libraries to link
|
||||
target_link_libraries(${TARGET_NAME} ${ZLIB_LIBRARIES})
|
||||
|
||||
# call macro to link our dependencies and bubble them up via a property on our target
|
||||
link_shared_dependencies()
|
||||
# call macro to include our dependency includes and bubble them up via a property on our target
|
||||
include_dependency_includes()
|
||||
|
|
|
@ -189,6 +189,9 @@ void OctreePersistThread::backup() {
|
|||
quint64 intervalToBackup = _backupInterval * MSECS_TO_USECS;
|
||||
|
||||
if (sinceLastBackup > intervalToBackup) {
|
||||
qDebug() << "Time since last backup [" << sinceLastBackup << "] exceeds backup interval ["
|
||||
<< intervalToBackup << "] doing backup now...";
|
||||
|
||||
struct tm* localTime = localtime(&_lastPersistTime);
|
||||
|
||||
QString backupFileName;
|
||||
|
@ -213,6 +216,8 @@ void OctreePersistThread::backup() {
|
|||
} else {
|
||||
qDebug() << "ERROR in backing up persist file...";
|
||||
}
|
||||
|
||||
_lastBackup = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,5 +12,5 @@ endif (BULLET_FOUND)
|
|||
link_hifi_libraries(shared fbx entities)
|
||||
include_hifi_library_headers(fbx)
|
||||
|
||||
# call macro to link our dependencies and bubble them up via a property on our target
|
||||
link_shared_dependencies()
|
||||
# call macro to include our dependency includes and bubble them up via a property on our target
|
||||
include_dependency_includes()
|
||||
|
|
|
@ -5,12 +5,7 @@ setup_hifi_library(Widgets OpenGL Network Script)
|
|||
|
||||
include_glm()
|
||||
|
||||
link_hifi_libraries(shared gpu)
|
||||
link_hifi_libraries(animation fbx shared gpu)
|
||||
|
||||
if (WIN32)
|
||||
find_package(GLUT REQUIRED)
|
||||
include_directories(SYSTEM "${GLUT_INCLUDE_DIRS}")
|
||||
endif ()
|
||||
|
||||
# call macro to link our dependencies and bubble them up via a property on our target
|
||||
link_shared_dependencies()
|
||||
# call macro to include our dependency includes and bubble them up via a property on our target
|
||||
include_dependency_includes()
|
||||
|
|
|
@ -5,7 +5,8 @@ setup_hifi_library(Gui Network Script Widgets)
|
|||
|
||||
include_glm()
|
||||
|
||||
link_hifi_libraries(shared octree voxels fbx entities animation audio physics)
|
||||
link_hifi_libraries(shared octree voxels gpu model fbx entities animation audio physics metavoxels)
|
||||
|
||||
# call macro to include our dependency includes and bubble them up via a property on our target
|
||||
include_dependency_includes()
|
||||
|
||||
# call macro to link our dependencies and bubble them up via a property on our target
|
||||
link_shared_dependencies()
|
||||
|
|
|
@ -601,16 +601,20 @@ void ScriptEngine::stopTimer(QTimer *timer) {
|
|||
}
|
||||
|
||||
QUrl ScriptEngine::resolvePath(const QString& include) const {
|
||||
// first lets check to see if it's already a full URL
|
||||
QUrl url(include);
|
||||
// first lets check to see if it's already a full URL
|
||||
if (!url.scheme().isEmpty()) {
|
||||
return url;
|
||||
}
|
||||
|
||||
// we apparently weren't a fully qualified url, so, let's assume we're relative
|
||||
// to the original URL of our script
|
||||
QUrl parentURL(_fileNameString);
|
||||
|
||||
QUrl parentURL;
|
||||
if (_parentURL.isEmpty()) {
|
||||
parentURL = QUrl(_fileNameString);
|
||||
} else {
|
||||
parentURL = QUrl(_parentURL);
|
||||
}
|
||||
// if the parent URL's scheme is empty, then this is probably a local file...
|
||||
if (parentURL.scheme().isEmpty()) {
|
||||
parentURL = QUrl::fromLocalFile(_fileNameString);
|
||||
|
@ -643,6 +647,7 @@ void ScriptEngine::include(const QString& includeFile) {
|
|||
#else
|
||||
QString fileName = url.toLocalFile();
|
||||
#endif
|
||||
|
||||
QFile scriptFile(fileName);
|
||||
if (scriptFile.open(QFile::ReadOnly | QFile::Text)) {
|
||||
qDebug() << "Including file:" << fileName;
|
||||
|
|
|
@ -89,6 +89,8 @@ public:
|
|||
void setUserLoaded(bool isUserLoaded) { _isUserLoaded = isUserLoaded; }
|
||||
bool isUserLoaded() const { return _isUserLoaded; }
|
||||
|
||||
void setParentURL(const QString& parentURL) { _parentURL = parentURL; }
|
||||
|
||||
public slots:
|
||||
void loadURL(const QUrl& scriptURL);
|
||||
void stop();
|
||||
|
@ -120,6 +122,7 @@ signals:
|
|||
|
||||
protected:
|
||||
QString _scriptContents;
|
||||
QString _parentURL;
|
||||
bool _isFinished;
|
||||
bool _isRunning;
|
||||
bool _isInitialized;
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
set(TARGET_NAME shared)
|
||||
|
||||
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
||||
setup_hifi_library(Network Widgets)
|
||||
# TODO: there isn't really a good reason to have Script linked here - let's get what is requiring it out (RegisteredMetaTypes.cpp)
|
||||
setup_hifi_library(Network Script Widgets)
|
||||
|
||||
# call macro to link our dependencies and bubble them up via a property on our target
|
||||
link_shared_dependencies()
|
||||
# call macro to include our dependency includes and bubble them up via a property on our target
|
||||
include_dependency_includes()
|
|
@ -33,7 +33,7 @@ AABox::AABox(const glm::vec3& corner, const glm::vec3& dimensions) :
|
|||
_corner(corner), _scale(dimensions) {
|
||||
};
|
||||
|
||||
AABox::AABox() : _corner(0.0f, 0.0f, 0.0f), _scale(0.0f, 0.0f, 0.0f) {
|
||||
AABox::AABox() : _corner(std::numeric_limits<float>::infinity()), _scale(0.0f) {
|
||||
};
|
||||
|
||||
glm::vec3 AABox::calcCenter() const {
|
||||
|
@ -69,7 +69,7 @@ glm::vec3 AABox::getVertex(BoxVertex vertex) const {
|
|||
return _corner + glm::vec3(0, 0, _scale.z);
|
||||
case TOP_RIGHT_FAR:
|
||||
return _corner + glm::vec3(0, _scale.y, _scale.z);
|
||||
default: //quiet windows warnings
|
||||
default: //quiet windows warnings
|
||||
case TOP_LEFT_FAR:
|
||||
return _corner + _scale;
|
||||
}
|
||||
|
@ -357,7 +357,7 @@ glm::vec3 AABox::getClosestPointOnFace(const glm::vec3& point, BoxFace face) con
|
|||
return glm::clamp(point, glm::vec3(_corner.x, _corner.y, _corner.z),
|
||||
glm::vec3(_corner.x + _scale.z, _corner.y + _scale.y, _corner.z));
|
||||
|
||||
default: //quiet windows warnings
|
||||
default: //quiet windows warnings
|
||||
case MAX_Z_FACE:
|
||||
return glm::clamp(point, glm::vec3(_corner.x, _corner.y, _corner.z + _scale.z),
|
||||
glm::vec3(_corner.x + _scale.x, _corner.y + _scale.y, _corner.z + _scale.z));
|
||||
|
@ -438,7 +438,7 @@ glm::vec4 AABox::getPlane(BoxFace face) const {
|
|||
case MIN_Y_FACE: return glm::vec4(0.0f, -1.0f, 0.0f, _corner.y);
|
||||
case MAX_Y_FACE: return glm::vec4(0.0f, 1.0f, 0.0f, -_corner.y - _scale.y);
|
||||
case MIN_Z_FACE: return glm::vec4(0.0f, 0.0f, -1.0f, _corner.z);
|
||||
default: //quiet windows warnings
|
||||
default: //quiet windows warnings
|
||||
case MAX_Z_FACE: return glm::vec4(0.0f, 0.0f, 1.0f, -_corner.z - _scale.z);
|
||||
}
|
||||
}
|
||||
|
@ -450,7 +450,7 @@ BoxFace AABox::getOppositeFace(BoxFace face) {
|
|||
case MIN_Y_FACE: return MAX_Y_FACE;
|
||||
case MAX_Y_FACE: return MIN_Y_FACE;
|
||||
case MIN_Z_FACE: return MAX_Z_FACE;
|
||||
default: //quiet windows warnings
|
||||
default: //quiet windows warnings
|
||||
case MAX_Z_FACE: return MIN_Z_FACE;
|
||||
}
|
||||
}
|
||||
|
@ -470,3 +470,18 @@ AABox AABox::clamp(float min, float max) const {
|
|||
|
||||
return AABox(clampedCorner, clampedScale);
|
||||
}
|
||||
|
||||
AABox& AABox::operator += (const glm::vec3& point) {
|
||||
_corner = glm::min(_corner, point);
|
||||
_scale = glm::max(_scale, point - _corner);
|
||||
|
||||
return (*this);
|
||||
}
|
||||
|
||||
AABox& AABox::operator += (const AABox& box) {
|
||||
if (!box.isInvalid()) {
|
||||
(*this) += box._corner;
|
||||
_scale = glm::max(_scale, box.calcTopFarLeft() - _corner);
|
||||
}
|
||||
return (*this);
|
||||
}
|
||||
|
|
|
@ -72,6 +72,11 @@ public:
|
|||
AABox clamp(const glm::vec3& min, const glm::vec3& max) const;
|
||||
AABox clamp(float min, float max) const;
|
||||
|
||||
AABox& operator += (const glm::vec3& point);
|
||||
AABox& operator += (const AABox& box);
|
||||
|
||||
bool isInvalid() const { return _corner == glm::vec3(std::numeric_limits<float>::infinity()); }
|
||||
|
||||
private:
|
||||
glm::vec3 getClosestPointOnFace(const glm::vec3& point, BoxFace face) const;
|
||||
glm::vec3 getClosestPointOnFace(const glm::vec4& origin, const glm::vec4& direction, BoxFace face) const;
|
||||
|
|
|
@ -98,6 +98,14 @@ std::ostream& operator<<(std::ostream& s, const CapsuleShape& capsule) {
|
|||
#ifndef QT_NO_DEBUG_STREAM
|
||||
#include <QDebug>
|
||||
|
||||
QDebug& operator<<(QDebug& dbg, const glm::vec2& v) {
|
||||
dbg.nospace() << "{type='glm::vec2'"
|
||||
", x=" << v.x <<
|
||||
", y=" << v.y <<
|
||||
"}";
|
||||
return dbg;
|
||||
}
|
||||
|
||||
QDebug& operator<<(QDebug& dbg, const glm::vec3& v) {
|
||||
dbg.nospace() << "{type='glm::vec3'"
|
||||
", x=" << v.x <<
|
||||
|
|
|
@ -49,6 +49,7 @@ std::ostream& operator<<(std::ostream& s, const CapsuleShape& capsule);
|
|||
#ifndef QT_NO_DEBUG_STREAM
|
||||
class QDebug;
|
||||
// Add support for writing these to qDebug().
|
||||
QDebug& operator<<(QDebug& s, const glm::vec2& v);
|
||||
QDebug& operator<<(QDebug& s, const glm::vec3& v);
|
||||
QDebug& operator<<(QDebug& s, const glm::quat& q);
|
||||
QDebug& operator<<(QDebug& s, const glm::mat4& m);
|
||||
|
|
|
@ -63,20 +63,20 @@ public:
|
|||
void setIdentity();
|
||||
|
||||
const Vec3& getTranslation() const;
|
||||
void setTranslation(const Vec3& translation);
|
||||
void preTranslate(const Vec3& translation);
|
||||
void postTranslate(const Vec3& translation);
|
||||
void setTranslation(const Vec3& translation); // [new this] = [translation] * [this.rotation] * [this.scale]
|
||||
void preTranslate(const Vec3& translation); // [new this] = [translation] * [this]
|
||||
void postTranslate(const Vec3& translation); // [new this] = [this] * [translation] equivalent to glTranslate
|
||||
|
||||
const Quat& getRotation() const;
|
||||
void setRotation(const Quat& rotation);
|
||||
void preRotate(const Quat& rotation);
|
||||
void postRotate(const Quat& rotation);
|
||||
void setRotation(const Quat& rotation); // [new this] = [this.translation] * [rotation] * [this.scale]
|
||||
void preRotate(const Quat& rotation); // [new this] = [rotation] * [this]
|
||||
void postRotate(const Quat& rotation); // [new this] = [this] * [rotation] equivalent to glRotate
|
||||
|
||||
const Vec3& getScale() const;
|
||||
void setScale(float scale);
|
||||
void setScale(const Vec3& scale);
|
||||
void postScale(float scale);
|
||||
void postScale(const Vec3& scale);
|
||||
void setScale(const Vec3& scale); // [new this] = [this.translation] * [this.rotation] * [scale]
|
||||
void postScale(float scale); // [new this] = [this] * [scale] equivalent to glScale
|
||||
void postScale(const Vec3& scale); // [new this] = [this] * [scale] equivalent to glScale
|
||||
|
||||
bool isIdentity() const { return (_flags & ~Flags(FLAG_CACHE_INVALID_BITSET)).none(); }
|
||||
bool isTranslating() const { return _flags[FLAG_TRANSLATION]; }
|
||||
|
|
|
@ -14,5 +14,5 @@ include_directories(SYSTEM "${ZLIB_INCLUDE_DIRS}")
|
|||
# add it to our list of libraries to link
|
||||
target_link_libraries(${TARGET_NAME} ${ZLIB_LIBRARIES})
|
||||
|
||||
# call macro to link our dependencies and bubble them up via a property on our target
|
||||
link_shared_dependencies()
|
||||
# call macro to include our dependency includes and bubble them up via a property on our target
|
||||
include_dependency_includes()
|
|
@ -7,4 +7,4 @@ include_glm()
|
|||
# link in the shared libraries
|
||||
link_hifi_libraries(shared audio networking)
|
||||
|
||||
link_shared_dependencies()
|
||||
include_dependency_includes()
|
|
@ -5,4 +5,4 @@ setup_hifi_project()
|
|||
# link in the shared libraries
|
||||
link_hifi_libraries(shared networking)
|
||||
|
||||
link_shared_dependencies()
|
||||
include_dependency_includes()
|
|
@ -9,4 +9,4 @@ setup_hifi_project(Network Script Widgets)
|
|||
# link in the shared libraries
|
||||
link_hifi_libraries(metavoxels networking shared)
|
||||
|
||||
link_shared_dependencies()
|
||||
include_dependency_includes()
|
|
@ -5,4 +5,4 @@ setup_hifi_project()
|
|||
# link in the shared libraries
|
||||
link_hifi_libraries(shared networking)
|
||||
|
||||
link_shared_dependencies()
|
||||
include_dependency_includes()
|
|
@ -5,6 +5,6 @@ setup_hifi_project(Script Network)
|
|||
include_glm()
|
||||
|
||||
# link in the shared libraries
|
||||
link_hifi_libraries(shared octree voxels fbx metavoxels networking entities avatars audio animation script-engine physics)
|
||||
link_hifi_libraries(shared octree voxels gpu model fbx metavoxels networking entities avatars audio animation script-engine physics)
|
||||
|
||||
link_shared_dependencies()
|
||||
include_dependency_includes()
|
||||
|
|
|
@ -7,4 +7,4 @@ include_bullet()
|
|||
|
||||
link_hifi_libraries(shared physics)
|
||||
|
||||
link_shared_dependencies()
|
||||
include_dependency_includes()
|
||||
|
|
|
@ -7,4 +7,4 @@ include_glm()
|
|||
# link in the shared libraries
|
||||
link_hifi_libraries(shared)
|
||||
|
||||
link_shared_dependencies()
|
||||
include_dependency_includes()
|
|
@ -2,3 +2,4 @@
|
|||
add_subdirectory(bitstream2json)
|
||||
add_subdirectory(json2bitstream)
|
||||
add_subdirectory(mtc)
|
||||
add_subdirectory(scribe)
|
||||
|
|
|
@ -5,4 +5,4 @@ include_glm()
|
|||
|
||||
link_hifi_libraries(metavoxels)
|
||||
|
||||
link_shared_dependencies()
|
||||
include_dependency_includes()
|
|
@ -5,4 +5,4 @@ include_glm()
|
|||
|
||||
link_hifi_libraries(metavoxels)
|
||||
|
||||
link_shared_dependencies()
|
||||
include_dependency_includes()
|
|
@ -1,4 +1,4 @@
|
|||
set(TARGET_NAME mtc)
|
||||
setup_hifi_project()
|
||||
|
||||
link_shared_dependencies()
|
||||
include_dependency_includes()
|
5
tools/scribe/CMakeLists.txt
Executable file
5
tools/scribe/CMakeLists.txt
Executable file
|
@ -0,0 +1,5 @@
|
|||
set(TARGET_NAME scribe)
|
||||
setup_hifi_project()
|
||||
|
||||
# call macro to include our dependency includes and bubble them up via a property on our target
|
||||
include_dependency_includes()
|
989
tools/scribe/src/TextTemplate.cpp
Executable file
989
tools/scribe/src/TextTemplate.cpp
Executable file
|
@ -0,0 +1,989 @@
|
|||
//
|
||||
// TextTemplate.cpp
|
||||
// tools/shaderScribe/src
|
||||
//
|
||||
// Created by Sam Gateau on 12/15/2014.
|
||||
// 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
|
||||
#include "TextTemplate.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
|
||||
typedef TextTemplate::Block::Pointer BlockPointer;
|
||||
typedef TextTemplate::Config::Pointer ConfigPointer;
|
||||
typedef TextTemplate::Pointer TextTemplatePointer;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
TextTemplate::Config::Config() :
|
||||
_includes(),
|
||||
_funcs(),
|
||||
_logStream(&std::cout),
|
||||
_numErrors(0),
|
||||
_includerCallback(TextTemplate::loadFile) {
|
||||
_paths.push_back("");
|
||||
}
|
||||
|
||||
const TextTemplatePointer TextTemplate::Config::addInclude(const ConfigPointer& config, const char* include) {
|
||||
if (!config) {
|
||||
return TextTemplatePointer();
|
||||
}
|
||||
|
||||
TextTemplatePointer included = config->findInclude(include);
|
||||
if (included) {
|
||||
return included;
|
||||
}
|
||||
|
||||
// INcluded doest exist yet so let's try to create it
|
||||
String includeStream;
|
||||
if (config->_includerCallback(config, include, includeStream)) {
|
||||
// ok, then create a new Template on the include file with this as lib
|
||||
included = TextTemplatePointer(new TextTemplate(include, config));
|
||||
|
||||
std::stringstream src(includeStream);
|
||||
|
||||
int nbErrors = included->parse(src);
|
||||
if (nbErrors > 0) {
|
||||
included->logError(included->_root, "File failed to parse, not included");
|
||||
return TextTemplatePointer();
|
||||
}
|
||||
config->_includes.insert(Includes::value_type(include, included));
|
||||
return included;
|
||||
}
|
||||
|
||||
return TextTemplatePointer();
|
||||
}
|
||||
|
||||
const TextTemplatePointer TextTemplate::Config::findInclude(const char* include) {
|
||||
Includes::iterator includeIt = _includes.find(String(include));
|
||||
if (includeIt != _includes.end()) {
|
||||
return (*includeIt).second;
|
||||
} else {
|
||||
return TextTemplatePointer();
|
||||
}
|
||||
}
|
||||
|
||||
void TextTemplate::Config::addIncludePath(const char* path) {
|
||||
_paths.push_back(String(path));
|
||||
}
|
||||
|
||||
bool TextTemplate::loadFile(const ConfigPointer& config, const char* filename, String& source) {
|
||||
String sourceFile(filename);
|
||||
String fullfilename;
|
||||
for (unsigned int i = 0; i < config->_paths.size(); i++) {
|
||||
fullfilename = config->_paths[i] + sourceFile;
|
||||
std::ifstream ifs;
|
||||
ifs.open(fullfilename.c_str());
|
||||
if (ifs.is_open()) {
|
||||
std::string str((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
|
||||
source = str;
|
||||
ifs.close();
|
||||
return (source.length() > 0);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
TextTemplate::Funcs::Funcs() :
|
||||
_funcs() {
|
||||
}
|
||||
|
||||
TextTemplate::Funcs::~Funcs() {
|
||||
}
|
||||
|
||||
|
||||
const BlockPointer TextTemplate::Funcs::findFunc(const char* func) {
|
||||
map::iterator it = _funcs.find(String(func));
|
||||
if (it != _funcs.end()) {
|
||||
return (*it).second;
|
||||
} else {
|
||||
return BlockPointer();
|
||||
}
|
||||
}
|
||||
|
||||
const BlockPointer TextTemplate::Funcs::addFunc(const char* func, const BlockPointer& funcBlock) {
|
||||
BlockPointer included = findFunc(func);
|
||||
if (! included) {
|
||||
_funcs.insert(map::value_type(func, funcBlock));
|
||||
}
|
||||
return included;
|
||||
}
|
||||
|
||||
void TextTemplate::Block::addNewBlock(const Pointer& parent, const Pointer& block) {
|
||||
if (parent) {
|
||||
parent->blocks.push_back(block);
|
||||
block->parent = parent;
|
||||
}
|
||||
}
|
||||
|
||||
const BlockPointer& TextTemplate::Block::getCurrentBlock(const Pointer& block) {
|
||||
if (block && block->command.isBlockEnd()) {
|
||||
return block->parent;
|
||||
} else {
|
||||
return block;
|
||||
}
|
||||
}
|
||||
|
||||
void TextTemplate::logError(const Block::Pointer& block, const char* fmt, ...) {
|
||||
va_list argp;
|
||||
va_start(argp, fmt);
|
||||
|
||||
char buff[256];
|
||||
sprintf(buff, fmt, argp);
|
||||
|
||||
_numErrors++;
|
||||
log() << block->sourceName << " Error >>" << buff << std::endl;
|
||||
|
||||
int level = 1;
|
||||
displayTree(std::cerr, level);
|
||||
|
||||
}
|
||||
|
||||
bool TextTemplate::grabUntilBeginTag(std::istream* str, std::string& grabbed, Tag::Type& tagType) {
|
||||
std::stringstream dst;
|
||||
while (!str->eof()) {
|
||||
|
||||
std::string datatoken;
|
||||
getline((*str), datatoken, Tag::BEGIN);
|
||||
dst << datatoken;
|
||||
|
||||
char next = str->peek();
|
||||
if (next == Tag::VAR) {
|
||||
tagType = Tag::VARIABLE;
|
||||
grabbed = dst.str();
|
||||
str->get(); // skip tag char
|
||||
return true;
|
||||
} else if (next == Tag::COM) {
|
||||
tagType = Tag::COMMAND;
|
||||
grabbed = dst.str();
|
||||
str->get(); // skip tag char
|
||||
return true;
|
||||
} else if (next == Tag::REM) {
|
||||
tagType = Tag::REMARK;
|
||||
grabbed = dst.str();
|
||||
str->get(); // skip tag char
|
||||
return true;
|
||||
} else {
|
||||
if (!str->eof()) {
|
||||
// false positive, just found the Tag::BEGIN without consequence
|
||||
dst << Tag::BEGIN;
|
||||
// keep searching
|
||||
} else {
|
||||
// end of the file finishing with no tag
|
||||
tagType = Tag::INVALID;
|
||||
grabbed = dst.str();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TextTemplate::grabUntilEndTag(std::istream* str, std::string& grabbed, Tag::Type& tagType) {
|
||||
std::stringstream dst;
|
||||
|
||||
// preEnd char depends on tag type
|
||||
char preEnd = Tag::COM;
|
||||
if (tagType == Tag::VARIABLE) {
|
||||
preEnd = Tag::VAR;
|
||||
} else if (tagType == Tag::REMARK) {
|
||||
preEnd = Tag::REM;
|
||||
}
|
||||
|
||||
while (!str->eof()) {
|
||||
// looking for the end of the tag means find the next preEnd
|
||||
std::string dataToken;
|
||||
getline((*str), dataToken, preEnd);
|
||||
char end = str->peek();
|
||||
|
||||
dst << dataToken;
|
||||
|
||||
// and if the next char is Tag::END then that's it
|
||||
if (end == Tag::END) {
|
||||
grabbed = dst.str();
|
||||
str->get(); // eat the Tag::END
|
||||
return true;
|
||||
} else {
|
||||
// false positive, keep on searching
|
||||
dst << preEnd;
|
||||
}
|
||||
}
|
||||
grabbed = dst.str();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TextTemplate::stepForward(std::istream* str, std::string& grabbed, std::string& tag, Tag::Type& tagType,
|
||||
Tag::Type& nextTagType) {
|
||||
if (str->eof()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_steppingStarted) {
|
||||
_steppingStarted = true;
|
||||
return grabUntilBeginTag(str, grabbed, nextTagType);
|
||||
}
|
||||
|
||||
// Read from the last opening Tag::BEGIN captured to the next Tag::END
|
||||
if (grabUntilEndTag(str, tag, tagType)) {
|
||||
// skip trailing space and new lines only after Command or Remark tag block
|
||||
if ((tagType == Tag::COMMAND) || (tagType == Tag::REMARK)) {
|
||||
while (!str->eof()) {
|
||||
char c = str->peek();
|
||||
if ((c == ' ') || (c == '\t') || (c == '\n')) {
|
||||
str->get();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
grabUntilBeginTag(str, grabbed, nextTagType);
|
||||
}
|
||||
|
||||
return true; //hasElement;
|
||||
}
|
||||
|
||||
const BlockPointer TextTemplate::processStep(const BlockPointer& block, std::string& grabbed, std::string& tag,
|
||||
Tag::Type& tagType) {
|
||||
switch (tagType) {
|
||||
case Tag::INVALID:
|
||||
block->ostr << grabbed;
|
||||
return block;
|
||||
break;
|
||||
case Tag::VARIABLE:
|
||||
return processStepVar(block, grabbed, tag);
|
||||
break;
|
||||
case Tag::COMMAND:
|
||||
return processStepCommand(block, grabbed, tag);
|
||||
break;
|
||||
case Tag::REMARK:
|
||||
return processStepRemark(block, grabbed, tag);
|
||||
break;
|
||||
}
|
||||
|
||||
logError(block, "Invalid tag");
|
||||
return block;
|
||||
}
|
||||
|
||||
bool TextTemplate::grabFirstToken(String& src, String& token, String& reminder) {
|
||||
bool goOn = true;
|
||||
std::string::size_type i = 0;
|
||||
while (goOn && (i < src.length())) {
|
||||
char c = src[i];
|
||||
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || (c == '_') || (c == '.') || (c == '[') || (c == ']')) {
|
||||
token += c;
|
||||
} else {
|
||||
if (!token.empty()) {
|
||||
reminder = src.substr(i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return (!token.empty());
|
||||
}
|
||||
|
||||
bool TextTemplate::convertExpressionToArguments(String& src, std::vector< String >& arguments) {
|
||||
std::stringstream str(src);
|
||||
String token;
|
||||
|
||||
while (!str.eof()) {
|
||||
str >> token;
|
||||
if (!str.fail()) {
|
||||
arguments.push_back(token);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextTemplate::convertExpressionToDefArguments(String& src, std::vector< String >& arguments) {
|
||||
if (src.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::stringstream argstr(src);
|
||||
std::stringstream dest;
|
||||
bool inVar = false;
|
||||
while (!argstr.eof()) {
|
||||
// parse the value of a var, try to find a VAR, so look for the pattern BEGIN,VAR ... VAR,END
|
||||
String token;
|
||||
char tag;
|
||||
if (!inVar) {
|
||||
getline(argstr, token, Tag::BEGIN);
|
||||
dest << token;
|
||||
tag = argstr.peek();
|
||||
} else {
|
||||
getline(argstr, token, Tag::END);
|
||||
dest << token;
|
||||
tag = token.back();
|
||||
}
|
||||
|
||||
if (tag == Tag::VAR) {
|
||||
if (!inVar) {
|
||||
// real var coming:
|
||||
arguments.push_back(dest.str());
|
||||
inVar = true;
|
||||
} else {
|
||||
// real var over
|
||||
arguments.push_back(dest.str());
|
||||
inVar = false;
|
||||
}
|
||||
} else {
|
||||
if (argstr.eof()) {
|
||||
arguments.push_back(dest.str());
|
||||
} else {
|
||||
// put back the tag char stolen
|
||||
dest << (!inVar ? Tag::BEGIN : Tag::END);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextTemplate::convertExpressionToFuncArguments(String& src, std::vector< String >& arguments) {
|
||||
if (src.empty()) {
|
||||
return false;
|
||||
}
|
||||
std::stringstream streamSrc(src);
|
||||
String params;
|
||||
getline(streamSrc, params, '(');
|
||||
getline(streamSrc, params, ')');
|
||||
if (params.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::stringstream str(params);
|
||||
String token;
|
||||
int nbTokens = 0;
|
||||
while (!str.eof()) {
|
||||
char c = str.peek();
|
||||
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || (c == '_') || (c == '.') || (c == Tag::VAR) || (c == '[') || (c == ']')) {
|
||||
token += c;
|
||||
} else if (c == ',') {
|
||||
if (!token.empty()) {
|
||||
arguments.push_back(token);
|
||||
nbTokens++;
|
||||
}
|
||||
token.clear();
|
||||
}
|
||||
|
||||
str.get();
|
||||
}
|
||||
|
||||
if (token.size()) {
|
||||
arguments.push_back(token);
|
||||
nbTokens++;
|
||||
}
|
||||
|
||||
return (nbTokens > 0);
|
||||
}
|
||||
|
||||
const BlockPointer TextTemplate::processStepVar(const BlockPointer& block, String& grabbed, String& tag) {
|
||||
// then look at the define
|
||||
String var = tag;
|
||||
String varName;
|
||||
String val;
|
||||
|
||||
if (grabFirstToken(var, varName, val)) {
|
||||
if (!varName.empty()) {
|
||||
BlockPointer parent = Block::getCurrentBlock(block);
|
||||
|
||||
// Add a new BLock
|
||||
BlockPointer newBlock = BlockPointer(new Block(_root->sourceName));
|
||||
(newBlock->ostr) << grabbed;
|
||||
|
||||
newBlock->command.type = Command::VAR;
|
||||
|
||||
newBlock->command.arguments.push_back(varName);
|
||||
|
||||
if (!val.empty()) {
|
||||
convertExpressionToFuncArguments(val, newBlock->command.arguments);
|
||||
}
|
||||
|
||||
Block::addNewBlock(parent, newBlock);
|
||||
|
||||
// dive in the new block
|
||||
return newBlock;
|
||||
}
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
const BlockPointer TextTemplate::processStepCommand(const BlockPointer& block, String& grabbed, String& tag) {
|
||||
// Grab the command name
|
||||
String command = tag;
|
||||
String commandName;
|
||||
|
||||
if (grabFirstToken(command, commandName, command)) {
|
||||
if (commandName.compare("def") == 0) {
|
||||
return processStepDef(block, grabbed, command);
|
||||
} else if (commandName.compare("if") == 0) {
|
||||
return processStepCommandIf(block, grabbed, command);
|
||||
} else if (commandName.compare("endif") == 0) {
|
||||
return processStepCommandEndIf(block, grabbed, command);
|
||||
} else if (commandName.compare("else") == 0) {
|
||||
return processStepCommandElse(block, grabbed, command);
|
||||
} else if (commandName.compare("elif") == 0) {
|
||||
return processStepCommandElif(block, grabbed, command);
|
||||
} else if (commandName.compare("include") == 0) {
|
||||
return processStepInclude(block, grabbed, command);
|
||||
} else if (commandName.compare("func") == 0) {
|
||||
return processStepFunc(block, grabbed, command);
|
||||
} else if (commandName.compare("endfunc") == 0) {
|
||||
return processStepEndFunc(block, grabbed, command);
|
||||
}
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
const BlockPointer TextTemplate::processStepDef(const BlockPointer& block, String& grabbed, String& def) {
|
||||
// then look at the define
|
||||
String varName;
|
||||
String val;
|
||||
|
||||
if (!grabFirstToken(def, varName, val)) {
|
||||
logError(block, "Invalid Var name in a <def> tag");
|
||||
return block;
|
||||
}
|
||||
|
||||
BlockPointer parent = Block::getCurrentBlock(block);
|
||||
|
||||
// Add a new BLock
|
||||
BlockPointer newBlock = BlockPointer(new Block(_root->sourceName));
|
||||
(newBlock->ostr) << grabbed;
|
||||
|
||||
newBlock->command.type = Command::DEF;
|
||||
newBlock->command.arguments.push_back(varName);
|
||||
if (!val.empty()) {
|
||||
// loose first character which should be a white space
|
||||
val = val.substr(val.find_first_not_of(' '));
|
||||
convertExpressionToDefArguments(val, newBlock->command.arguments);
|
||||
}
|
||||
|
||||
Block::addNewBlock(parent, newBlock);
|
||||
|
||||
// dive in the new block
|
||||
return newBlock;
|
||||
}
|
||||
|
||||
|
||||
const BlockPointer TextTemplate::processStepCommandIf(const BlockPointer& block, String& grabbed, String& expression) {
|
||||
BlockPointer parent = Block::getCurrentBlock(block);
|
||||
|
||||
// Add a new BLock depth
|
||||
BlockPointer newIfBlock = BlockPointer(new Block(_root->sourceName));
|
||||
newIfBlock->command.type = Command::IFBLOCK;
|
||||
|
||||
Block::addNewBlock(parent, newIfBlock);
|
||||
|
||||
BlockPointer newBlock = BlockPointer(new Block(_root->sourceName));
|
||||
(newBlock->ostr) << grabbed;
|
||||
|
||||
newBlock->command.type = Command::IF;
|
||||
convertExpressionToArguments(expression, newBlock->command.arguments);
|
||||
|
||||
Block::addNewBlock(newIfBlock, newBlock);
|
||||
|
||||
// dive in the new If branch
|
||||
return newBlock;
|
||||
}
|
||||
|
||||
const BlockPointer TextTemplate::processStepCommandEndIf(const BlockPointer& block, String& grabbed, String& expression) {
|
||||
BlockPointer parent = Block::getCurrentBlock(block);
|
||||
|
||||
// are we in a if block ?
|
||||
if ((parent->command.type == Command::IF)
|
||||
|| (parent->command.type == Command::ELIF)
|
||||
|| (parent->command.type == Command::ELSE)) {
|
||||
BlockPointer newBlock = BlockPointer(new Block(_root->sourceName));
|
||||
(newBlock->ostr) << grabbed;
|
||||
|
||||
newBlock->command.type = Command::ENDIF;
|
||||
newBlock->command.arguments.push_back(expression);
|
||||
|
||||
Block::addNewBlock(parent->parent->parent, newBlock);
|
||||
|
||||
return newBlock;
|
||||
} else {
|
||||
logError(block, "Invalid <endif> block, not in a <if> block");
|
||||
return block;
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
const BlockPointer TextTemplate::processStepCommandElse(const BlockPointer& block, String& grabbed, String& expression) {
|
||||
BlockPointer parent = Block::getCurrentBlock(block);
|
||||
|
||||
// are we in a if block ?
|
||||
if ((parent->command.type == Command::IF)
|
||||
|| (parent->command.type == Command::ELIF)) {
|
||||
// All good go back to the IfBlock
|
||||
parent = parent->parent;
|
||||
|
||||
// Add a new BLock depth
|
||||
BlockPointer newBlock = BlockPointer(new Block(_root->sourceName));
|
||||
newBlock->ostr << grabbed;
|
||||
newBlock->command.type = Command::ELSE;
|
||||
newBlock->command.arguments.push_back(expression);
|
||||
|
||||
Block::addNewBlock(parent, newBlock);
|
||||
|
||||
// dive in the new block
|
||||
return newBlock;
|
||||
|
||||
} else if ((block)->command.type == Command::ELSE) {
|
||||
logError(block, "Invalid <elif> block, in a <if> block but after a <else> block");
|
||||
return block;
|
||||
} else {
|
||||
logError(block, "Invalid <elif> block, not in a <if> block");
|
||||
return block;
|
||||
}
|
||||
}
|
||||
|
||||
const BlockPointer TextTemplate::processStepCommandElif(const BlockPointer& block, String& grabbed, String& expression) {
|
||||
BlockPointer parent = Block::getCurrentBlock(block);
|
||||
|
||||
// are we in a if block ?
|
||||
if ((parent->command.type == Command::IF)
|
||||
|| (parent->command.type == Command::ELIF)) {
|
||||
// All good go back to the IfBlock
|
||||
parent = parent->parent;
|
||||
|
||||
// Add a new BLock depth
|
||||
BlockPointer newBlock = BlockPointer(new Block(_root->sourceName));
|
||||
(newBlock->ostr) << grabbed;
|
||||
|
||||
newBlock->command.type = Command::ELIF;
|
||||
convertExpressionToArguments(expression, newBlock->command.arguments);
|
||||
|
||||
Block::addNewBlock(parent, newBlock);
|
||||
|
||||
// dive in the new block
|
||||
return newBlock;
|
||||
|
||||
} else if (parent->command.type == Command::ELSE) {
|
||||
logError(block, "Invalid <elif> block, in a <if> block but after a <else> block");
|
||||
return block;
|
||||
} else {
|
||||
logError(block, "Invalid <elif> block, not in a <if> block");
|
||||
return block;
|
||||
}
|
||||
}
|
||||
|
||||
const BlockPointer TextTemplate::processStepRemark(const BlockPointer& block, String& grabbed, String& tag) {
|
||||
// nothing to do :)
|
||||
|
||||
// no need to think, let's just add the grabbed text
|
||||
(block->ostr) << grabbed;
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
const BlockPointer TextTemplate::processStepInclude(const BlockPointer& block, String& grabbed, String& include) {
|
||||
BlockPointer parent = Block::getCurrentBlock(block);
|
||||
|
||||
// Add a new BLock
|
||||
BlockPointer newBlock = BlockPointer(new Block(_root->sourceName));
|
||||
(newBlock->ostr) << grabbed;
|
||||
|
||||
newBlock->command.type = Command::INCLUDE;
|
||||
convertExpressionToArguments(include, newBlock->command.arguments);
|
||||
|
||||
Block::addNewBlock(parent, newBlock);
|
||||
|
||||
TextTemplatePointer inc = Config::addInclude(_config, newBlock->command.arguments.front().c_str());
|
||||
if (!inc) {
|
||||
logError(newBlock, "Failed to include %s", newBlock->command.arguments.front().c_str());
|
||||
}
|
||||
|
||||
// dive in the new block
|
||||
return newBlock;
|
||||
}
|
||||
|
||||
const BlockPointer TextTemplate::processStepFunc(const BlockPointer& block, String& grabbed, String& func) {
|
||||
// then look at the define
|
||||
String varName;
|
||||
String var;
|
||||
|
||||
if (!grabFirstToken(func, varName, var)) {
|
||||
logError(block, "Invalid func name <%s> in a <func> block", func.c_str());
|
||||
return block;
|
||||
}
|
||||
|
||||
// DOes the func already exists ?
|
||||
if (_config->_funcs.findFunc(varName.c_str())) {
|
||||
logError(block, "Declaring a new func named <%s> already exists", func.c_str());
|
||||
return block;
|
||||
}
|
||||
|
||||
BlockPointer parent = Block::getCurrentBlock(block);
|
||||
|
||||
// Add a new BLock
|
||||
BlockPointer newBlock = BlockPointer(new Block(_root->sourceName));
|
||||
(newBlock->ostr) << grabbed;
|
||||
|
||||
newBlock->command.type = Command::FUNC;
|
||||
newBlock->command.arguments.push_back(varName);
|
||||
convertExpressionToFuncArguments(var, newBlock->command.arguments);
|
||||
|
||||
_config->_funcs.addFunc(varName.c_str(), newBlock);
|
||||
|
||||
Block::addNewBlock(parent, newBlock);
|
||||
|
||||
// dive in the new block
|
||||
return newBlock;
|
||||
}
|
||||
|
||||
const BlockPointer TextTemplate::processStepEndFunc(const BlockPointer& block, String& grabbed, String& tag) {
|
||||
BlockPointer parent = Block::getCurrentBlock(block);
|
||||
|
||||
// are we in a func block ?
|
||||
if (parent->command.type == Command::FUNC) {
|
||||
// Make sure the FUnc has been declared properly
|
||||
BlockPointer funcBlock = _config->_funcs.findFunc(parent->command.arguments.front().c_str());
|
||||
if (funcBlock != parent) {
|
||||
logError(block, "Mismatching <func> blocks");
|
||||
return BlockPointer();
|
||||
}
|
||||
|
||||
// Everything is cool , so let's unplugg the FUnc block from this tree and just put the EndFunc block
|
||||
|
||||
BlockPointer newBlock = BlockPointer(new Block(_root->sourceName));
|
||||
(newBlock->ostr) << grabbed;
|
||||
|
||||
newBlock->command.type = Command::ENDFUNC;
|
||||
convertExpressionToArguments(tag, newBlock->command.arguments);
|
||||
|
||||
newBlock->parent = parent->parent;
|
||||
|
||||
parent->parent->blocks.back() = newBlock;
|
||||
|
||||
parent->parent = 0;
|
||||
|
||||
// dive in the new block
|
||||
return newBlock;
|
||||
} else {
|
||||
logError(block, "Invalid <endfunc> block, not in a <func> block");
|
||||
return BlockPointer();
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
TextTemplate::TextTemplate(const String& name, const ConfigPointer& externalConfig) :
|
||||
_config(externalConfig),
|
||||
_root(new Block(name)),
|
||||
_numErrors(0),
|
||||
_steppingStarted(false) {
|
||||
}
|
||||
|
||||
TextTemplate::~TextTemplate() {
|
||||
}
|
||||
|
||||
int TextTemplate::scribe(std::ostream& dst, std::istream& src, Vars& vars) {
|
||||
int nbErrors = parse(src);
|
||||
|
||||
if (nbErrors == 0) {
|
||||
return generate(dst, vars);
|
||||
}
|
||||
return nbErrors;
|
||||
}
|
||||
|
||||
int TextTemplate::generateTree(std::ostream& dst, const BlockPointer& block, Vars& vars) {
|
||||
BlockPointer newCurrentBlock;
|
||||
int numPasses = evalBlockGeneration(dst, block, vars, newCurrentBlock);
|
||||
for (int passNum= 0; passNum < numPasses; passNum++) {
|
||||
dst << newCurrentBlock->ostr.str();
|
||||
|
||||
for (auto child : newCurrentBlock->blocks) {
|
||||
generateTree(dst, child, vars);
|
||||
}
|
||||
}
|
||||
|
||||
return _numErrors;
|
||||
}
|
||||
|
||||
int TextTemplate::evalBlockGeneration(std::ostream& dst, const BlockPointer& block, Vars& vars, BlockPointer& branch) {
|
||||
switch (block->command.type) {
|
||||
case Command::BLOCK: {
|
||||
branch = block;
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case Command::VAR: {
|
||||
Vars::iterator it = vars.find(block->command.arguments.front());
|
||||
if (it != vars.end()) {
|
||||
dst << (*it).second;
|
||||
} else {
|
||||
BlockPointer funcBlock = _config->_funcs.findFunc(block->command.arguments.front().c_str());
|
||||
if (funcBlock) {
|
||||
// before diving in the func tree, let's modify the vars with the local defs:
|
||||
int nbParams = std::min(block->command.arguments.size(), funcBlock->command.arguments.size());
|
||||
std::vector< String > paramCache;
|
||||
paramCache.push_back("");
|
||||
String val;
|
||||
for (int i = 1; i < nbParams; i++) {
|
||||
val = block->command.arguments[i];
|
||||
if ((val[0] == Tag::VAR) && (val[val.length()-1] == Tag::VAR)) {
|
||||
val = val.substr(1, val.length()-2);
|
||||
Vars::iterator it = vars.find(val);
|
||||
if (it != vars.end()) {
|
||||
val = (*it).second;
|
||||
}
|
||||
}
|
||||
|
||||
Vars::iterator it = vars.find(funcBlock->command.arguments[i]);
|
||||
if (it != vars.end()) {
|
||||
paramCache.push_back((*it).second);
|
||||
(*it).second = val;
|
||||
} else {
|
||||
vars.insert(Vars::value_type(funcBlock->command.arguments[i], val));
|
||||
paramCache.push_back("");
|
||||
}
|
||||
}
|
||||
|
||||
generateTree(dst, funcBlock, vars);
|
||||
|
||||
for (int i = 1; i < nbParams; i++) {
|
||||
vars[ funcBlock->command.arguments[i] ] = paramCache[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
branch = block;
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case Command::IFBLOCK: {
|
||||
// ok, go through the branches and pick the first one that goes
|
||||
for (auto child: block->blocks) {
|
||||
int numPasses = evalBlockGeneration(dst, child, vars, branch);
|
||||
if (numPasses > 0) {
|
||||
return numPasses;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Command::IF:
|
||||
case Command::ELIF: {
|
||||
if (!block->command.arguments.empty()) {
|
||||
// Just one argument means check for the var beeing defined
|
||||
if (block->command.arguments.size() == 1) {
|
||||
Vars::iterator it = vars.find(block->command.arguments.front());
|
||||
if (it != vars.end()) {
|
||||
branch = block;
|
||||
return 1;
|
||||
}
|
||||
} else if (block->command.arguments.size() == 2) {
|
||||
if (block->command.arguments[0].compare("not") == 0) {
|
||||
Vars::iterator it = vars.find(block->command.arguments[1]);
|
||||
if (it == vars.end()) {
|
||||
branch = block;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} else if (block->command.arguments.size() == 3) {
|
||||
if (block->command.arguments[1].compare("and") == 0) {
|
||||
Vars::iterator itL = vars.find(block->command.arguments[0]);
|
||||
Vars::iterator itR = vars.find(block->command.arguments[2]);
|
||||
if ((itL != vars.end()) && (itR != vars.end())) {
|
||||
branch = block;
|
||||
return 1;
|
||||
}
|
||||
} else if (block->command.arguments[1].compare("or") == 0) {
|
||||
Vars::iterator itL = vars.find(block->command.arguments[0]);
|
||||
Vars::iterator itR = vars.find(block->command.arguments[2]);
|
||||
if ((itL != vars.end()) || (itR != vars.end())) {
|
||||
branch = block;
|
||||
return 1;
|
||||
}
|
||||
} else if (block->command.arguments[1].compare("==") == 0) {
|
||||
Vars::iterator itL = vars.find(block->command.arguments[0]);
|
||||
if (itL != vars.end()) {
|
||||
if ((*itL).second.compare(block->command.arguments[2]) == 0) {
|
||||
branch = block;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case Command::ELSE: {
|
||||
branch = block;
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case Command::ENDIF: {
|
||||
branch = block;
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case Command::DEF: {
|
||||
if (block->command.arguments.size()) {
|
||||
// THe actual value of the var defined sneeds to be evaluated:
|
||||
String val;
|
||||
for (int t = 1; t < block->command.arguments.size(); t++) {
|
||||
// detect if a param is a var
|
||||
int len = block->command.arguments[t].length();
|
||||
if ((block->command.arguments[t][0] == Tag::VAR)
|
||||
&& (block->command.arguments[t][len - 1] == Tag::VAR)) {
|
||||
String var = block->command.arguments[t].substr(1, len - 2);
|
||||
Vars::iterator it = vars.find(var);
|
||||
if (it != vars.end()) {
|
||||
val += (*it).second;
|
||||
}
|
||||
} else {
|
||||
val += block->command.arguments[t];
|
||||
}
|
||||
}
|
||||
|
||||
Vars::iterator it = vars.find(block->command.arguments.front());
|
||||
if (it == vars.end()) {
|
||||
vars.insert(Vars::value_type(block->command.arguments.front(), val));
|
||||
} else {
|
||||
(*it).second = val;
|
||||
}
|
||||
|
||||
branch = block;
|
||||
return 1;
|
||||
} else {
|
||||
branch = block;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Command::INCLUDE: {
|
||||
TextTemplatePointer include = _config->findInclude(block->command.arguments.front().c_str());
|
||||
if (include && !include->_root->blocks.empty()) {
|
||||
if (&include->_root) {
|
||||
generateTree(dst, include->_root, vars);
|
||||
}
|
||||
}
|
||||
|
||||
branch = block;
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case Command::FUNC: {
|
||||
branch = block;
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case Command::ENDFUNC: {
|
||||
branch = block;
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
|
||||
default: {
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int TextTemplate::parse(std::istream& src) {
|
||||
_root->command.type = Command::BLOCK;
|
||||
BlockPointer currentBlock = _root;
|
||||
_numErrors = 0;
|
||||
|
||||
// First Parse the input file
|
||||
int nbLoops = 0;
|
||||
bool goOn = true;
|
||||
Tag::Type tagType = Tag::INVALID;
|
||||
Tag::Type nextTagType = Tag::INVALID;
|
||||
while (goOn) {
|
||||
std::string data;
|
||||
std::string tag;
|
||||
if (stepForward(&src, data, tag, tagType, nextTagType)) {
|
||||
currentBlock = processStep(currentBlock, data, tag, tagType);
|
||||
} else {
|
||||
goOn = false;
|
||||
}
|
||||
|
||||
tagType = nextTagType;
|
||||
nbLoops++;
|
||||
}
|
||||
|
||||
return _numErrors;
|
||||
}
|
||||
|
||||
int TextTemplate::generate(std::ostream& dst, Vars& vars) {
|
||||
return generateTree(dst, _root, vars);
|
||||
}
|
||||
|
||||
void TextTemplate::displayTree(std::ostream& dst, int& level) const {
|
||||
Block::displayTree(_root, dst, level);
|
||||
}
|
||||
|
||||
void TextTemplate::Block::displayTree(const BlockPointer& block, std::ostream& dst, int& level) {
|
||||
String tab(level * 2, ' ');
|
||||
|
||||
const String BLOCK_TYPE_NAMES[] = {
|
||||
"VAR",
|
||||
"BLOCK",
|
||||
"FUNC",
|
||||
"ENDFUNC",
|
||||
"IFBLOCK",
|
||||
"IF",
|
||||
"ELIF",
|
||||
"ELSE",
|
||||
"ENDIF",
|
||||
"FOR",
|
||||
"ENDFOR",
|
||||
"INCLUDE",
|
||||
"DEF"
|
||||
};
|
||||
|
||||
dst << tab << "{ " << BLOCK_TYPE_NAMES[block->command.type] << ":";
|
||||
if (!block->command.arguments.empty()) {
|
||||
for (auto arg: block->command.arguments) {
|
||||
dst << " " << arg;
|
||||
}
|
||||
}
|
||||
dst << std::endl;
|
||||
|
||||
level++;
|
||||
for (auto sub: block->blocks) {
|
||||
displayTree(sub, dst, level);
|
||||
}
|
||||
level--;
|
||||
|
||||
dst << tab << "}" << std::endl;
|
||||
}
|
||||
|
||||
void TextTemplate::Config::displayTree(std::ostream& dst, int& level) const {
|
||||
String tab(level * 2, ' ');
|
||||
|
||||
level++;
|
||||
dst << tab << "Includes:" << std::endl;
|
||||
for (auto inc: _includes) {
|
||||
dst << tab << tab << inc.first << std::endl;
|
||||
inc.second->displayTree(dst, level);
|
||||
}
|
||||
dst << tab << "Funcs:" << std::endl;
|
||||
for (auto func: _funcs._funcs) {
|
||||
dst << tab << tab << func.first << std::endl;
|
||||
TextTemplate::Block::displayTree( func.second, dst, level);
|
||||
}
|
||||
level--;
|
||||
}
|
204
tools/scribe/src/TextTemplate.h
Executable file
204
tools/scribe/src/TextTemplate.h
Executable file
|
@ -0,0 +1,204 @@
|
|||
|
||||
//
|
||||
// TextTemplate.cpp
|
||||
// tools/shaderScribe/src
|
||||
//
|
||||
// Created by Sam Gateau on 12/15/2014.
|
||||
// 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
|
||||
#ifndef hifi_TEXT_TEMPLATE_H
|
||||
#define hifi_TEXT_TEMPLATE_H
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <memory>
|
||||
|
||||
class TextTemplate {
|
||||
public:
|
||||
typedef std::shared_ptr< TextTemplate > Pointer;
|
||||
typedef std::string String;
|
||||
typedef std::vector< String > StringVector;
|
||||
typedef std::map< String, String > Vars;
|
||||
typedef std::map< String, TextTemplate::Pointer > Includes;
|
||||
|
||||
class Tag {
|
||||
public:
|
||||
enum Type {
|
||||
VARIABLE = 0,
|
||||
COMMAND,
|
||||
REMARK,
|
||||
INVALID = -1,
|
||||
};
|
||||
|
||||
static const char BEGIN = '<';
|
||||
static const char END = '>';
|
||||
|
||||
static const char VAR = '$';
|
||||
static const char COM = '@';
|
||||
static const char REM = '!';
|
||||
};
|
||||
|
||||
class Command {
|
||||
public:
|
||||
typedef std::vector< Command > vector;
|
||||
|
||||
enum Type {
|
||||
VAR = 0,
|
||||
BLOCK,
|
||||
FUNC,
|
||||
ENDFUNC,
|
||||
IFBLOCK,
|
||||
IF,
|
||||
ELIF,
|
||||
ELSE,
|
||||
ENDIF,
|
||||
FOR,
|
||||
ENDFOR,
|
||||
INCLUDE,
|
||||
DEF,
|
||||
};
|
||||
|
||||
Type type;
|
||||
std::vector< String > arguments;
|
||||
|
||||
bool isBlockEnd() {
|
||||
switch (type) {
|
||||
case ENDFUNC:
|
||||
case ENDIF:
|
||||
case ENDFOR:
|
||||
case INCLUDE:
|
||||
case DEF:
|
||||
case VAR:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class Block {
|
||||
public:
|
||||
typedef std::shared_ptr<Block> Pointer;
|
||||
typedef std::vector< Block::Pointer > Vector;
|
||||
|
||||
Block::Pointer parent;
|
||||
Command command;
|
||||
Vector blocks;
|
||||
std::stringstream ostr;
|
||||
|
||||
String sourceName;
|
||||
|
||||
Block(const String& sourceFilename) :
|
||||
sourceName(sourceFilename) {}
|
||||
|
||||
static void addNewBlock(const Block::Pointer& parent, const Block::Pointer& block);
|
||||
static const Block::Pointer& getCurrentBlock(const Block::Pointer& block);
|
||||
|
||||
static void displayTree(const Block::Pointer& block, std::ostream& dst, int& level);
|
||||
};
|
||||
|
||||
class Funcs {
|
||||
public:
|
||||
typedef std::map< String, Block::Pointer > map;
|
||||
|
||||
Funcs();
|
||||
~Funcs();
|
||||
|
||||
const Block::Pointer findFunc(const char* func);
|
||||
const Block::Pointer addFunc(const char* func, const Block::Pointer& root);
|
||||
|
||||
map _funcs;
|
||||
protected:
|
||||
};
|
||||
|
||||
class Config {
|
||||
public:
|
||||
typedef std::shared_ptr< Config > Pointer;
|
||||
typedef bool (*IncluderCallback) (const Config::Pointer& config, const char* filename, String& source);
|
||||
|
||||
Includes _includes;
|
||||
Funcs _funcs;
|
||||
std::ostream* _logStream;
|
||||
int _numErrors;
|
||||
IncluderCallback _includerCallback;
|
||||
StringVector _paths;
|
||||
|
||||
Config();
|
||||
|
||||
static const TextTemplate::Pointer addInclude(const Config::Pointer& config, const char* include);
|
||||
const TextTemplate::Pointer findInclude(const char* include);
|
||||
|
||||
void addIncludePath(const char* path);
|
||||
|
||||
void displayTree(std::ostream& dst, int& level) const;
|
||||
};
|
||||
|
||||
static bool loadFile(const Config::Pointer& config, const char* filename, String& source);
|
||||
|
||||
TextTemplate(const String& name, const Config::Pointer& config = Config::Pointer(new Config()));
|
||||
~TextTemplate();
|
||||
|
||||
// Scibe does all the job of parsing an inout template stream and then gneerating theresulting stream using the vars
|
||||
int scribe(std::ostream& dst, std::istream& src, Vars& vars);
|
||||
|
||||
int parse(std::istream& src);
|
||||
int generate(std::ostream& dst, Vars& vars);
|
||||
|
||||
const Config::Pointer config() { return _config; }
|
||||
|
||||
void displayTree(std::ostream& dst, int& level) const;
|
||||
|
||||
protected:
|
||||
Config::Pointer _config;
|
||||
Block::Pointer _root;
|
||||
int _numErrors;
|
||||
bool _steppingStarted;
|
||||
|
||||
bool grabUntilBeginTag(std::istream* str, String& grabbed, Tag::Type& tagType);
|
||||
bool grabUntilEndTag(std::istream* str, String& grabbed, Tag::Type& tagType);
|
||||
|
||||
bool stepForward(std::istream* str, String& grabbed, String& tag, Tag::Type& tagType, Tag::Type& nextTagType);
|
||||
|
||||
bool grabFirstToken(String& src, String& token, String& reminder);
|
||||
bool convertExpressionToArguments(String& src, std::vector< String >& arguments);
|
||||
bool convertExpressionToDefArguments(String& src, std::vector< String >& arguments);
|
||||
bool convertExpressionToFuncArguments(String& src, std::vector< String >& arguments);
|
||||
|
||||
// Filter between var, command or comments
|
||||
const Block::Pointer processStep(const Block::Pointer& block, String& grabbed, String& tag, Tag::Type& tagType);
|
||||
const Block::Pointer processStepVar(const Block::Pointer& block, String& grabbed, String& tag);
|
||||
const Block::Pointer processStepCommand(const Block::Pointer& block, String& grabbed, String& tag);
|
||||
const Block::Pointer processStepRemark(const Block::Pointer& block, String& grabbed, String& tag);
|
||||
|
||||
// Define command
|
||||
const Block::Pointer processStepDef(const Block::Pointer& block, String& grabbed, String& tag);
|
||||
|
||||
// If commands
|
||||
const Block::Pointer processStepCommandIf(const Block::Pointer& block, String& grabbed, String& expression);
|
||||
const Block::Pointer processStepCommandEndIf(const Block::Pointer& block, String& grabbed, String& expression);
|
||||
const Block::Pointer processStepCommandElif(const Block::Pointer& block, String& grabbed, String& expression);
|
||||
const Block::Pointer processStepCommandElse(const Block::Pointer& block, String& grabbed, String& expression);
|
||||
|
||||
// Include command
|
||||
const Block::Pointer processStepInclude(const Block::Pointer& block, String& grabbed, String& tag);
|
||||
|
||||
// Function command
|
||||
const Block::Pointer processStepFunc(const Block::Pointer& block, String& grabbed, String& tag);
|
||||
const Block::Pointer processStepEndFunc(const Block::Pointer& block, String& grabbed, String& tag);
|
||||
|
||||
// Generation
|
||||
int generateTree(std::ostream& dst, const Block::Pointer& block, Vars& vars);
|
||||
int evalBlockGeneration(std::ostream& dst, const Block::Pointer& block, Vars& vars, Block::Pointer& branch);
|
||||
|
||||
// Errors
|
||||
std::ostream& log() { return (* _config->_logStream); }
|
||||
void logError(const Block::Pointer& block, const char* error, ...);
|
||||
};
|
||||
|
||||
#endif
|
225
tools/scribe/src/main.cpp
Executable file
225
tools/scribe/src/main.cpp
Executable file
|
@ -0,0 +1,225 @@
|
|||
//
|
||||
// main.cpp
|
||||
// tools/shaderScribe/src
|
||||
//
|
||||
// Created by Sam Gateau on 12/15/2014.
|
||||
// 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
|
||||
|
||||
|
||||
#include "TextTemplate.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <ctime>
|
||||
#include <chrono>
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main (int argc, char** argv) {
|
||||
// process the command line arguments
|
||||
std::vector< std::string > inputs;
|
||||
|
||||
std::string srcFilename;
|
||||
std::string destFilename;
|
||||
std::string targetName;
|
||||
TextTemplate::Vars vars;
|
||||
|
||||
std::string lastVarName;
|
||||
bool listVars = false;
|
||||
bool showParseTree = false;
|
||||
bool makeCPlusPlus = false;
|
||||
|
||||
TextTemplate::Config::Pointer config(new TextTemplate::Config());
|
||||
|
||||
enum Mode {
|
||||
READY = 0,
|
||||
GRAB_OUTPUT,
|
||||
GRAB_VAR_NAME,
|
||||
GRAB_VAR_VALUE,
|
||||
GRAB_INCLUDE_PATH,
|
||||
GRAB_TARGET_NAME,
|
||||
EXIT,
|
||||
} mode = READY;
|
||||
|
||||
for (int ii = 1; (mode != EXIT) && (ii < argc); ii++) {
|
||||
inputs.push_back(argv[ii]);
|
||||
|
||||
switch (mode) {
|
||||
case READY: {
|
||||
if (inputs.back() == "-o") {
|
||||
mode = GRAB_OUTPUT;
|
||||
} else if (inputs.back() == "-tn") {
|
||||
mode = GRAB_TARGET_NAME;
|
||||
} else if (inputs.back() == "-D") {
|
||||
mode = GRAB_VAR_NAME;
|
||||
} else if (inputs.back() == "-I") {
|
||||
mode = GRAB_INCLUDE_PATH;
|
||||
} else if (inputs.back() == "-listVars") {
|
||||
listVars = true;
|
||||
mode = READY;
|
||||
} else if (inputs.back() == "-showParseTree") {
|
||||
showParseTree = true;
|
||||
mode = READY;
|
||||
} else if (inputs.back() == "-c++") {
|
||||
makeCPlusPlus = true;
|
||||
mode = READY;
|
||||
} else {
|
||||
// just grabbed the source filename, stop parameter parsing
|
||||
srcFilename = inputs.back();
|
||||
mode = EXIT;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case GRAB_OUTPUT: {
|
||||
destFilename = inputs.back();
|
||||
mode = READY;
|
||||
}
|
||||
break;
|
||||
|
||||
case GRAB_TARGET_NAME: {
|
||||
targetName = inputs.back();
|
||||
mode = READY;
|
||||
}
|
||||
break;
|
||||
|
||||
case GRAB_VAR_NAME: {
|
||||
// grab first the name of the var
|
||||
lastVarName = inputs.back();
|
||||
mode = GRAB_VAR_VALUE;
|
||||
}
|
||||
break;
|
||||
case GRAB_VAR_VALUE: {
|
||||
// and then the value
|
||||
vars.insert(TextTemplate::Vars::value_type(lastVarName, inputs.back()));
|
||||
|
||||
mode = READY;
|
||||
}
|
||||
break;
|
||||
|
||||
case GRAB_INCLUDE_PATH: {
|
||||
config->addIncludePath(inputs.back().c_str());
|
||||
mode = READY;
|
||||
}
|
||||
break;
|
||||
|
||||
case EXIT: {
|
||||
// THis shouldn't happen
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (srcFilename.empty()) {
|
||||
cerr << "Usage: shaderScribe [OPTION]... inputFilename" << endl;
|
||||
cerr << "Where options include:" << endl;
|
||||
cerr << " -o filename: Send output to filename rather than standard output." << endl;
|
||||
cerr << " -t targetName: Set the targetName used, if not defined use the output filename 'name' and if not defined use the inputFilename 'name'" << endl;
|
||||
cerr << " -I include_directory: Declare a directory to be added to the includes search pool." << endl;
|
||||
cerr << " -D varname varvalue: Declare a var used to generate the output file." << endl;
|
||||
cerr << " varname and varvalue must be made of alpha numerical characters with no spaces." << endl;
|
||||
cerr << " -listVars : Will list the vars name and value in the standard output." << endl;
|
||||
cerr << " -showParseTree : Draw the tree obtained while parsing the source" << endl;
|
||||
cerr << " -c++ : Generate a c++ header file containing the output file stream stored as a char[] variable" << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Define targetName: if none, get destFilenmae, if none get srcFilename
|
||||
if (targetName.empty()) {
|
||||
if (destFilename.empty()) {
|
||||
targetName = srcFilename;
|
||||
} else {
|
||||
targetName = destFilename;
|
||||
}
|
||||
}
|
||||
// no clean it to have just a descent c var name
|
||||
if (!targetName.empty()) {
|
||||
// trim anything before '/' or '\'
|
||||
targetName = targetName.substr(targetName.find_last_of('/') + 1);
|
||||
targetName = targetName.substr(targetName.find_last_of('\\') + 1);
|
||||
|
||||
// trim anything after '.'
|
||||
targetName = targetName.substr(0, targetName.find_first_of('.'));
|
||||
}
|
||||
|
||||
// Add built in vars
|
||||
time_t endTime = chrono::system_clock::to_time_t(chrono::system_clock::now());
|
||||
std::string endTimStr(ctime(&endTime));
|
||||
vars["_SCRIBE_DATE"] = endTimStr.substr(0, endTimStr.length() - 1);
|
||||
|
||||
// List vars?
|
||||
if (listVars) {
|
||||
cerr << "Vars:" << endl;
|
||||
for (auto v : vars) {
|
||||
cerr << " " << v.first << " = " << v.second << endl;
|
||||
}
|
||||
}
|
||||
|
||||
// Open up source
|
||||
std::fstream srcStream;
|
||||
srcStream.open(srcFilename, std::fstream::in);
|
||||
if (!srcStream.is_open()) {
|
||||
cerr << "Failed to open source file <" << srcFilename << ">" << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
TextTemplate::Pointer scribe(new TextTemplate(srcFilename, config));
|
||||
|
||||
// ready to parse and generate
|
||||
std::ostringstream destStringStream;
|
||||
int numErrors = scribe->scribe(destStringStream, srcStream, vars);
|
||||
if (numErrors) {
|
||||
cerr << "Scribe " << srcFilename << "> failed: " << numErrors << " errors." << endl;
|
||||
return 0;
|
||||
};
|
||||
|
||||
|
||||
if (showParseTree) {
|
||||
int level = 1;
|
||||
cerr << "Config trees:" << std::endl;
|
||||
config->displayTree(cerr, level);
|
||||
|
||||
cerr << srcFilename << " tree:" << std::endl;
|
||||
scribe->displayTree(cerr, level);
|
||||
}
|
||||
|
||||
std::ostringstream targetStringStream;
|
||||
if (makeCPlusPlus) {
|
||||
targetStringStream << "// File generated by Scribe " << vars["_SCRIBE_DATE"] << std::endl;
|
||||
|
||||
targetStringStream << "#ifndef scribe_" << targetName << "_h" << std::endl;
|
||||
targetStringStream << "#define scribe_" << targetName << "_h" << std::endl << std::endl;
|
||||
|
||||
targetStringStream << "const char " << targetName << "[] = {\n\"";
|
||||
|
||||
std::stringstream destStringStreamAgain(destStringStream.str());
|
||||
while (!destStringStreamAgain.eof()) {
|
||||
std::string line;
|
||||
std::getline(destStringStreamAgain, line);
|
||||
targetStringStream << line << " \\n\\\n";
|
||||
}
|
||||
targetStringStream << "\"};" << std::endl << std::endl;
|
||||
|
||||
targetStringStream << "#endif" << std::endl;
|
||||
} else {
|
||||
targetStringStream << destStringStream.str();
|
||||
}
|
||||
|
||||
// Destination stream
|
||||
if (!destFilename.empty()) {
|
||||
std::fstream destFileStream;
|
||||
destFileStream.open(destFilename, std::fstream::out);
|
||||
if (!destFileStream.is_open()) {
|
||||
cerr << "Scribe output file " << destFilename << "> failed to open." << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
destFileStream << targetStringStream.str();
|
||||
} else {
|
||||
cerr << targetStringStream.str();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in a new issue