diff --git a/CMakeLists.txt b/CMakeLists.txt index 62cdc925f3..b421040a50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,8 +20,13 @@ if (WIN32) # set path for Microsoft SDKs # if get build error about missing 'glu32' this path is likely wrong # Uncomment the line with 8.1 if running Windows 8.1 - #set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} "C:\\Program Files (x86)\\Windows Kits\\8.1\\Lib\\winv6.3\\um\\x86") - set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} "C:\\Program Files\\Microsoft SDKs\\Windows\\v7.1 ") + if (MSVC10) + set(WINDOW_SDK_PATH "C:\\Program Files\\Microsoft SDKs\\Windows\\v7.1 ") + elseif (MSVC12) + set(WINDOW_SDK_PATH "C:\\Program Files (x86)\\Windows Kits\\8.1\\Lib\\winv6.3\\um\\x86 ") + endif () + message (WINDOW_SDK_PATH= ${WINDOW_SDK_PATH}) + set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${WINDOW_SDK_PATH}) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") elseif (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) #SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long -pedantic") diff --git a/Doxyfile b/Doxyfile deleted file mode 100644 index b2581f4a95..0000000000 --- a/Doxyfile +++ /dev/null @@ -1,1891 +0,0 @@ -# Doxyfile 1.8.4 - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project. -# -# All text after a double hash (##) is considered a comment and is placed -# in front of the TAG it is preceding . -# All text after a hash (#) is considered a comment and will be ignored. -# The format is: -# TAG = value [value, ...] -# For lists items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (" "). - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all -# text before the first occurrence of this tag. Doxygen uses libiconv (or the -# iconv built into libc) for the transcoding. See -# http://www.gnu.org/software/libiconv for the list of possible encodings. - -DOXYFILE_ENCODING = UTF-8 - -# The PROJECT_NAME tag is a single word (or sequence of words) that should -# identify the project. Note that if you do not use Doxywizard you need -# to put quotes around the project name if it contains spaces. - -PROJECT_NAME = "Hifi" - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. -# This could be handy for archiving the generated documentation or -# if some version control system is used. - -PROJECT_NUMBER = - -# Using the PROJECT_BRIEF tag one can provide an optional one line description -# for a project that appears at the top of each page and should give viewer -# a quick idea about the purpose of the project. Keep the description short. - -PROJECT_BRIEF = - -# With the PROJECT_LOGO tag one can specify an logo or icon that is -# included in the documentation. The maximum height of the logo should not -# exceed 55 pixels and the maximum width should not exceed 200 pixels. -# Doxygen will copy the logo to the output directory. - -PROJECT_LOGO = - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) -# base path where the generated documentation will be put. -# If a relative path is entered, it will be relative to the location -# where doxygen was started. If left blank the current directory will be used. - -OUTPUT_DIRECTORY = - -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create -# 4096 sub-directories (in 2 levels) under the output directory of each output -# format and will distribute the generated files over these directories. -# Enabling this option can be useful when feeding doxygen a huge amount of -# source files, where putting all generated files in the same directory would -# otherwise cause performance problems for the file system. - -CREATE_SUBDIRS = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# The default language is English, other supported languages are: -# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, -# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, -# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English -# messages), Korean, Korean-en, Latvian, Lithuanian, Norwegian, Macedonian, -# Persian, Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, -# Slovak, Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. - -OUTPUT_LANGUAGE = English - -# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will -# include brief member descriptions after the members that are listed in -# the file and class documentation (similar to JavaDoc). -# Set to NO to disable this. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend -# the brief description of a member or function before the detailed description. -# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator -# that is used to form the text in various listings. Each string -# in this list, if found as the leading text of the brief description, will be -# stripped from the text and the result after processing the whole list, is -# used as the annotated text. Otherwise, the brief description is used as-is. -# If left blank, the following values are used ("$name" is automatically -# replaced with the name of the entity): "The $name class" "The $name widget" -# "The $name file" "is" "provides" "specifies" "contains" -# "represents" "a" "an" "the" - -ABBREVIATE_BRIEF = - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# Doxygen will generate a detailed section even if there is only a brief -# description. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full -# path before files name in the file list and in the header files. If set -# to NO the shortest path that makes the file name unique will be used. - -FULL_PATH_NAMES = YES - -# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag -# can be used to strip a user-defined part of the path. Stripping is -# only done if one of the specified strings matches the left-hand part of -# the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the -# path to strip. Note that you specify absolute paths here, but also -# relative paths, which will be relative from the directory where doxygen is -# started. - -STRIP_FROM_PATH = - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of -# the path mentioned in the documentation of a class, which tells -# the reader which header file to include in order to use a class. -# If left blank only the name of the header file containing the class -# definition is used. Otherwise one should specify the include paths that -# are normally passed to the compiler using the -I flag. - -STRIP_FROM_INC_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful if your file system -# doesn't support long names like on DOS, Mac, or CD-ROM. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen -# will interpret the first line (until the first dot) of a JavaDoc-style -# comment as the brief description. If set to NO, the JavaDoc -# comments will behave just like regular Qt-style comments -# (thus requiring an explicit @brief command for a brief description.) - -JAVADOC_AUTOBRIEF = NO - -# If the QT_AUTOBRIEF tag is set to YES then Doxygen will -# interpret the first line (until the first dot) of a Qt-style -# comment as the brief description. If set to NO, the comments -# will behave just like regular Qt-style comments (thus requiring -# an explicit \brief command for a brief description.) - -QT_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen -# treat a multi-line C++ special comment block (i.e. a block of //! or /// -# comments) as a brief description. This used to be the default behaviour. -# The new default is to treat a multi-line C++ comment block as a detailed -# description. Set this tag to YES if you prefer the old behaviour instead. - -MULTILINE_CPP_IS_BRIEF = NO - -# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented -# member inherits the documentation from any documented member that it -# re-implements. - -INHERIT_DOCS = YES - -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce -# a new page for each member. If set to NO, the documentation of a member will -# be part of the file/class/namespace that contains it. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. -# Doxygen uses this value to replace tabs by spaces in code fragments. - -TAB_SIZE = 4 - -# This tag can be used to specify a number of aliases that acts -# as commands in the documentation. An alias has the form "name=value". -# For example adding "sideeffect=\par Side Effects:\n" will allow you to -# put the command \sideeffect (or @sideeffect) in the documentation, which -# will result in a user-defined paragraph with heading "Side Effects:". -# You can put \n's in the value part of an alias to insert newlines. - -ALIASES = "thread=\par Thread :\n" - -# This tag can be used to specify a number of word-keyword mappings (TCL only). -# A mapping has the form "name=value". For example adding -# "class=itcl::class" will allow you to use the command class in the -# itcl::class meaning. - -TCL_SUBST = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C -# sources only. Doxygen will then generate output that is more tailored for C. -# For instance, some of the names that are used will be different. The list -# of all members will be omitted, etc. - -OPTIMIZE_OUTPUT_FOR_C = NO - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java -# sources only. Doxygen will then generate output that is more tailored for -# Java. For instance, namespaces will be presented as packages, qualified -# scopes will look different, etc. - -OPTIMIZE_OUTPUT_JAVA = NO - -# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources only. Doxygen will then generate output that is more tailored for -# Fortran. - -OPTIMIZE_FOR_FORTRAN = NO - -# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for -# VHDL. - -OPTIMIZE_OUTPUT_VHDL = NO - -# Doxygen selects the parser to use depending on the extension of the files it -# parses. With this tag you can assign which parser to use for a given -# extension. Doxygen has a built-in mapping, but you can override or extend it -# using this tag. The format is ext=language, where ext is a file extension, -# and language is one of the parsers supported by doxygen: IDL, Java, -# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, -# C++. For instance to make doxygen treat .inc files as Fortran files (default -# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note -# that for custom extensions you also need to set FILE_PATTERNS otherwise the -# files are not read by doxygen. - -EXTENSION_MAPPING = - -# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all -# comments according to the Markdown format, which allows for more readable -# documentation. See http://daringfireball.net/projects/markdown/ for details. -# The output of markdown processing is further processed by doxygen, so you -# can mix doxygen, HTML, and XML commands with Markdown formatting. -# Disable only in case of backward compatibilities issues. - -MARKDOWN_SUPPORT = YES - -# When enabled doxygen tries to link words that correspond to documented -# classes, or namespaces to their corresponding documentation. Such a link can -# be prevented in individual cases by by putting a % sign in front of the word -# or globally by setting AUTOLINK_SUPPORT to NO. - -AUTOLINK_SUPPORT = YES - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should -# set this tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. -# func(std::string) {}). This also makes the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. - -BUILTIN_STL_SUPPORT = NO - -# If you use Microsoft's C++/CLI language, you should set this option to YES to -# enable parsing support. - -CPP_CLI_SUPPORT = NO - -# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. -# Doxygen will parse them like normal C++ but will assume all classes use public -# instead of private inheritance when no explicit protection keyword is present. - -SIP_SUPPORT = NO - -# For Microsoft's IDL there are propget and propput attributes to indicate -# getter and setter methods for a property. Setting this option to YES (the -# default) will make doxygen replace the get and set methods by a property in -# the documentation. This will only work if the methods are indeed getting or -# setting a simple type. If this is not the case, or you want to show the -# methods anyway, you should set this option to NO. - -IDL_PROPERTY_SUPPORT = YES - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. - -DISTRIBUTE_GROUP_DOC = NO - -# Set the SUBGROUPING tag to YES (the default) to allow class member groups of -# the same type (for instance a group of public functions) to be put as a -# subgroup of that type (e.g. under the Public Functions section). Set it to -# NO to prevent subgrouping. Alternatively, this can be done per class using -# the \nosubgrouping command. - -SUBGROUPING = YES - -# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and -# unions are shown inside the group in which they are included (e.g. using -# @ingroup) instead of on a separate page (for HTML and Man pages) or -# section (for LaTeX and RTF). - -INLINE_GROUPED_CLASSES = NO - -# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and -# unions with only public data fields or simple typedef fields will be shown -# inline in the documentation of the scope in which they are defined (i.e. file, -# namespace, or group documentation), provided this scope is documented. If set -# to NO (the default), structs, classes, and unions are shown on a separate -# page (for HTML and Man pages) or section (for LaTeX and RTF). - -INLINE_SIMPLE_STRUCTS = NO - -# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum -# is documented as struct, union, or enum with the name of the typedef. So -# typedef struct TypeS {} TypeT, will appear in the documentation as a struct -# with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically -# be useful for C code in case the coding convention dictates that all compound -# types are typedef'ed and only the typedef is referenced, never the tag name. - -TYPEDEF_HIDES_STRUCT = NO - -# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This -# cache is used to resolve symbols given their name and scope. Since this can -# be an expensive process and often the same symbol appear multiple times in -# the code, doxygen keeps a cache of pre-resolved symbols. If the cache is too -# small doxygen will become slower. If the cache is too large, memory is wasted. -# The cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid -# range is 0..9, the default is 0, corresponding to a cache size of 2^16 = 65536 -# symbols. - -LOOKUP_CACHE_SIZE = 0 - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. -# Private class members and static file members will be hidden unless -# the EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES - -EXTRACT_ALL = YES - -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class -# will be included in the documentation. - -EXTRACT_PRIVATE = YES - -# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal -# scope will be included in the documentation. - -EXTRACT_PACKAGE = NO - -# If the EXTRACT_STATIC tag is set to YES all static members of a file -# will be included in the documentation. - -EXTRACT_STATIC = NO - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) -# defined locally in source files will be included in the documentation. -# If set to NO only classes defined in header files are included. - -EXTRACT_LOCAL_CLASSES = YES - -# This flag is only useful for Objective-C code. When set to YES local -# methods, which are defined in the implementation section but not in -# the interface are included in the documentation. -# If set to NO (the default) only methods in the interface are included. - -EXTRACT_LOCAL_METHODS = NO - -# If this flag is set to YES, the members of anonymous namespaces will be -# extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base -# name of the file that contains the anonymous namespace. By default -# anonymous namespaces are hidden. - -EXTRACT_ANON_NSPACES = NO - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all -# undocumented members of documented classes, files or namespaces. -# If set to NO (the default) these members will be included in the -# various overviews, but no documentation section is generated. -# This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_MEMBERS = NO - -# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. -# If set to NO (the default) these classes will be included in the various -# overviews. This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_CLASSES = NO - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all -# friend (class|struct|union) declarations. -# If set to NO (the default) these declarations will be included in the -# documentation. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any -# documentation blocks found inside the body of a function. -# If set to NO (the default) these blocks will be appended to the -# function's detailed documentation block. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation -# that is typed after a \internal command is included. If the tag is set -# to NO (the default) then the documentation will be excluded. -# Set it to YES to include the internal documentation. - -INTERNAL_DOCS = NO - -# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate -# file names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. - -CASE_SENSE_NAMES = NO - -# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen -# will show members with their full class and namespace scopes in the -# documentation. If set to YES the scope will be hidden. - -HIDE_SCOPE_NAMES = NO - -# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen -# will put a list of the files that are included by a file in the documentation -# of that file. - -SHOW_INCLUDE_FILES = YES - -# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen -# will list include files with double quotes in the documentation -# rather than with sharp brackets. - -FORCE_LOCAL_INCLUDES = NO - -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] -# is inserted in the documentation for inline members. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen -# will sort the (detailed) documentation of file and class members -# alphabetically by member name. If set to NO the members will appear in -# declaration order. - -SORT_MEMBER_DOCS = YES - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the -# brief documentation of file, namespace and class members alphabetically -# by member name. If set to NO (the default) the members will appear in -# declaration order. - -SORT_BRIEF_DOCS = NO - -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen -# will sort the (brief and detailed) documentation of class members so that -# constructors and destructors are listed first. If set to NO (the default) -# the constructors will appear in the respective orders defined by -# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. -# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO -# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. - -SORT_MEMBERS_CTORS_1ST = NO - -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the -# hierarchy of group names into alphabetical order. If set to NO (the default) -# the group names will appear in their defined order. - -SORT_GROUP_NAMES = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be -# sorted by fully-qualified names, including namespaces. If set to -# NO (the default), the class list will be sorted only by class name, -# not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the -# alphabetical list. - -SORT_BY_SCOPE_NAME = NO - -# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to -# do proper type resolution of all parameters of a function it will reject a -# match between the prototype and the implementation of a member function even -# if there is only one candidate or it is obvious which candidate to choose -# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen -# will still accept a match between prototype and implementation in such cases. - -STRICT_PROTO_MATCHING = NO - -# The GENERATE_TODOLIST tag can be used to enable (YES) or -# disable (NO) the todo list. This list is created by putting \todo -# commands in the documentation. - -GENERATE_TODOLIST = YES - -# The GENERATE_TESTLIST tag can be used to enable (YES) or -# disable (NO) the test list. This list is created by putting \test -# commands in the documentation. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable (YES) or -# disable (NO) the bug list. This list is created by putting \bug -# commands in the documentation. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or -# disable (NO) the deprecated list. This list is created by putting -# \deprecated commands in the documentation. - -GENERATE_DEPRECATEDLIST= YES - -# The ENABLED_SECTIONS tag can be used to enable conditional -# documentation sections, marked by \if section-label ... \endif -# and \cond section-label ... \endcond blocks. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or macro consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and macros in the -# documentation can be controlled using \showinitializer or \hideinitializer -# command in the documentation regardless of this setting. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated -# at the bottom of the documentation of classes and structs. If set to YES the -# list will mention the files that were used to generate the documentation. - -SHOW_USED_FILES = YES - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. -# This will remove the Files entry from the Quick Index and from the -# Folder Tree View (if specified). The default is YES. - -SHOW_FILES = YES - -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the -# Namespaces page. -# This will remove the Namespaces entry from the Quick Index -# and from the Folder Tree View (if specified). The default is YES. - -SHOW_NAMESPACES = YES - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from -# the version control system). Doxygen will invoke the program by executing (via -# popen()) the command , where is the value of -# the FILE_VERSION_FILTER tag, and is the name of an input file -# provided by doxygen. Whatever the program writes to standard output -# is used as the file version. See the manual for examples. - -FILE_VERSION_FILTER = - -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed -# by doxygen. The layout file controls the global structure of the generated -# output files in an output format independent way. To create the layout file -# that represents doxygen's defaults, run doxygen with the -l option. -# You can optionally specify a file name after the option, if omitted -# DoxygenLayout.xml will be used as the name of the layout file. - -LAYOUT_FILE = - -# The CITE_BIB_FILES tag can be used to specify one or more bib files -# containing the references data. This must be a list of .bib files. The -# .bib extension is automatically appended if omitted. Using this command -# requires the bibtex tool to be installed. See also -# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style -# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this -# feature you need bibtex and perl available in the search path. Do not use -# file names with spaces, bibtex cannot handle them. - -CITE_BIB_FILES = - -#--------------------------------------------------------------------------- -# configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated -# by doxygen. Possible values are YES and NO. If left blank NO is used. - -QUIET = NO - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated by doxygen. Possible values are YES and NO. If left blank -# NO is used. - -WARNINGS = YES - -# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings -# for undocumented members. If EXTRACT_ALL is set to YES then this flag will -# automatically be disabled. - -WARN_IF_UNDOCUMENTED = YES - -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that -# don't exist or using markup commands wrongly. - -WARN_IF_DOC_ERROR = YES - -# The WARN_NO_PARAMDOC option can be enabled to get warnings for -# functions that are documented, but have no documentation for their parameters -# or return value. If set to NO (the default) doxygen will only warn about -# wrong or incomplete parameter documentation, but not about the absence of -# documentation. - -WARN_NO_PARAMDOC = NO - -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. Optionally the format may contain -# $version, which will be replaced by the version of the file (if it could -# be obtained via FILE_VERSION_FILTER) - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written -# to stderr. - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag can be used to specify the files and/or directories that contain -# documented source files. You may enter file names like "myfile.cpp" or -# directories like "/usr/src/myproject". Separate the files or directories -# with spaces. - -INPUT = - -# This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is -# also the default input encoding. Doxygen uses libiconv (or the iconv built -# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for -# the list of possible encodings. - -INPUT_ENCODING = UTF-8 - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank the following patterns are tested: -# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh -# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py -# *.f90 *.f *.for *.vhd *.vhdl - -FILE_PATTERNS = - -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. -# If left blank NO is used. - -RECURSIVE = YES - -# The EXCLUDE tag can be used to specify files and/or directories that should be -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. -# Note that relative paths are relative to the directory from which doxygen is -# run. - -EXCLUDE = - -# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or -# directories that are symbolic links (a Unix file system feature) are excluded -# from the input. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. Note that the wildcards are matched -# against the file with absolute path, so to exclude all test directories -# for example use the pattern */test/* - -EXCLUDE_PATTERNS = */external/* \ - */externals/* \ - -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test - -EXCLUDE_SYMBOLS = - -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see -# the \include command). - -EXAMPLE_PATH = - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank all files are included. - -EXAMPLE_PATTERNS = - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. -# Possible values are YES and NO. If left blank NO is used. - -EXAMPLE_RECURSIVE = NO - -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see -# the \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command , where -# is the value of the INPUT_FILTER tag, and is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. -# If FILTER_PATTERNS is specified, this tag will be ignored. -# Note that the filter must not add or remove lines; it is applied before the -# code is scanned, but not when the output code is generated. If lines are added -# or removed, the anchors will not be placed correctly. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. -# Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. -# The filters are a list of the form: -# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty or if -# non of the patterns match the file name, INPUT_FILTER is applied. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source -# files to browse (i.e. when SOURCE_BROWSER is set to YES). - -FILTER_SOURCE_FILES = NO - -# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file -# pattern. A pattern will override the setting for FILTER_PATTERN (if any) -# and it is also possible to disable source filtering for a specific pattern -# using *.ext= (so without naming a filter). This option only has effect when -# FILTER_SOURCE_FILES is enabled. - -FILTER_SOURCE_PATTERNS = - -# If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that -# is part of the input, its contents will be placed on the main page -# (index.html). This can be useful if you have a project on for instance GitHub -# and want reuse the introduction page also for the doxygen output. - -USE_MDFILE_AS_MAINPAGE = - -#--------------------------------------------------------------------------- -# configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. -# Note: To get rid of all source code in the generated output, make sure also -# VERBATIM_HEADERS is set to NO. - -SOURCE_BROWSER = NO - -# Setting the INLINE_SOURCES tag to YES will include the body -# of functions and classes directly in the documentation. - -INLINE_SOURCES = YES - -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code -# fragments. Normal C, C++ and Fortran comments will always remain visible. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES -# then for each documented function all documented -# functions referencing it will be listed. - -REFERENCED_BY_RELATION = NO - -# If the REFERENCES_RELATION tag is set to YES -# then for each documented function all documented entities -# called/used by that function will be listed. - -REFERENCES_RELATION = NO - -# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) -# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from -# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will -# link to the source code. -# Otherwise they will link to the documentation. - -REFERENCES_LINK_SOURCE = YES - -# If the USE_HTAGS tag is set to YES then the references to source code -# will point to the HTML generated by the htags(1) tool instead of doxygen -# built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You -# will need version 4.8.6 or higher. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for -# which an include is specified. Set to NO to disable this. - -VERBATIM_HEADERS = YES - -#--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project -# contains a lot of classes, structs, unions or interfaces. - -ALPHABETICAL_INDEX = YES - -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns -# in which this list will be split (can be a number in the range [1..20]) - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that -# should be ignored while generating the index headers. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will -# generate HTML output. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `html' will be used as the default path. - -HTML_OUTPUT = html - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank -# doxygen will generate files with .html extension. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a -# standard header. Note that when using a custom header you are responsible -# for the proper inclusion of any scripts and style sheets that doxygen -# needs, which is dependent on the configuration options used. -# It is advised to generate a default header using "doxygen -w html -# header.html footer.html stylesheet.css YourConfigFile" and then modify -# that header. Note that the header is subject to change so you typically -# have to redo this when upgrading to a newer version of doxygen or when -# changing the value of configuration settings such as GENERATE_TREEVIEW! - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a -# standard footer. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If left blank doxygen will -# generate a default style sheet. Note that it is recommended to use -# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this -# tag will in the future become obsolete. - -HTML_STYLESHEET = - -# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional -# user-defined cascading style sheet that is included after the standard -# style sheets created by doxygen. Using this option one can overrule -# certain style aspects. This is preferred over using HTML_STYLESHEET -# since it does not replace the standard style sheet and is therefor more -# robust against future updates. Doxygen will copy the style sheet file to -# the output directory. - -HTML_EXTRA_STYLESHEET = - -# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or -# other source files which should be copied to the HTML output directory. Note -# that these files will be copied to the base HTML output directory. Use the -# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these -# files. In the HTML_STYLESHEET file, use the file name only. Also note that -# the files will be copied as-is; there are no commands or markers available. - -HTML_EXTRA_FILES = - -# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. -# Doxygen will adjust the colors in the style sheet and background images -# according to this color. Hue is specified as an angle on a colorwheel, -# see http://en.wikipedia.org/wiki/Hue for more information. -# For instance the value 0 represents red, 60 is yellow, 120 is green, -# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. -# The allowed range is 0 to 359. - -HTML_COLORSTYLE_HUE = 220 - -# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of -# the colors in the HTML output. For a value of 0 the output will use -# grayscales only. A value of 255 will produce the most vivid colors. - -HTML_COLORSTYLE_SAT = 100 - -# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to -# the luminance component of the colors in the HTML output. Values below -# 100 gradually make the output lighter, whereas values above 100 make -# the output darker. The value divided by 100 is the actual gamma applied, -# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, -# and 100 does not change the gamma. - -HTML_COLORSTYLE_GAMMA = 80 - -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting -# this to NO can help when comparing the output of multiple runs. - -HTML_TIMESTAMP = YES - -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. - -HTML_DYNAMIC_SECTIONS = NO - -# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of -# entries shown in the various tree structured indices initially; the user -# can expand and collapse entries dynamically later on. Doxygen will expand -# the tree to such a level that at most the specified number of entries are -# visible (unless a fully collapsed tree already exceeds this amount). -# So setting the number of entries 1 will produce a full collapsed tree by -# default. 0 is a special value representing an infinite number of entries -# and will result in a full expanded tree by default. - -HTML_INDEX_NUM_ENTRIES = 100 - -# If the GENERATE_DOCSET tag is set to YES, additional index files -# will be generated that can be used as input for Apple's Xcode 3 -# integrated development environment, introduced with OSX 10.5 (Leopard). -# To create a documentation set, doxygen will generate a Makefile in the -# HTML output directory. Running make will produce the docset in that -# directory and running "make install" will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find -# it at startup. -# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html -# for more information. - -GENERATE_DOCSET = YES - -# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the -# feed. A documentation feed provides an umbrella under which multiple -# documentation sets from a single provider (such as a company or product suite) -# can be grouped. - -DOCSET_FEEDNAME = "HiFi documentation" - -# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that -# should uniquely identify the documentation set bundle. This should be a -# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen -# will append .docset to the name. - -DOCSET_BUNDLE_ID = io.highfidelity.HiFi - -# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely -# identify the documentation publisher. This should be a reverse domain-name -# style string, e.g. com.mycompany.MyDocSet.documentation. - -DOCSET_PUBLISHER_ID = io.highfidelity.HiFi.HighFidelity - -# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. - -DOCSET_PUBLISHER_NAME = High Fidelity - -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) -# of the generated HTML documentation. - -GENERATE_HTMLHELP = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be -# written to the html output directory. - -CHM_FILE = - -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run -# the HTML help compiler on the generated index.hhp. - -HHC_LOCATION = - -# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag -# controls if a separate .chi index file is generated (YES) or that -# it should be included in the master .chm file (NO). - -GENERATE_CHI = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING -# is used to encode HtmlHelp index (hhk), content (hhc) and project file -# content. - -CHM_INDEX_ENCODING = - -# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag -# controls whether a binary table of contents is generated (YES) or a -# normal table of contents (NO) in the .chm file. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members -# to the contents of the HTML help documentation and to the tree view. - -TOC_EXPAND = NO - -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and -# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated -# that can be used as input for Qt's qhelpgenerator to generate a -# Qt Compressed Help (.qch) of the generated HTML documentation. - -GENERATE_QHP = NO - -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can -# be used to specify the file name of the resulting .qch file. -# The path specified is relative to the HTML output folder. - -QCH_FILE = - -# The QHP_NAMESPACE tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#namespace - -QHP_NAMESPACE = org.doxygen.Project - -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#virtual-folders - -QHP_VIRTUAL_FOLDER = doc - -# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to -# add. For more information please see -# http://doc.trolltech.com/qthelpproject.html#custom-filters - -QHP_CUST_FILTER_NAME = - -# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the -# custom filter to add. For more information please see -# -# Qt Help Project / Custom Filters. - -QHP_CUST_FILTER_ATTRS = - -# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this -# project's -# filter section matches. -# -# Qt Help Project / Filter Attributes. - -QHP_SECT_FILTER_ATTRS = - -# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can -# be used to specify the location of Qt's qhelpgenerator. -# If non-empty doxygen will try to run qhelpgenerator on the generated -# .qhp file. - -QHG_LOCATION = - -# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files -# will be generated, which together with the HTML files, form an Eclipse help -# plugin. To install this plugin and make it available under the help contents -# menu in Eclipse, the contents of the directory containing the HTML and XML -# files needs to be copied into the plugins directory of eclipse. The name of -# the directory within the plugins directory should be the same as -# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before -# the help appears. - -GENERATE_ECLIPSEHELP = NO - -# A unique identifier for the eclipse help plugin. When installing the plugin -# the directory name containing the HTML and XML files should also have -# this name. - -ECLIPSE_DOC_ID = org.doxygen.Project - -# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) -# at top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. Since the tabs have the same information as the -# navigation tree you can set this option to NO if you already set -# GENERATE_TREEVIEW to YES. - -DISABLE_INDEX = NO - -# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. -# If the tag value is set to YES, a side panel will be generated -# containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). -# Windows users are probably better off using the HTML help feature. -# Since the tree basically has the same information as the tab index you -# could consider to set DISABLE_INDEX to NO when enabling this option. - -GENERATE_TREEVIEW = NO - -# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values -# (range [0,1..20]) that doxygen will group on one line in the generated HTML -# documentation. Note that a value of 0 will completely suppress the enum -# values from appearing in the overview section. - -ENUM_VALUES_PER_LINE = 4 - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree -# is shown. - -TREEVIEW_WIDTH = 250 - -# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open -# links to external symbols imported via tag files in a separate window. - -EXT_LINKS_IN_WINDOW = NO - -# Use this tag to change the font size of Latex formulas included -# as images in the HTML documentation. The default is 10. Note that -# when you change the font size after a successful doxygen run you need -# to manually remove any form_*.png images from the HTML output directory -# to force them to be regenerated. - -FORMULA_FONTSIZE = 10 - -# Use the FORMULA_TRANPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are -# not supported properly for IE 6.0, but are supported on all modern browsers. -# Note that when changing this option you need to delete any form_*.png files -# in the HTML output before the changes have effect. - -FORMULA_TRANSPARENT = YES - -# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax -# (see http://www.mathjax.org) which uses client side Javascript for the -# rendering instead of using prerendered bitmaps. Use this if you do not -# have LaTeX installed or if you want to formulas look prettier in the HTML -# output. When enabled you may also need to install MathJax separately and -# configure the path to it using the MATHJAX_RELPATH option. - -USE_MATHJAX = NO - -# When MathJax is enabled you can set the default output format to be used for -# the MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and -# SVG. The default value is HTML-CSS, which is slower, but has the best -# compatibility. - -MATHJAX_FORMAT = HTML-CSS - -# When MathJax is enabled you need to specify the location relative to the -# HTML output directory using the MATHJAX_RELPATH option. The destination -# directory should contain the MathJax.js script. For instance, if the mathjax -# directory is located at the same level as the HTML output directory, then -# MATHJAX_RELPATH should be ../mathjax. The default value points to -# the MathJax Content Delivery Network so you can quickly see the result without -# installing MathJax. -# However, it is strongly recommended to install a local -# copy of MathJax from http://www.mathjax.org before deployment. - -MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest - -# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension -# names that should be enabled during MathJax rendering. - -MATHJAX_EXTENSIONS = - -# The MATHJAX_CODEFILE tag can be used to specify a file with javascript -# pieces of code that will be used on startup of the MathJax code. - -MATHJAX_CODEFILE = - -# When the SEARCHENGINE tag is enabled doxygen will generate a search box -# for the HTML output. The underlying search engine uses javascript -# and DHTML and should work on any modern browser. Note that when using -# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets -# (GENERATE_DOCSET) there is already a search function so this one should -# typically be disabled. For large projects the javascript based search engine -# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. - -SEARCHENGINE = YES - -# When the SERVER_BASED_SEARCH tag is enabled the search engine will be -# implemented using a web server instead of a web client using Javascript. -# There are two flavours of web server based search depending on the -# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for -# searching and an index file used by the script. When EXTERNAL_SEARCH is -# enabled the indexing and searching needs to be provided by external tools. -# See the manual for details. - -SERVER_BASED_SEARCH = NO - -# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP -# script for searching. Instead the search results are written to an XML file -# which needs to be processed by an external indexer. Doxygen will invoke an -# external search engine pointed to by the SEARCHENGINE_URL option to obtain -# the search results. Doxygen ships with an example indexer (doxyindexer) and -# search engine (doxysearch.cgi) which are based on the open source search -# engine library Xapian. See the manual for configuration details. - -EXTERNAL_SEARCH = NO - -# The SEARCHENGINE_URL should point to a search engine hosted by a web server -# which will returned the search results when EXTERNAL_SEARCH is enabled. -# Doxygen ships with an example search engine (doxysearch) which is based on -# the open source search engine library Xapian. See the manual for configuration -# details. - -SEARCHENGINE_URL = - -# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed -# search data is written to a file for indexing by an external tool. With the -# SEARCHDATA_FILE tag the name of this file can be specified. - -SEARCHDATA_FILE = searchdata.xml - -# When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the -# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is -# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple -# projects and redirect the results back to the right project. - -EXTERNAL_SEARCH_ID = - -# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen -# projects other than the one defined by this configuration file, but that are -# all added to the same external search index. Each project needs to have a -# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id -# of to a relative location where the documentation can be found. -# The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ... - -EXTRA_SEARCH_MAPPINGS = - -#--------------------------------------------------------------------------- -# configuration options related to the LaTeX output -#--------------------------------------------------------------------------- - -# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will -# generate Latex output. - -GENERATE_LATEX = NO - -# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `latex' will be used as the default path. - -LATEX_OUTPUT = latex - -# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be -# invoked. If left blank `latex' will be used as the default command name. -# Note that when enabling USE_PDFLATEX this option is only used for -# generating bitmaps for formulas in the HTML output, but not in the -# Makefile that is written to the output directory. - -LATEX_CMD_NAME = latex - -# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to -# generate index for LaTeX. If left blank `makeindex' will be used as the -# default command name. - -MAKEINDEX_CMD_NAME = makeindex - -# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact -# LaTeX documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_LATEX = NO - -# The PAPER_TYPE tag can be used to set the paper type that is used -# by the printer. Possible values are: a4, letter, legal and -# executive. If left blank a4 will be used. - -PAPER_TYPE = a4 - -# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX -# packages that should be included in the LaTeX output. - -EXTRA_PACKAGES = - -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for -# the generated latex document. The header should contain everything until -# the first chapter. If it is left blank doxygen will generate a -# standard header. Notice: only use this tag if you know what you are doing! - -LATEX_HEADER = - -# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for -# the generated latex document. The footer should contain everything after -# the last chapter. If it is left blank doxygen will generate a -# standard footer. Notice: only use this tag if you know what you are doing! - -LATEX_FOOTER = - -# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images -# or other source files which should be copied to the LaTeX output directory. -# Note that the files will be copied as-is; there are no commands or markers -# available. - -LATEX_EXTRA_FILES = - -# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated -# is prepared for conversion to pdf (using ps2pdf). The pdf file will -# contain links (just like the HTML output) instead of page references -# This makes the output suitable for online browsing using a pdf viewer. - -PDF_HYPERLINKS = YES - -# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of -# plain latex in the generated Makefile. Set this option to YES to get a -# higher quality PDF documentation. - -USE_PDFLATEX = YES - -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. -# command to the generated LaTeX files. This will instruct LaTeX to keep -# running if errors occur, instead of asking the user for help. -# This option is also used when generating formulas in HTML. - -LATEX_BATCHMODE = NO - -# If LATEX_HIDE_INDICES is set to YES then doxygen will not -# include the index chapters (such as File Index, Compound Index, etc.) -# in the output. - -LATEX_HIDE_INDICES = NO - -# If LATEX_SOURCE_CODE is set to YES then doxygen will include -# source code with syntax highlighting in the LaTeX output. -# Note that which sources are shown also depends on other settings -# such as SOURCE_BROWSER. - -LATEX_SOURCE_CODE = NO - -# The LATEX_BIB_STYLE tag can be used to specify the style to use for the -# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See -# http://en.wikipedia.org/wiki/BibTeX for more info. - -LATEX_BIB_STYLE = plain - -#--------------------------------------------------------------------------- -# configuration options related to the RTF output -#--------------------------------------------------------------------------- - -# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output -# The RTF output is optimized for Word 97 and may not look very pretty with -# other RTF readers or editors. - -GENERATE_RTF = NO - -# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `rtf' will be used as the default path. - -RTF_OUTPUT = rtf - -# If the COMPACT_RTF tag is set to YES Doxygen generates more compact -# RTF documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_RTF = NO - -# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated -# will contain hyperlink fields. The RTF file will -# contain links (just like the HTML output) instead of page references. -# This makes the output suitable for online browsing using WORD or other -# programs which support those fields. -# Note: wordpad (write) and others do not support links. - -RTF_HYPERLINKS = NO - -# Load style sheet definitions from file. Syntax is similar to doxygen's -# config file, i.e. a series of assignments. You only have to provide -# replacements, missing definitions are set to their default value. - -RTF_STYLESHEET_FILE = - -# Set optional variables used in the generation of an rtf document. -# Syntax is similar to doxygen's config file. - -RTF_EXTENSIONS_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to the man page output -#--------------------------------------------------------------------------- - -# If the GENERATE_MAN tag is set to YES (the default) Doxygen will -# generate man pages - -GENERATE_MAN = NO - -# The MAN_OUTPUT tag is used to specify where the man pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `man' will be used as the default path. - -MAN_OUTPUT = man - -# The MAN_EXTENSION tag determines the extension that is added to -# the generated man pages (default is the subroutine's section .3) - -MAN_EXTENSION = .3 - -# If the MAN_LINKS tag is set to YES and Doxygen generates man output, -# then it will generate one additional man file for each entity -# documented in the real man page(s). These additional files -# only source the real man page, but without them the man command -# would be unable to find the correct page. The default is NO. - -MAN_LINKS = NO - -#--------------------------------------------------------------------------- -# configuration options related to the XML output -#--------------------------------------------------------------------------- - -# If the GENERATE_XML tag is set to YES Doxygen will -# generate an XML file that captures the structure of -# the code including all documentation. - -GENERATE_XML = NO - -# The XML_OUTPUT tag is used to specify where the XML pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `xml' will be used as the default path. - -XML_OUTPUT = xml - -# The XML_SCHEMA tag can be used to specify an XML schema, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_SCHEMA = - -# The XML_DTD tag can be used to specify an XML DTD, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_DTD = - -# If the XML_PROGRAMLISTING tag is set to YES Doxygen will -# dump the program listings (including syntax highlighting -# and cross-referencing information) to the XML output. Note that -# enabling this will significantly increase the size of the XML output. - -XML_PROGRAMLISTING = YES - -#--------------------------------------------------------------------------- -# configuration options related to the DOCBOOK output -#--------------------------------------------------------------------------- - -# If the GENERATE_DOCBOOK tag is set to YES Doxygen will generate DOCBOOK files -# that can be used to generate PDF. - -GENERATE_DOCBOOK = NO - -# The DOCBOOK_OUTPUT tag is used to specify where the DOCBOOK pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in -# front of it. If left blank docbook will be used as the default path. - -DOCBOOK_OUTPUT = docbook - -#--------------------------------------------------------------------------- -# configuration options for the AutoGen Definitions output -#--------------------------------------------------------------------------- - -# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will -# generate an AutoGen Definitions (see autogen.sf.net) file -# that captures the structure of the code including all -# documentation. Note that this feature is still experimental -# and incomplete at the moment. - -GENERATE_AUTOGEN_DEF = NO - -#--------------------------------------------------------------------------- -# configuration options related to the Perl module output -#--------------------------------------------------------------------------- - -# If the GENERATE_PERLMOD tag is set to YES Doxygen will -# generate a Perl module file that captures the structure of -# the code including all documentation. Note that this -# feature is still experimental and incomplete at the -# moment. - -GENERATE_PERLMOD = NO - -# If the PERLMOD_LATEX tag is set to YES Doxygen will generate -# the necessary Makefile rules, Perl scripts and LaTeX code to be able -# to generate PDF and DVI output from the Perl module output. - -PERLMOD_LATEX = NO - -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be -# nicely formatted so it can be parsed by a human reader. -# This is useful -# if you want to understand what is going on. -# On the other hand, if this -# tag is set to NO the size of the Perl module output will be much smaller -# and Perl will parse it just the same. - -PERLMOD_PRETTY = YES - -# The names of the make variables in the generated doxyrules.make file -# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. -# This is useful so different doxyrules.make files included by the same -# Makefile don't overwrite each other's variables. - -PERLMOD_MAKEVAR_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- - -# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will -# evaluate all C-preprocessor directives found in the sources and include -# files. - -ENABLE_PREPROCESSING = YES - -# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro -# names in the source code. If set to NO (the default) only conditional -# compilation will be performed. Macro expansion can be done in a controlled -# way by setting EXPAND_ONLY_PREDEF to YES. - -MACRO_EXPANSION = NO - -# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES -# then the macro expansion is limited to the macros specified with the -# PREDEFINED and EXPAND_AS_DEFINED tags. - -EXPAND_ONLY_PREDEF = NO - -# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files -# pointed to by INCLUDE_PATH will be searched when a #include is found. - -SEARCH_INCLUDES = YES - -# The INCLUDE_PATH tag can be used to specify one or more directories that -# contain include files that are not input files but should be processed by -# the preprocessor. - -INCLUDE_PATH = - -# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard -# patterns (like *.h and *.hpp) to filter out the header-files in the -# directories. If left blank, the patterns specified with FILE_PATTERNS will -# be used. - -INCLUDE_FILE_PATTERNS = - -# The PREDEFINED tag can be used to specify one or more macro names that -# are defined before the preprocessor is started (similar to the -D option of -# gcc). The argument of the tag is a list of macros of the form: name -# or name=definition (no spaces). If the definition and the = are -# omitted =1 is assumed. To prevent a macro definition from being -# undefined via #undef or recursively expanded use the := operator -# instead of the = operator. - -PREDEFINED = - -# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then -# this tag can be used to specify a list of macro names that should be expanded. -# The macro definition that is found in the sources will be used. -# Use the PREDEFINED tag if you want to use a different macro definition that -# overrules the definition found in the source code. - -EXPAND_AS_DEFINED = - -# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then -# doxygen's preprocessor will remove all references to function-like macros -# that are alone on a line, have an all uppercase name, and do not end with a -# semicolon, because these will confuse the parser if not removed. - -SKIP_FUNCTION_MACROS = YES - -#--------------------------------------------------------------------------- -# Configuration::additions related to external references -#--------------------------------------------------------------------------- - -# The TAGFILES option can be used to specify one or more tagfiles. For each -# tag file the location of the external documentation should be added. The -# format of a tag file without this location is as follows: -# -# TAGFILES = file1 file2 ... -# Adding location for the tag files is done as follows: -# -# TAGFILES = file1=loc1 "file2 = loc2" ... -# where "loc1" and "loc2" can be relative or absolute paths -# or URLs. Note that each tag file must have a unique name (where the name does -# NOT include the path). If a tag file is not located in the directory in which -# doxygen is run, you must also specify the path to the tagfile here. - -TAGFILES = - -# When a file name is specified after GENERATE_TAGFILE, doxygen will create -# a tag file that is based on the input files it reads. - -GENERATE_TAGFILE = - -# If the ALLEXTERNALS tag is set to YES all external classes will be listed -# in the class index. If set to NO only the inherited external classes -# will be listed. - -ALLEXTERNALS = NO - -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will -# be listed. - -EXTERNAL_GROUPS = YES - -# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed -# in the related pages index. If set to NO, only the current project's -# pages will be listed. - -EXTERNAL_PAGES = YES - -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of `which perl'). - -PERL_PATH = /usr/bin/perl - -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- - -# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will -# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base -# or super classes. Setting the tag to NO turns the diagrams off. Note that -# this option also works with HAVE_DOT disabled, but it is recommended to -# install and use dot, since it yields more powerful graphs. - -CLASS_DIAGRAMS = YES - -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see -# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the -# default search path. - -MSCGEN_PATH = - -# If set to YES, the inheritance and collaboration graphs will hide -# inheritance and usage relations if the target is undocumented -# or is not a class. - -HIDE_UNDOC_RELATIONS = YES - -# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is -# available from the path. This tool is part of Graphviz, a graph visualization -# toolkit from AT&T and Lucent Bell Labs. The other options in this section -# have no effect if this option is set to NO (the default) - -HAVE_DOT = NO - -# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is -# allowed to run in parallel. When set to 0 (the default) doxygen will -# base this on the number of processors available in the system. You can set it -# explicitly to a value larger than 0 to get control over the balance -# between CPU load and processing speed. - -DOT_NUM_THREADS = 0 - -# By default doxygen will use the Helvetica font for all dot files that -# doxygen generates. When you want a differently looking font you can specify -# the font name using DOT_FONTNAME. You need to make sure dot is able to find -# the font, which can be done by putting it in a standard location or by setting -# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the -# directory containing the font. - -DOT_FONTNAME = Helvetica - -# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. -# The default size is 10pt. - -DOT_FONTSIZE = 10 - -# By default doxygen will tell dot to use the Helvetica font. -# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to -# set the path where dot can find it. - -DOT_FONTPATH = - -# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect inheritance relations. Setting this tag to YES will force the -# CLASS_DIAGRAMS tag to NO. - -CLASS_GRAPH = YES - -# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect implementation dependencies (inheritance, containment, and -# class references variables) of the class with other documented classes. - -COLLABORATION_GRAPH = YES - -# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for groups, showing the direct groups dependencies - -GROUP_GRAPHS = YES - -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and -# collaboration diagrams in a style similar to the OMG's Unified Modeling -# Language. - -UML_LOOK = NO - -# If the UML_LOOK tag is enabled, the fields and methods are shown inside -# the class node. If there are many fields or methods and many nodes the -# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS -# threshold limits the number of items for each type to make the size more -# manageable. Set this to 0 for no limit. Note that the threshold may be -# exceeded by 50% before the limit is enforced. - -UML_LIMIT_NUM_FIELDS = 10 - -# If set to YES, the inheritance and collaboration graphs will show the -# relations between templates and their instances. - -TEMPLATE_RELATIONS = NO - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT -# tags are set to YES then doxygen will generate a graph for each documented -# file showing the direct and indirect include dependencies of the file with -# other documented files. - -INCLUDE_GRAPH = YES - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and -# HAVE_DOT tags are set to YES then doxygen will generate a graph for each -# documented header file showing the documented files that directly or -# indirectly include this file. - -INCLUDED_BY_GRAPH = YES - -# If the CALL_GRAPH and HAVE_DOT options are set to YES then -# doxygen will generate a call dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable call graphs -# for selected functions only using the \callgraph command. - -CALL_GRAPH = NO - -# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then -# doxygen will generate a caller dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable caller -# graphs for selected functions only using the \callergraph command. - -CALLER_GRAPH = NO - -# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen -# will generate a graphical hierarchy of all classes instead of a textual one. - -GRAPHICAL_HIERARCHY = YES - -# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES -# then doxygen will show the dependencies a directory has on other directories -# in a graphical way. The dependency relations are determined by the #include -# relations between the files in the directories. - -DIRECTORY_GRAPH = YES - -# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. Possible values are svg, png, jpg, or gif. -# If left blank png will be used. If you choose svg you need to set -# HTML_FILE_EXTENSION to xhtml in order to make the SVG files -# visible in IE 9+ (other browsers do not have this requirement). - -DOT_IMAGE_FORMAT = png - -# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to -# enable generation of interactive SVG images that allow zooming and panning. -# Note that this requires a modern browser other than Internet Explorer. -# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you -# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files -# visible. Older versions of IE do not have SVG support. - -INTERACTIVE_SVG = NO - -# The tag DOT_PATH can be used to specify the path where the dot tool can be -# found. If left blank, it is assumed the dot tool can be found in the path. - -DOT_PATH = - -# The DOTFILE_DIRS tag can be used to specify one or more directories that -# contain dot files that are included in the documentation (see the -# \dotfile command). - -DOTFILE_DIRS = - -# The MSCFILE_DIRS tag can be used to specify one or more directories that -# contain msc files that are included in the documentation (see the -# \mscfile command). - -MSCFILE_DIRS = - -# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of -# nodes that will be shown in the graph. If the number of nodes in a graph -# becomes larger than this value, doxygen will truncate the graph, which is -# visualized by representing a node as a red box. Note that doxygen if the -# number of direct children of the root node in a graph is already larger than -# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note -# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. - -DOT_GRAPH_MAX_NODES = 50 - -# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the -# graphs generated by dot. A depth value of 3 means that only nodes reachable -# from the root by following a path via at most 3 edges will be shown. Nodes -# that lay further from the root node will be omitted. Note that setting this -# option to 1 or 2 may greatly reduce the computation time needed for large -# code bases. Also note that the size of a graph can be further restricted by -# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. - -MAX_DOT_GRAPH_DEPTH = 0 - -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not -# seem to support this out of the box. Warning: Depending on the platform used, -# enabling this option may lead to badly anti-aliased labels on the edges of -# a graph (i.e. they become hard to read). - -DOT_TRANSPARENT = NO - -# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output -# files in one run (i.e. multiple -o and -T options on the command line). This -# makes dot run faster, but since only newer versions of dot (>1.8.10) -# support this, this feature is disabled by default. - -DOT_MULTI_TARGETS = NO - -# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will -# generate a legend page explaining the meaning of the various boxes and -# arrows in the dot generated graphs. - -GENERATE_LEGEND = YES - -# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will -# remove the intermediate dot files that are used to generate -# the various graphs. - -DOT_CLEANUP = YES diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 37d89f3790..8caf4ddf09 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -9,7 +9,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include #include #include #include @@ -46,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -57,14 +57,14 @@ #include "AvatarAudioStream.h" #include "InjectedAudioStream.h" - - #include "AudioMixer.h" const float LOUDNESS_TO_DISTANCE_RATIO = 0.00001f; const float DEFAULT_ATTENUATION_PER_DOUBLING_IN_DISTANCE = 0.18; const QString AUDIO_MIXER_LOGGING_TARGET_NAME = "audio-mixer"; +const QString AUDIO_ENV_GROUP_KEY = "audio_env"; +const QString AUDIO_BUFFER_GROUP_KEY = "audio_buffer"; void attachNewNodeDataToNode(Node *newNode) { if (!newNode->getLinkedData()) { @@ -87,8 +87,6 @@ AudioMixer::AudioMixer(const QByteArray& packet) : _numStatFrames(0), _sumListeners(0), _sumMixes(0), - _sourceUnattenuatedZone(NULL), - _listenerUnattenuatedZone(NULL), _lastPerSecondCallbackTime(usecTimestampNow()), _sendAudioStreamStats(false), _datagramsReadPerCallStats(0, READ_DATAGRAMS_STATS_WINDOW_SECONDS), @@ -100,18 +98,13 @@ AudioMixer::AudioMixer(const QByteArray& packet) : // we will soon find a better common home for these audio-related constants } -AudioMixer::~AudioMixer() { - delete _sourceUnattenuatedZone; - delete _listenerUnattenuatedZone; -} - const float ATTENUATION_BEGINS_AT_DISTANCE = 1.0f; const float RADIUS_OF_HEAD = 0.076f; int AudioMixer::addStreamToMixForListeningNodeWithStream(AudioMixerClientData* listenerNodeData, - const QUuid& streamUUID, - PositionalAudioStream* streamToAdd, - AvatarAudioStream* listeningNodeStream) { + const QUuid& streamUUID, + PositionalAudioStream* streamToAdd, + AvatarAudioStream* listeningNodeStream) { // If repetition with fade is enabled: // If streamToAdd could not provide a frame (it was starved), then we'll mix its previously-mixed frame // This is preferable to not mixing it at all since that's equivalent to inserting silence. @@ -120,9 +113,9 @@ int AudioMixer::addStreamToMixForListeningNodeWithStream(AudioMixerClientData* l // This improves the perceived quality of the audio slightly. bool showDebug = false; // (randFloat() < 0.05f); - + float repeatedFrameFadeFactor = 1.0f; - + if (!streamToAdd->lastPopSucceeded()) { if (_streamSettings._repetitionWithFade && !streamToAdd->getLastPopOutput().isNull()) { // reptition with fade is enabled, and we do have a valid previous frame to repeat. @@ -135,21 +128,19 @@ int AudioMixer::addStreamToMixForListeningNodeWithStream(AudioMixerClientData* l return 0; } } - + // at this point, we know streamToAdd's last pop output is valid - + // if the frame we're about to mix is silent, bail if (streamToAdd->getLastPopOutputLoudness() == 0.0f) { return 0; } - + float bearingRelativeAngleToSource = 0.0f; float attenuationCoefficient = 1.0f; int numSamplesDelay = 0; float weakChannelAmplitudeRatio = 1.0f; - bool shouldDistanceAttenuate = true; - // Is the source that I am mixing my own? bool sourceIsSelf = (streamToAdd == listeningNodeStream); @@ -169,10 +160,6 @@ int AudioMixer::addStreamToMixForListeningNodeWithStream(AudioMixerClientData* l ++_sumMixes; - if (streamToAdd->getListenerUnattenuatedZone()) { - shouldDistanceAttenuate = !streamToAdd->getListenerUnattenuatedZone()->contains(listeningNodeStream->getPosition()); - } - if (streamToAdd->getType() == PositionalAudioStream::Injector) { attenuationCoefficient *= reinterpret_cast(streamToAdd)->getAttenuationRatio(); if (showDebug) { @@ -208,10 +195,19 @@ int AudioMixer::addStreamToMixForListeningNodeWithStream(AudioMixerClientData* l attenuationCoefficient *= offAxisCoefficient; } - if (shouldDistanceAttenuate && (distanceBetween >= ATTENUATION_BEGINS_AT_DISTANCE)) { + float attenuationPerDoublingInDistance = _attenuationPerDoublingInDistance; + for (int i = 0; i < _zonesSettings.length(); ++i) { + if (_audioZones[_zonesSettings[i].source].contains(streamToAdd->getPosition()) && + _audioZones[_zonesSettings[i].listener].contains(listeningNodeStream->getPosition())) { + attenuationPerDoublingInDistance = _zonesSettings[i].coefficient; + break; + } + } + + if (distanceBetween >= ATTENUATION_BEGINS_AT_DISTANCE) { // calculate the distance coefficient using the distance to this node float distanceCoefficient = 1 - (logf(distanceBetween / ATTENUATION_BEGINS_AT_DISTANCE) / logf(2.0f) - * _attenuationPerDoublingInDistance); + * attenuationPerDoublingInDistance); if (distanceCoefficient < 0) { distanceCoefficient = 0; @@ -637,120 +633,7 @@ void AudioMixer::run() { const QJsonObject& settingsObject = domainHandler.getSettingsObject(); // check the settings object to see if we have anything we can parse out - const QString AUDIO_GROUP_KEY = "audio"; - - if (settingsObject.contains(AUDIO_GROUP_KEY)) { - QJsonObject audioGroupObject = settingsObject[AUDIO_GROUP_KEY].toObject(); - - // check the payload to see if we have asked for dynamicJitterBuffer support - const QString DYNAMIC_JITTER_BUFFER_JSON_KEY = "dynamic_jitter_buffer"; - _streamSettings._dynamicJitterBuffers = audioGroupObject[DYNAMIC_JITTER_BUFFER_JSON_KEY].toBool(); - if (_streamSettings._dynamicJitterBuffers) { - qDebug() << "Enable dynamic jitter buffers."; - } else { - qDebug() << "Dynamic jitter buffers disabled."; - } - - bool ok; - const QString DESIRED_JITTER_BUFFER_FRAMES_KEY = "static_desired_jitter_buffer_frames"; - _streamSettings._staticDesiredJitterBufferFrames = audioGroupObject[DESIRED_JITTER_BUFFER_FRAMES_KEY].toString().toInt(&ok); - if (!ok) { - _streamSettings._staticDesiredJitterBufferFrames = DEFAULT_STATIC_DESIRED_JITTER_BUFFER_FRAMES; - } - qDebug() << "Static desired jitter buffer frames:" << _streamSettings._staticDesiredJitterBufferFrames; - - const QString MAX_FRAMES_OVER_DESIRED_JSON_KEY = "max_frames_over_desired"; - _streamSettings._maxFramesOverDesired = audioGroupObject[MAX_FRAMES_OVER_DESIRED_JSON_KEY].toString().toInt(&ok); - if (!ok) { - _streamSettings._maxFramesOverDesired = DEFAULT_MAX_FRAMES_OVER_DESIRED; - } - qDebug() << "Max frames over desired:" << _streamSettings._maxFramesOverDesired; - - const QString USE_STDEV_FOR_DESIRED_CALC_JSON_KEY = "use_stdev_for_desired_calc"; - _streamSettings._useStDevForJitterCalc = audioGroupObject[USE_STDEV_FOR_DESIRED_CALC_JSON_KEY].toBool(); - if (_streamSettings._useStDevForJitterCalc) { - qDebug() << "Using Philip's stdev method for jitter calc if dynamic jitter buffers enabled"; - } else { - qDebug() << "Using Fred's max-gap method for jitter calc if dynamic jitter buffers enabled"; - } - - const QString WINDOW_STARVE_THRESHOLD_JSON_KEY = "window_starve_threshold"; - _streamSettings._windowStarveThreshold = audioGroupObject[WINDOW_STARVE_THRESHOLD_JSON_KEY].toString().toInt(&ok); - if (!ok) { - _streamSettings._windowStarveThreshold = DEFAULT_WINDOW_STARVE_THRESHOLD; - } - qDebug() << "Window A starve threshold:" << _streamSettings._windowStarveThreshold; - - const QString WINDOW_SECONDS_FOR_DESIRED_CALC_ON_TOO_MANY_STARVES_JSON_KEY = "window_seconds_for_desired_calc_on_too_many_starves"; - _streamSettings._windowSecondsForDesiredCalcOnTooManyStarves = audioGroupObject[WINDOW_SECONDS_FOR_DESIRED_CALC_ON_TOO_MANY_STARVES_JSON_KEY].toString().toInt(&ok); - if (!ok) { - _streamSettings._windowSecondsForDesiredCalcOnTooManyStarves = DEFAULT_WINDOW_SECONDS_FOR_DESIRED_CALC_ON_TOO_MANY_STARVES; - } - qDebug() << "Window A length:" << _streamSettings._windowSecondsForDesiredCalcOnTooManyStarves << "seconds"; - - const QString WINDOW_SECONDS_FOR_DESIRED_REDUCTION_JSON_KEY = "window_seconds_for_desired_reduction"; - _streamSettings._windowSecondsForDesiredReduction = audioGroupObject[WINDOW_SECONDS_FOR_DESIRED_REDUCTION_JSON_KEY].toString().toInt(&ok); - if (!ok) { - _streamSettings._windowSecondsForDesiredReduction = DEFAULT_WINDOW_SECONDS_FOR_DESIRED_REDUCTION; - } - qDebug() << "Window B length:" << _streamSettings._windowSecondsForDesiredReduction << "seconds"; - - const QString REPETITION_WITH_FADE_JSON_KEY = "repetition_with_fade"; - _streamSettings._repetitionWithFade = audioGroupObject[REPETITION_WITH_FADE_JSON_KEY].toBool(); - if (_streamSettings._repetitionWithFade) { - qDebug() << "Repetition with fade enabled"; - } else { - qDebug() << "Repetition with fade disabled"; - } - - const QString PRINT_STREAM_STATS_JSON_KEY = "print_stream_stats"; - _printStreamStats = audioGroupObject[PRINT_STREAM_STATS_JSON_KEY].toBool(); - if (_printStreamStats) { - qDebug() << "Stream stats will be printed to stdout"; - } - - const QString FILTER_KEY = "enable_filter"; - if (audioGroupObject[FILTER_KEY].isBool()) { - _enableFilter = audioGroupObject[FILTER_KEY].toBool(); - } - if (_enableFilter) { - qDebug() << "Filter enabled"; - } - - const QString UNATTENUATED_ZONE_KEY = "unattenuated_zone"; - - QString unattenuatedZoneString = audioGroupObject[UNATTENUATED_ZONE_KEY].toString(); - if (!unattenuatedZoneString.isEmpty()) { - QStringList zoneStringList = unattenuatedZoneString.split(','); - - glm::vec3 sourceCorner(zoneStringList[0].toFloat(), zoneStringList[1].toFloat(), zoneStringList[2].toFloat()); - glm::vec3 sourceDimensions(zoneStringList[3].toFloat(), zoneStringList[4].toFloat(), zoneStringList[5].toFloat()); - - glm::vec3 listenerCorner(zoneStringList[6].toFloat(), zoneStringList[7].toFloat(), zoneStringList[8].toFloat()); - glm::vec3 listenerDimensions(zoneStringList[9].toFloat(), zoneStringList[10].toFloat(), zoneStringList[11].toFloat()); - - _sourceUnattenuatedZone = new AABox(sourceCorner, sourceDimensions); - _listenerUnattenuatedZone = new AABox(listenerCorner, listenerDimensions); - - glm::vec3 sourceCenter = _sourceUnattenuatedZone->calcCenter(); - glm::vec3 destinationCenter = _listenerUnattenuatedZone->calcCenter(); - - qDebug() << "There is an unattenuated zone with source center at" - << QString("%1, %2, %3").arg(sourceCenter.x).arg(sourceCenter.y).arg(sourceCenter.z); - qDebug() << "Buffers inside this zone will not be attenuated inside a box with center at" - << QString("%1, %2, %3").arg(destinationCenter.x).arg(destinationCenter.y).arg(destinationCenter.z); - } - - const QString ATTENATION_PER_DOULING_IN_DISTANCE = "attenuation_per_doubling_in_distance"; - if (audioGroupObject[ATTENATION_PER_DOULING_IN_DISTANCE].isString()) { - bool ok = false; - float attenuation = audioGroupObject[ATTENATION_PER_DOULING_IN_DISTANCE].toString().toFloat(&ok); - if (ok) { - _attenuationPerDoublingInDistance = attenuation; - qDebug() << "Attenuation per doubling in distance changed to" << _attenuationPerDoublingInDistance; - } - } - } + parseSettingsObject(settingsObject); int nextFrame = 0; QElapsedTimer timer; @@ -829,7 +712,7 @@ void AudioMixer::run() { // this function will attempt to pop a frame from each audio stream. // a pointer to the popped data is stored as a member in InboundAudioStream. // That's how the popped audio data will be read for mixing (but only if the pop was successful) - nodeData->checkBuffersBeforeFrameSend(_sourceUnattenuatedZone, _listenerUnattenuatedZone); + nodeData->checkBuffersBeforeFrameSend(); if (node->getType() == NodeType::Agent && node->getActiveSocket() && nodeData->getAvatarAudioStream()) { @@ -982,3 +865,175 @@ QString AudioMixer::getReadPendingDatagramsHashMatchTimeStatsString() const { + " prct_time_in_hashmatch_1s: " + QString::number(_timeSpentPerHashMatchCallStats.getLastCompleteIntervalStats().getSum() / USECS_PER_SECOND * 100.0, 'f', 6) + "%"; return result; } + +void AudioMixer::parseSettingsObject(const QJsonObject &settingsObject) { + if (settingsObject.contains(AUDIO_BUFFER_GROUP_KEY)) { + QJsonObject audioBufferGroupObject = settingsObject[AUDIO_BUFFER_GROUP_KEY].toObject(); + + // check the payload to see if we have asked for dynamicJitterBuffer support + const QString DYNAMIC_JITTER_BUFFER_JSON_KEY = "dynamic_jitter_buffer"; + _streamSettings._dynamicJitterBuffers = audioBufferGroupObject[DYNAMIC_JITTER_BUFFER_JSON_KEY].toBool(); + if (_streamSettings._dynamicJitterBuffers) { + qDebug() << "Enable dynamic jitter buffers."; + } else { + qDebug() << "Dynamic jitter buffers disabled."; + } + + bool ok; + const QString DESIRED_JITTER_BUFFER_FRAMES_KEY = "static_desired_jitter_buffer_frames"; + _streamSettings._staticDesiredJitterBufferFrames = audioBufferGroupObject[DESIRED_JITTER_BUFFER_FRAMES_KEY].toString().toInt(&ok); + if (!ok) { + _streamSettings._staticDesiredJitterBufferFrames = DEFAULT_STATIC_DESIRED_JITTER_BUFFER_FRAMES; + } + qDebug() << "Static desired jitter buffer frames:" << _streamSettings._staticDesiredJitterBufferFrames; + + const QString MAX_FRAMES_OVER_DESIRED_JSON_KEY = "max_frames_over_desired"; + _streamSettings._maxFramesOverDesired = audioBufferGroupObject[MAX_FRAMES_OVER_DESIRED_JSON_KEY].toString().toInt(&ok); + if (!ok) { + _streamSettings._maxFramesOverDesired = DEFAULT_MAX_FRAMES_OVER_DESIRED; + } + qDebug() << "Max frames over desired:" << _streamSettings._maxFramesOverDesired; + + const QString USE_STDEV_FOR_DESIRED_CALC_JSON_KEY = "use_stdev_for_desired_calc"; + _streamSettings._useStDevForJitterCalc = audioBufferGroupObject[USE_STDEV_FOR_DESIRED_CALC_JSON_KEY].toBool(); + if (_streamSettings._useStDevForJitterCalc) { + qDebug() << "Using Philip's stdev method for jitter calc if dynamic jitter buffers enabled"; + } else { + qDebug() << "Using Fred's max-gap method for jitter calc if dynamic jitter buffers enabled"; + } + + const QString WINDOW_STARVE_THRESHOLD_JSON_KEY = "window_starve_threshold"; + _streamSettings._windowStarveThreshold = audioBufferGroupObject[WINDOW_STARVE_THRESHOLD_JSON_KEY].toString().toInt(&ok); + if (!ok) { + _streamSettings._windowStarveThreshold = DEFAULT_WINDOW_STARVE_THRESHOLD; + } + qDebug() << "Window A starve threshold:" << _streamSettings._windowStarveThreshold; + + const QString WINDOW_SECONDS_FOR_DESIRED_CALC_ON_TOO_MANY_STARVES_JSON_KEY = "window_seconds_for_desired_calc_on_too_many_starves"; + _streamSettings._windowSecondsForDesiredCalcOnTooManyStarves = audioBufferGroupObject[WINDOW_SECONDS_FOR_DESIRED_CALC_ON_TOO_MANY_STARVES_JSON_KEY].toString().toInt(&ok); + if (!ok) { + _streamSettings._windowSecondsForDesiredCalcOnTooManyStarves = DEFAULT_WINDOW_SECONDS_FOR_DESIRED_CALC_ON_TOO_MANY_STARVES; + } + qDebug() << "Window A length:" << _streamSettings._windowSecondsForDesiredCalcOnTooManyStarves << "seconds"; + + const QString WINDOW_SECONDS_FOR_DESIRED_REDUCTION_JSON_KEY = "window_seconds_for_desired_reduction"; + _streamSettings._windowSecondsForDesiredReduction = audioBufferGroupObject[WINDOW_SECONDS_FOR_DESIRED_REDUCTION_JSON_KEY].toString().toInt(&ok); + if (!ok) { + _streamSettings._windowSecondsForDesiredReduction = DEFAULT_WINDOW_SECONDS_FOR_DESIRED_REDUCTION; + } + qDebug() << "Window B length:" << _streamSettings._windowSecondsForDesiredReduction << "seconds"; + + const QString REPETITION_WITH_FADE_JSON_KEY = "repetition_with_fade"; + _streamSettings._repetitionWithFade = audioBufferGroupObject[REPETITION_WITH_FADE_JSON_KEY].toBool(); + if (_streamSettings._repetitionWithFade) { + qDebug() << "Repetition with fade enabled"; + } else { + qDebug() << "Repetition with fade disabled"; + } + + const QString PRINT_STREAM_STATS_JSON_KEY = "print_stream_stats"; + _printStreamStats = audioBufferGroupObject[PRINT_STREAM_STATS_JSON_KEY].toBool(); + if (_printStreamStats) { + qDebug() << "Stream stats will be printed to stdout"; + } + } + + if (settingsObject.contains(AUDIO_ENV_GROUP_KEY)) { + QJsonObject audioEnvGroupObject = settingsObject[AUDIO_ENV_GROUP_KEY].toObject(); + + const QString ATTENATION_PER_DOULING_IN_DISTANCE = "attenuation_per_doubling_in_distance"; + if (audioEnvGroupObject[ATTENATION_PER_DOULING_IN_DISTANCE].isString()) { + bool ok = false; + float attenuation = audioEnvGroupObject[ATTENATION_PER_DOULING_IN_DISTANCE].toString().toFloat(&ok); + if (ok) { + _attenuationPerDoublingInDistance = attenuation; + qDebug() << "Attenuation per doubling in distance changed to" << _attenuationPerDoublingInDistance; + } + } + + const QString FILTER_KEY = "enable_filter"; + if (audioEnvGroupObject[FILTER_KEY].isBool()) { + _enableFilter = audioEnvGroupObject[FILTER_KEY].toBool(); + } + if (_enableFilter) { + qDebug() << "Filter enabled"; + } + + const QString AUDIO_ZONES = "zones"; + if (audioEnvGroupObject[AUDIO_ZONES].isObject()) { + const QJsonObject& zones = audioEnvGroupObject[AUDIO_ZONES].toObject(); + + const QString X_RANGE = "x_range"; + const QString Y_RANGE = "y_range"; + const QString Z_RANGE = "z_range"; + foreach (const QString& zone, zones.keys()) { + QJsonObject zoneObject = zones[zone].toObject(); + + if (zoneObject.contains(X_RANGE) && zoneObject.contains(Y_RANGE) && zoneObject.contains(Z_RANGE)) { + QStringList xRange = zoneObject.value(X_RANGE).toString().split("-", QString::SkipEmptyParts); + QStringList yRange = zoneObject.value(Y_RANGE).toString().split("-", QString::SkipEmptyParts); + QStringList zRange = zoneObject.value(Z_RANGE).toString().split("-", QString::SkipEmptyParts); + + if (xRange.size() == 2 && yRange.size() == 2 && zRange.size() == 2) { + float xMin, xMax, yMin, yMax, zMin, zMax; + bool ok, allOk = true; + xMin = xRange[0].toFloat(&ok); + allOk &= ok; + xMax = xRange[1].toFloat(&ok); + allOk &= ok; + yMin = yRange[0].toFloat(&ok); + allOk &= ok; + yMax = yRange[1].toFloat(&ok); + allOk &= ok; + zMin = zRange[0].toFloat(&ok); + allOk &= ok; + zMax = zRange[1].toFloat(&ok); + allOk &= ok; + + if (allOk) { + glm::vec3 corner(xMin, yMin, zMin); + glm::vec3 dimensions(xMax - xMin, yMax - yMin, zMax - zMin); + AABox zoneAABox(corner, dimensions); + _audioZones.insert(zone, zoneAABox); + qDebug() << "Added zone:" << zone << "(corner:" << corner + << ", dimensions:" << dimensions << ")"; + } + } + } + } + } + + const QString ATTENUATION_COEFFICIENTS = "attenuation_coefficients"; + if (audioEnvGroupObject[ATTENUATION_COEFFICIENTS].isArray()) { + const QJsonArray& coefficients = audioEnvGroupObject[ATTENUATION_COEFFICIENTS].toArray(); + + const QString SOURCE = "source"; + const QString LISTENER = "listener"; + const QString COEFFICIENT = "coefficient"; + for (int i = 0; i < coefficients.count(); ++i) { + QJsonObject coefficientObject = coefficients[i].toObject(); + + if (coefficientObject.contains(SOURCE) && + coefficientObject.contains(LISTENER) && + coefficientObject.contains(COEFFICIENT)) { + + ZonesSettings settings; + + bool ok; + settings.source = coefficientObject.value(SOURCE).toString(); + settings.listener = coefficientObject.value(LISTENER).toString(); + settings.coefficient = coefficientObject.value(COEFFICIENT).toString().toFloat(&ok); + + if (ok && settings.coefficient >= 0.0f && settings.coefficient <= 1.0f && + _audioZones.contains(settings.source) && _audioZones.contains(settings.listener)) { + + _zonesSettings.push_back(settings); + qDebug() << "Added Coefficient:" << settings.source << settings.listener << settings.coefficient; + } + } + } + } + } +} + + diff --git a/assignment-client/src/audio/AudioMixer.h b/assignment-client/src/audio/AudioMixer.h index 4d08d480f8..3cfa5443a8 100644 --- a/assignment-client/src/audio/AudioMixer.h +++ b/assignment-client/src/audio/AudioMixer.h @@ -29,7 +29,6 @@ class AudioMixer : public ThreadedAssignment { Q_OBJECT public: AudioMixer(const QByteArray& packet); - ~AudioMixer(); public slots: /// threaded run of assignment void run(); @@ -66,6 +65,8 @@ private: QString getReadPendingDatagramsTimeStatsString() const; QString getReadPendingDatagramsHashMatchTimeStatsString() const; + void parseSettingsObject(const QJsonObject& settingsObject); + float _trailingSleepRatio; float _minAudibilityThreshold; float _performanceThrottlingRatio; @@ -73,8 +74,14 @@ private: int _numStatFrames; int _sumListeners; int _sumMixes; - AABox* _sourceUnattenuatedZone; - AABox* _listenerUnattenuatedZone; + + QHash _audioZones; + struct ZonesSettings { + QString source; + QString listener; + float coefficient; + }; + QVector _zonesSettings; static InboundAudioStream::Settings _streamSettings; diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 2f60640da3..26178eb2af 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -106,7 +106,7 @@ int AudioMixerClientData::parseData(const QByteArray& packet) { return 0; } -void AudioMixerClientData::checkBuffersBeforeFrameSend(AABox* checkSourceZone, AABox* listenerZone) { +void AudioMixerClientData::checkBuffersBeforeFrameSend() { QHash::ConstIterator i; for (i = _audioStreams.constBegin(); i != _audioStreams.constEnd(); i++) { PositionalAudioStream* stream = i.value(); @@ -114,12 +114,6 @@ void AudioMixerClientData::checkBuffersBeforeFrameSend(AABox* checkSourceZone, A if (stream->popFrames(1, true) > 0) { stream->updateLastPopOutputLoudnessAndTrailingLoudness(); } - - if (checkSourceZone && checkSourceZone->contains(stream->getPosition())) { - stream->setListenerUnattenuatedZone(listenerZone); - } else { - stream->setListenerUnattenuatedZone(NULL); - } } } diff --git a/assignment-client/src/audio/AudioMixerClientData.h b/assignment-client/src/audio/AudioMixerClientData.h index 61d2b54e71..ce43229286 100644 --- a/assignment-client/src/audio/AudioMixerClientData.h +++ b/assignment-client/src/audio/AudioMixerClientData.h @@ -42,7 +42,7 @@ public: int parseData(const QByteArray& packet); - void checkBuffersBeforeFrameSend(AABox* checkSourceZone, AABox* listenerZone); + void checkBuffersBeforeFrameSend(); void removeDeadInjectedStreams(); diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 74db2c1e52..2c33897d07 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -6,7 +6,7 @@ { "name": "access_token", "label": "Access Token", - "help": "This is an access token generated on the My Tokens page of your High Fidelity account.
Generate a token with the 'domains' scope and paste it here.
This is required to associate this domain-server with a domain in your account." + "help": "This is an access token generated on the My Security page of your High Fidelity account.
Generate a token with the 'domains' scope and paste it here.
This is required to associate this domain-server with a domain in your account." }, { "name": "id", @@ -64,7 +64,7 @@ "name": "allowed_users", "type": "table", "label": "Allowed Users", - "help": "List the High Fidelity names for people you want to be able to connect to this domain.
An empty list means everyone.
You can always connect from this machine.", + "help": "List the High Fidelity names for people you want to be able to connect to this domain.
An empty list means everyone.
You can always connect from the domain-server machine.", "numbered": false, "columns": [ { @@ -77,10 +77,24 @@ ] }, { - "name": "audio", - "label": "Audio", + "name": "audio_env", + "label": "Audio Environment", "assignment-types": [0], "settings": [ + { + "name": "attenuation_per_doubling_in_distance", + "label": "Default Domain Attenuation", + "help": "Factor between 0 and 1.0 (0: No attenuation, 1.0: extreme attenuation)", + "placeholder": "0.18", + "default": "0.18", + "advanced": false + }, + { + "name": "enable_filter", + "type": "checkbox", + "help": "positional audio stream uses lowpass filter", + "default": true + }, { "name": "zones", "type": "table", @@ -90,7 +104,7 @@ "key": { "name": "name", "label": "Name", - "placeholder": "Zone name" + "placeholder": "Zone_Name" }, "columns": [ { @@ -118,7 +132,8 @@ "type": "table", "label": "Attenuation Coefficients", "help": "In this table you can set custom attenuation coefficients between audio zones", - "numbered": false, + "numbered": true, + "can_order": true, "columns": [ { "name": "source", @@ -139,28 +154,14 @@ "placeholder": "0.18" } ] - }, - { - "name": "enable_filter", - "type": "checkbox", - "label": "Positional filter", - "help": "positional audio stream uses lowpass filter", - "default": true - }, - { - "name": "unattenuated_zone", - "label": "Unattenuated Zone", - "help": "Boxes for source and listener (corner x, corner y, corner z, size x, size y, size z, corner x, corner y, corner z, size x, size y, size z)", - "placeholder": "no zone" - }, - { - "name": "attenuation_per_doubling_in_distance", - "label": "Attenuation per doubling in distance", - "help": "Factor between 0.0 and 1.0 (0.0: No attenuation, 1.0: extreme attenuation)", - "placeholder": "0.18", - "default": "0.18", - "advanced": false - }, + } + ] + }, + { + "name": "audio_buffer", + "label": "Audio Buffers", + "assignment-types": [0], + "settings": [ { "name": "dynamic_jitter_buffer", "type": "checkbox", diff --git a/domain-server/resources/web/css/style.css b/domain-server/resources/web/css/style.css index ad889274d4..1aab383a7d 100644 --- a/domain-server/resources/web/css/style.css +++ b/domain-server/resources/web/css/style.css @@ -79,12 +79,19 @@ td.buttons { width: 14px; } -td.buttons .glyphicon { - display: block; +td .glyphicon { text-align: center; font-size: 12px; } +td.add-del-buttons .glyphicon { + display: block; +} + +td.reorder-buttons .glyphicon { + display: inherit; +} + tr.new-row { color: #3c763d; background-color: #dff0d8; diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index 3f237b1aed..c2223a8f55 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -8,7 +8,15 @@ var Settings = { ADD_ROW_SPAN_CLASSES: 'glyphicon glyphicon-plus add-row', DEL_ROW_BUTTON_CLASS: 'del-row', DEL_ROW_SPAN_CLASSES: 'glyphicon glyphicon-remove del-row', + MOVE_UP_BUTTON_CLASS: 'move-up', + MOVE_UP_SPAN_CLASSES: 'glyphicon glyphicon-chevron-up move-up', + MOVE_DOWN_BUTTON_CLASS: 'move-down', + MOVE_DOWN_SPAN_CLASSES: 'glyphicon glyphicon-chevron-down move-down', TABLE_BUTTONS_CLASS: 'buttons', + ADD_DEL_BUTTONS_CLASS: 'add-del-buttons', + ADD_DEL_BUTTONS_CLASSES: 'buttons add-del-buttons', + REORDER_BUTTONS_CLASS: 'reorder-buttons', + REORDER_BUTTONS_CLASSES: 'buttons reorder-buttons', NEW_ROW_CLASS: 'new-row' }; @@ -35,7 +43,9 @@ var viewHelpers = { + " " + Settings.TRIGGER_CHANGE_CLASS + "' data-short-name='" + setting.name + "' name='" + setting_name + "' " if (setting.type === 'checkbox') { - form_group += "" + if (setting.label) { + form_group += "" + } form_group += "
" form_group += "
@@ -32,25 +32,32 @@ - +
diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index bbfa01c709..81835fb50a 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -615,60 +616,62 @@ const QString ALLOWED_USERS_SETTINGS_KEYPATH = "security.allowed_users"; bool DomainServer::shouldAllowConnectionFromNode(const QString& username, const QByteArray& usernameSignature, const HifiSockAddr& senderSockAddr) { - static const QVariant* allowedUsersVariant = valueForKeyPath(_settingsManager.getSettingsMap(), + const QVariant* allowedUsersVariant = valueForKeyPath(_settingsManager.getSettingsMap(), ALLOWED_USERS_SETTINGS_KEYPATH); - static QStringList allowedUsers = allowedUsersVariant ? allowedUsersVariant->toStringList() : QStringList(); + QStringList allowedUsers = allowedUsersVariant ? allowedUsersVariant->toStringList() : QStringList(); + + // we always let in a user who is sending a packet from our local socket or from the localhost address + if (senderSockAddr.getAddress() == LimitedNodeList::getInstance()->getLocalSockAddr().getAddress() + || senderSockAddr.getAddress() == QHostAddress::LocalHost) { + return true; + } if (allowedUsers.count() > 0) { - // this is an agent, we need to ask them to provide us with their signed username to see if they are allowed in - // we always let in a user who is sending a packet from our local socket or from the localhost address - - if (senderSockAddr.getAddress() != LimitedNodeList::getInstance()->getLocalSockAddr().getAddress() - && senderSockAddr.getAddress() != QHostAddress::LocalHost) { - if (allowedUsers.contains(username)) { - // it's possible this user can be allowed to connect, but we need to check their username signature - - QByteArray publicKeyArray = _userPublicKeys.value(username); - if (!publicKeyArray.isEmpty()) { - // if we do have a public key for the user, check for a signature match - - const unsigned char* publicKeyData = reinterpret_cast(publicKeyArray.constData()); - - // first load up the public key into an RSA struct - RSA* rsaPublicKey = d2i_RSAPublicKey(NULL, &publicKeyData, publicKeyArray.size()); - - if (rsaPublicKey) { - QByteArray decryptedArray(RSA_size(rsaPublicKey), 0); - int decryptResult = RSA_public_decrypt(usernameSignature.size(), - reinterpret_cast(usernameSignature.constData()), - reinterpret_cast(decryptedArray.data()), - rsaPublicKey, RSA_PKCS1_PADDING); - - if (decryptResult != -1) { - if (username == decryptedArray) { - qDebug() << "Username signature matches for" << username << "- allowing connection."; - - // free up the public key before we return - RSA_free(rsaPublicKey); - - return true; - } else { - qDebug() << "Username signature did not match for" << username << "- denying connection."; - } - } else { - qDebug() << "Couldn't decrypt user signature for" << username << "- denying connection."; - } - - // free up the public key, we don't need it anymore - RSA_free(rsaPublicKey); - } else { - // we can't let this user in since we couldn't convert their public key to an RSA key we could use - qDebug() << "Couldn't convert data to RSA key for" << username << "- denying connection."; - } - } + if (allowedUsers.contains(username)) { + // it's possible this user can be allowed to connect, but we need to check their username signature - requestUserPublicKey(username); + QByteArray publicKeyArray = _userPublicKeys.value(username); + if (!publicKeyArray.isEmpty()) { + // if we do have a public key for the user, check for a signature match + + const unsigned char* publicKeyData = reinterpret_cast(publicKeyArray.constData()); + + // first load up the public key into an RSA struct + RSA* rsaPublicKey = d2i_RSA_PUBKEY(NULL, &publicKeyData, publicKeyArray.size()); + + if (rsaPublicKey) { + QByteArray decryptedArray(RSA_size(rsaPublicKey), 0); + int decryptResult = RSA_public_decrypt(usernameSignature.size(), + reinterpret_cast(usernameSignature.constData()), + reinterpret_cast(decryptedArray.data()), + rsaPublicKey, RSA_PKCS1_PADDING); + + if (decryptResult != -1) { + if (username == decryptedArray) { + qDebug() << "Username signature matches for" << username << "- allowing connection."; + + // free up the public key before we return + RSA_free(rsaPublicKey); + + return true; + } else { + qDebug() << "Username signature did not match for" << username << "- denying connection."; + } + } else { + qDebug() << "Couldn't decrypt user signature for" << username << "- denying connection."; + } + + // free up the public key, we don't need it anymore + RSA_free(rsaPublicKey); + } else { + // we can't let this user in since we couldn't convert their public key to an RSA key we could use + qDebug() << "Couldn't convert data to RSA key for" << username << "- denying connection."; + } } + + requestUserPublicKey(username); + } else { + qDebug() << "Connect request denied for user" << username << "not in allowed users list."; } } else { // since we have no allowed user list, let them all in @@ -1205,17 +1208,20 @@ void DomainServer::processDatagram(const QByteArray& receivedPacket, const HifiS QDataStream packetStream(receivedPacket); packetStream.skipRawData(numBytesForPacketHeader(receivedPacket)); - int numNodeInfoBytes = parseNodeDataFromByteArray(packetStream, throwawayNodeType, - nodePublicAddress, nodeLocalAddress, - senderSockAddr); + parseNodeDataFromByteArray(packetStream, throwawayNodeType, nodePublicAddress, nodeLocalAddress, + senderSockAddr); - SharedNodePointer checkInNode = nodeList->updateSocketsForNode(nodeUUID, nodePublicAddress, nodeLocalAddress); + SharedNodePointer checkInNode = nodeList->updateSocketsForNode(nodeUUID, + nodePublicAddress, nodeLocalAddress); // update last receive to now quint64 timeNow = usecTimestampNow(); checkInNode->setLastHeardMicrostamp(timeNow); - sendDomainListToNode(checkInNode, senderSockAddr, nodeInterestListFromPacket(receivedPacket, numNodeInfoBytes)); + QList nodeInterestList; + packetStream >> nodeInterestList; + + sendDomainListToNode(checkInNode, senderSockAddr, nodeInterestList.toSet()); } break; diff --git a/examples/libraries/entityCameraTool.js b/examples/libraries/entityCameraTool.js new file mode 100644 index 0000000000..8e267e9544 --- /dev/null +++ b/examples/libraries/entityCameraTool.js @@ -0,0 +1,246 @@ +// +// entityCameraTool.js +// examples +// +// Created by Ryan Huffman on 10/14/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var MOUSE_SENSITIVITY = 0.9; +var SCROLL_SENSITIVITY = 0.05; +var PAN_ZOOM_SCALE_RATIO = 0.4; + +// Scaling applied based on the size of the object being focused +var FOCUS_ZOOM_SCALE = 1.3; + +// Minimum zoom level when focusing on an object +var FOCUS_MIN_ZOOM = 0.5; + +// Scaling applied based on the current zoom level +var ZOOM_SCALING = 0.02; + +var MIN_ZOOM_DISTANCE = 0.01; +var MAX_ZOOM_DISTANCE = 200; + +var MODE_INACTIVE = null; +var MODE_ORBIT = 'orbit'; +var MODE_PAN = 'pan'; + +var EASING_MULTIPLIER = 8; + +var INITIAL_ZOOM_DISTANCE = 2; +var INITIAL_ZOOM_DISTANCE_FIRST_PERSON = 3; + +EntityCameraTool = function() { + var that = {}; + + that.enabled = false; + that.mode = MODE_INACTIVE; + + that.zoomDistance = INITIAL_ZOOM_DISTANCE; + that.targetZoomDistance = INITIAL_ZOOM_DISTANCE; + + that.yaw = 0; + that.pitch = 0; + that.targetYaw = 0; + that.targetPitch = 0; + + that.focalPoint = { x: 0, y: 0, z: 0 }; + that.targetFocalPoint = { x: 0, y: 0, z: 0 }; + + that.previousCameraMode = null; + + that.lastMousePosition = { x: 0, y: 0 }; + + that.enable = function() { + if (that.enabled) return; + that.enabled = true; + that.mode = MODE_INACTIVE; + + // Pick a point INITIAL_ZOOM_DISTANCE in front of the camera to use as a focal point + that.zoomDistance = INITIAL_ZOOM_DISTANCE; + that.targetZoomDistance = that.zoomDistance; + var focalPoint = Vec3.sum(Camera.getPosition(), + Vec3.multiply(that.zoomDistance, Quat.getFront(Camera.getOrientation()))); + + if (Camera.getMode() == 'first person') { + that.targetZoomDistance = INITIAL_ZOOM_DISTANCE_FIRST_PERSON; + } + + // Determine the correct yaw and pitch to keep the camera in the same location + var dPos = Vec3.subtract(focalPoint, Camera.getPosition()); + var xzDist = Math.sqrt(dPos.x * dPos.x + dPos.z * dPos.z); + + that.targetPitch = -Math.atan2(dPos.y, xzDist) * 180 / Math.PI; + that.targetYaw = Math.atan2(dPos.x, dPos.z) * 180 / Math.PI; + that.pitch = that.targetPitch; + that.yaw = that.targetYaw; + + that.focalPoint = focalPoint; + that.setFocalPoint(focalPoint); + that.previousCameraMode = Camera.getMode(); + Camera.setMode("independent"); + + that.updateCamera(); + } + + that.disable = function() { + if (!that.enabled) return; + that.enabled = false; + that.mode = MODE_INACTIVE; + + Camera.setMode(that.previousCameraMode); + } + + that.focus = function(entityProperties) { + var dim = entityProperties.dimensions; + var size = Math.max(dim.x, Math.max(dim.y, dim.z)); + + that.targetZoomDistance = Math.max(size * FOCUS_ZOOM_SCALE, FOCUS_MIN_ZOOM); + + that.setFocalPoint(entityProperties.position); + + that.updateCamera(); + } + + that.moveFocalPoint = function(dPos) { + that.setFocalPoint(Vec3.sum(that.focalPoint, dPos)); + } + + that.setFocalPoint = function(pos) { + that.targetFocalPoint = pos + that.updateCamera(); + } + + that.mouseMoveEvent = function(event) { + if (that.enabled && that.mode != MODE_INACTIVE) { + if (that.mode == MODE_ORBIT) { + var diffX = event.x - that.lastMousePosition.x; + var diffY = event.y - that.lastMousePosition.y; + that.targetYaw -= MOUSE_SENSITIVITY * (diffX / 5.0) + that.targetPitch += MOUSE_SENSITIVITY * (diffY / 10.0) + + while (that.targetYaw > 180.0) that.targetYaw -= 360; + while (that.targetYaw < -180.0) that.targetYaw += 360; + + if (that.targetPitch > 90) that.targetPitch = 90; + if (that.targetPitch < -90) that.targetPitch = -90; + + that.updateCamera(); + } else if (that.mode == MODE_PAN) { + var diffX = event.x - that.lastMousePosition.x; + var diffY = event.y - that.lastMousePosition.y; + + var up = Quat.getUp(Camera.getOrientation()); + var right = Quat.getRight(Camera.getOrientation()); + + up = Vec3.multiply(up, diffY * 0.01 * PAN_ZOOM_SCALE_RATIO * that.zoomDistance); + right = Vec3.multiply(right, -diffX * 0.01 * PAN_ZOOM_SCALE_RATIO * that.zoomDistance); + + var dPosition = Vec3.sum(up, right); + + that.moveFocalPoint(dPosition); + } + that.lastMousePosition.x = event.x; + that.lastMousePosition.y = event.y; + + return true; + } + return false; + } + + that.mousePressEvent = function(event) { + if (!that.enabled) return; + + if (event.isRightButton || (event.isLeftButton && event.isControl && !event.isShifted)) { + that.mode = MODE_ORBIT; + that.lastMousePosition.x = event.x; + that.lastMousePosition.y = event.y; + return true; + } else if (event.isMiddleButton || (event.isLeftButton && event.isControl && event.isShifted)) { + that.mode = MODE_PAN; + that.lastMousePosition.x = event.x; + that.lastMousePosition.y = event.y; + return true; + } + + return false; + } + + that.mouseReleaseEvent = function(event) { + if (!that.enabled) return; + + that.mode = MODE_INACTIVE; + } + + that.wheelEvent = function(event) { + if (!that.enabled) return; + + var dZoom = -event.delta * SCROLL_SENSITIVITY; + + // Scale based on current zoom level + dZoom *= that.targetZoomDistance * ZOOM_SCALING; + + that.targetZoomDistance = Math.max(Math.min(that.targetZoomDistance + dZoom, MAX_ZOOM_DISTANCE), MIN_ZOOM_DISTANCE); + + that.updateCamera(); + } + + that.updateCamera = function() { + if (!that.enabled) return; + + var yRot = Quat.angleAxis(that.yaw, { x: 0, y: 1, z: 0 }); + var xRot = Quat.angleAxis(that.pitch, { x: 1, y: 0, z: 0 }); + var q = Quat.multiply(yRot, xRot); + + var pos = Vec3.multiply(Quat.getFront(q), that.zoomDistance); + Camera.setPosition(Vec3.sum(that.focalPoint, pos)); + + yRot = Quat.angleAxis(that.yaw - 180, { x: 0, y: 1, z: 0 }); + xRot = Quat.angleAxis(-that.pitch, { x: 1, y: 0, z: 0 }); + q = Quat.multiply(yRot, xRot); + + Camera.setOrientation(q); + } + + function normalizeDegrees(degrees) { + while (degrees > 180) degrees -= 360; + while (degrees < -180) degrees += 360; + return degrees; + } + + // Ease the position and orbit of the camera + that.update = function(dt) { + var scale = Math.min(dt * EASING_MULTIPLIER, 1.0); + + var dYaw = that.targetYaw - that.yaw; + if (dYaw > 180) dYaw -= 360; + if (dYaw < -180) dYaw += 360; + + var dPitch = that.targetPitch - that.pitch; + + that.yaw += scale * dYaw; + that.pitch += scale * dPitch; + + // Normalize between [-180, 180] + that.yaw = normalizeDegrees(that.yaw); + that.pitch = normalizeDegrees(that.pitch); + + var dFocal = Vec3.subtract(that.targetFocalPoint, that.focalPoint); + that.focalPoint = Vec3.sum(that.focalPoint, Vec3.multiply(scale, dFocal)); + + var dZoom = that.targetZoomDistance - that.zoomDistance; + that.zoomDistance += scale * dZoom; + + that.updateCamera(); + } + + Script.update.connect(that.update); + + Controller.wheelEvent.connect(that.wheelEvent); + + return that; +} diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 503d4415de..75ea95a74a 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -17,6 +17,8 @@ SelectionDisplay = (function () { var that = {}; var MINIMUM_DIMENSION = 0.001; + + var GRABBER_DISTANCE_TO_SIZE_RATIO = 0.015; var mode = "UNKNOWN"; var overlayNames = new Array(); @@ -174,6 +176,34 @@ SelectionDisplay = (function () { var grabberEdgeFR = Overlays.addOverlay("cube", grabberPropertiesEdge); var grabberEdgeFL = Overlays.addOverlay("cube", grabberPropertiesEdge); + var cornerEdgeFaceGrabbers = [ + grabberLBN, + grabberRBN, + grabberLBF, + grabberRBF, + grabberLTN, + grabberRTN, + grabberLTF, + grabberRTF, + grabberTOP, + grabberBOTTOM, + grabberLEFT, + grabberRIGHT, + grabberNEAR, + grabberFAR, + grabberEdgeTR, + grabberEdgeTL, + grabberEdgeTF, + grabberEdgeTN, + grabberEdgeBR, + grabberEdgeBL, + grabberEdgeBF, + grabberEdgeBN, + grabberEdgeNR, + grabberEdgeNL, + grabberEdgeFR, + grabberEdgeFL, + ]; var baseOverlayAngles = { x: 0, y: 0, z: 0 }; var baseOverlayRotation = Quat.fromVec3Degrees(baseOverlayAngles); @@ -2423,6 +2453,32 @@ SelectionDisplay = (function () { return true; }; + that.updateHandleSizes = function() { + if (selectedEntityProperties) { + var diff = Vec3.subtract(selectedEntityProperties.position, Camera.getPosition()); + var grabberSize = Vec3.length(diff) * GRABBER_DISTANCE_TO_SIZE_RATIO; + for (var i = 0; i < cornerEdgeFaceGrabbers.length; i++) { + Overlays.editOverlay(cornerEdgeFaceGrabbers[i], { + size: grabberSize, + }); + } + var handleSize = Vec3.length(diff) * GRABBER_DISTANCE_TO_SIZE_RATIO * 5; + Overlays.editOverlay(yawHandle, { + scale: handleSize, + }); + Overlays.editOverlay(pitchHandle, { + scale: handleSize, + }); + Overlays.editOverlay(rollHandle, { + scale: handleSize, + }); + Overlays.editOverlay(grabberMoveUp, { + scale: handleSize, + }); + } + } + Script.update.connect(that.updateHandleSizes); + that.mouseReleaseEvent = function(event) { var showHandles = false; // hide our rotation overlays..., and show our handles diff --git a/examples/metavoxels.js b/examples/metavoxels.js new file mode 100644 index 0000000000..32177cdcba --- /dev/null +++ b/examples/metavoxels.js @@ -0,0 +1,41 @@ +// +// metavoxels.js +// examples +// +// 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 +// + +Script.setInterval(function() { + var spanner; + if (Math.random() < 0.5) { + spanner = new Sphere(); + } else { + spanner = new Cuboid(); + spanner.aspectX = 0.1 + Math.random() * 1.9; + spanner.aspectY = 0.1 + Math.random() * 1.9; + } + spanner.scale = 0.1 + Math.random() * 1.9; + spanner.rotation = Quat.fromPitchYawRollDegrees(Math.random() * 360.0, Math.random() * 360.0, Math.random() * 360.0); + spanner.translation = { x: 10.0 + Math.random() * 10.0, y: 10.0 + Math.random() * 10.0, z: 10.0 + Math.random() * 10.0 }; + + if (Math.random() < 0.5) { + var material = new MaterialObject(); + if (Math.random() < 0.5) { + material.diffuse = "http://www.fungibleinsight.com/faces/grass.jpg"; + } else { + material.diffuse = "http://www.fungibleinsight.com/faces/soil.jpg"; + } + Metavoxels.setVoxelMaterial(spanner, material); + + } else if (Math.random() < 0.5) { + Metavoxels.setVoxelColor(spanner, { red: Math.random() * 255.0, green: Math.random() * 255.0, + blue: Math.random() * 255.0 }); + + } else { + Metavoxels.setVoxelColor(spanner, { red: 0, green: 0, blue: 0, alpha: 0 }); + } +}, 1000); + diff --git a/examples/newEditEntities.js b/examples/newEditEntities.js index e05416d5a7..756fbdb645 100644 --- a/examples/newEditEntities.js +++ b/examples/newEditEntities.js @@ -31,6 +31,9 @@ Script.include("libraries/ToolTip.js"); Script.include("libraries/entityPropertyDialogBox.js"); var entityPropertyDialogBox = EntityPropertyDialogBox; +Script.include("libraries/entityCameraTool.js"); +var entityCameraTool = new EntityCameraTool(); + var windowDimensions = Controller.getViewportDimensions(); var toolIconUrl = HIFI_PUBLIC_BUCKET + "images/tools/"; var toolHeight = 50; @@ -163,6 +166,9 @@ var toolBar = (function () { Overlays.editOverlay(loadFileMenuItem, { visible: active }); } + var RESIZE_INTERVAL = 50; + var RESIZE_TIMEOUT = 20000; + var RESIZE_MAX_CHECKS = RESIZE_TIMEOUT / RESIZE_INTERVAL; function addModel(url) { var position; @@ -176,6 +182,27 @@ var toolBar = (function () { modelURL: url }); print("Model added: " + url); + + var checkCount = 0; + function resize() { + var entityProperties = Entities.getEntityProperties(entityId); + var naturalDimensions = entityProperties.naturalDimensions; + + checkCount++; + + if (naturalDimensions.x == 0 && naturalDimensions.y == 0 && naturalDimensions.z == 0) { + if (checkCount < RESIZE_MAX_CHECKS) { + Script.setTimeout(resize, RESIZE_INTERVAL); + } else { + print("Resize failed: timed out waiting for model (" + url + ") to load"); + } + } else { + entityProperties.dimensions = naturalDimensions; + Entities.editEntity(entityId, entityProperties); + } + } + + Script.setTimeout(resize, RESIZE_INTERVAL); } else { print("Can't add model: Model would be out of bounds."); } @@ -217,6 +244,9 @@ var toolBar = (function () { isActive = !isActive; if (!isActive) { selectionDisplay.unselectAll(); + entityCameraTool.disable(); + } else { + entityCameraTool.enable(); } return true; } @@ -292,6 +322,7 @@ var toolBar = (function () { } + return false; }; @@ -339,7 +370,8 @@ function mousePressEvent(event) { mouseLastPosition = { x: event.x, y: event.y }; var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); - if (toolBar.mousePressEvent(event) || progressDialog.mousePressEvent(event) || selectionDisplay.mousePressEvent(event)) { + if (toolBar.mousePressEvent(event) || progressDialog.mousePressEvent(event) + || entityCameraTool.mousePressEvent(event) || selectionDisplay.mousePressEvent(event)) { // Event handled; do nothing. return; } else { @@ -442,8 +474,8 @@ function mouseMoveEvent(event) { return; } - // allow the selectionDisplay to handle the event first, if it doesn't handle it, then do our own thing - if (selectionDisplay.mouseMoveEvent(event)) { + // allow the selectionDisplay and entityCameraTool to handle the event first, if it doesn't handle it, then do our own thing + if (selectionDisplay.mouseMoveEvent(event) || entityCameraTool.mouseMoveEvent(event)) { return; } @@ -482,6 +514,7 @@ function mouseReleaseEvent(event) { if (entitySelected) { tooltip.show(false); } + entityCameraTool.mouseReleaseEvent(event); } Controller.mousePressEvent.connect(mousePressEvent); @@ -613,6 +646,10 @@ Controller.keyReleaseEvent.connect(function (event) { } if (event.text == "BACKSPACE") { handeMenuEvent("Delete"); + } else if (event.text == "f") { + if (entitySelected) { + entityCameraTool.focus(selectedEntityProperties); + } } }); diff --git a/hifi.podspec b/hifi.podspec deleted file mode 100644 index e721ada8f9..0000000000 --- a/hifi.podspec +++ /dev/null @@ -1,101 +0,0 @@ -# -# Be sure to run `pod spec lint hifi.podspec' to ensure this is a -# valid spec and remove all comments before submitting the spec. -# -# To learn more about the attributes see http://docs.cocoapods.org/specification.html -# -Pod::Spec.new do |s| - s.name = "hifi" - s.version = "0.0.1" - s.summary = "Test platform for various render and interface tests for next-gen VR system." - - s.homepage = "https://github.com/worklist/hifi" - - # Specify the license type. CocoaPods detects automatically the license file if it is named - # 'LICENCE*.*' or 'LICENSE*.*', however if the name is different, specify it. - # s.license = 'MIT (example)' - - # Specify the authors of the library, with email addresses. You can often find - # the email addresses of the authors by using the SCM log. E.g. $ git log - # - s.author = { "Worklist" => "contact@worklist.net" } - - # Specify the location from where the source should be retrieved. - # - s.source = { :git => "https://github.com/worklist/hifi.git" } - - s.platform = :ios - s.ios.deployment_target = "6.0" - - # A list of file patterns which select the source files that should be - # added to the Pods project. If the pattern is a directory then the - # path will automatically have '*.{h,m,mm,c,cpp}' appended. - # - # s.source_files = 'Classes', 'Classes/**/*.{h,m}' - # s.exclude_files = 'Classes/Exclude' - - s.subspec "shared" do |sp| - sp.source_files = 'libraries/shared/src', 'libraries/shared/moc_*' - sp.exclude_files = "libraries/shared/src/UrlReader.*" - sp.dependency 'glm' - sp.xcconfig = { 'CLANG_CXX_LIBRARY' => "libc++" } - end - - s.subspec "audio" do |sp| - sp.source_files = "libraries/audio/src" - sp.dependency 'glm' - end - - s.subspec "avatars" do |sp| - sp.source_files = 'libraries/avatars/src', 'libraries/avatars/moc_*' - sp.dependency 'glm' - end - - s.subspec "voxels" do |sp| - sp.source_files = 'libraries/voxels/src', 'libraries/voxels/moc_*' - sp.dependency 'glm' - end - - s.xcconfig = { 'HEADER_SEARCH_PATHS' => '${PODS_ROOT}/../../qt5-device/qtbase/include' } - s.libraries = 'libQtCoreCombined', 'libQt5Network', 'libQt5Script' - - # A list of file patterns which select the header files that should be - # made available to the application. If the pattern is a directory then the - # path will automatically have '*.h' appended. - # - # If you do not explicitly set the list of public header files, - # all headers of source_files will be made public. - # - # s.public_header_files = 'Classes/**/*.h' - - # A list of paths to preserve after installing the Pod. - # CocoaPods cleans by default any file that is not used. - # Please don't include documentation, example, and test files. - # - # s.preserve_paths = "FilesToSave", "MoreFilesToSave" - - # Specify a list of frameworks that the application needs to link - # against for this Pod to work. - # - # s.framework = 'SomeFramework' - # s.frameworks = 'SomeFramework', 'AnotherFramework' - - # Specify a list of libraries that the application needs to link - # against for this Pod to work. - # - # s.library = 'iconv' - # s.libraries = 'iconv', 'xml2' - - # If this Pod uses ARC, specify it like so. - # - s.requires_arc = false - - # If you need to specify any other build settings, add them to the - # xcconfig hash. - # - # s.xcconfig = { 'CLANG_CXX_LIBRARY' => "libc++" } - - # Finally, specify any Pods that this Pod depends on. - # - # s.dependency 'JSONKit', '~> 1.4' -end diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index e6e49750d1..a85181b89e 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -2,7 +2,7 @@ set(TARGET_NAME interface) project(${TARGET_NAME}) # set a default root dir for each of our optional externals if it was not passed -set(OPTIONAL_EXTERNALS "Faceplus" "Faceshift" "LibOVR" "PrioVR" "Sixense" "Visage" "LeapMotion" "RtMidi" "Qxmpp" "SDL2") +set(OPTIONAL_EXTERNALS "Faceshift" "LibOVR" "PrioVR" "Sixense" "Visage" "LeapMotion" "RtMidi" "Qxmpp" "SDL2") foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) string(TOUPPER ${EXTERNAL} ${EXTERNAL}_UPPERCASE) if (NOT ${${EXTERNAL}_UPPERCASE}_ROOT_DIR) diff --git a/interface/external/faceplus/readme.txt b/interface/external/faceplus/readme.txt deleted file mode 100644 index e98f8becdc..0000000000 --- a/interface/external/faceplus/readme.txt +++ /dev/null @@ -1,11 +0,0 @@ - -Instructions for adding the Faceplus driver to Interface -Andrzej Kapolka, April 8, 2014 - -1. Copy the Faceplus sdk folders (include, win32) into the interface/external/faceplus folder. - This readme.txt should be there as well. - -2. Copy the Faceplus DLLs from the win32 folder into your path. - -3. Delete your build directory, run cmake and build, and you should be all set. - diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0079631943..b85e1e4440 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -177,7 +177,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _runningScriptsWidgetWasVisible(false), _trayIcon(new QSystemTrayIcon(_window)), _lastNackTime(usecTimestampNow()), - _lastSendDownstreamAudioStats(usecTimestampNow()) + _lastSendDownstreamAudioStats(usecTimestampNow()), + _renderResolutionScale(1.0f) { // read the ApplicationInfo.ini file for Name/Version/Domain information QSettings applicationInfo(Application::resourcesPath() + "info/ApplicationInfo.ini", QSettings::IniFormat); @@ -587,7 +588,8 @@ void Application::paintGL() { if (OculusManager::isConnected()) { _textureCache.setFrameBufferSize(OculusManager::getRenderTargetSize()); } else { - _textureCache.setFrameBufferSize(_glWidget->getDeviceSize()); + QSize fbSize = _glWidget->getDeviceSize() * _renderResolutionScale; + _textureCache.setFrameBufferSize(fbSize); } glEnable(GL_LINE_SMOOTH); @@ -685,6 +687,10 @@ void Application::paintGL() { } else { _glowEffect.prepare(); + // Viewport is assigned to the size of the framebuffer + QSize size = Application::getInstance()->getTextureCache()->getPrimaryFramebufferObject()->size(); + glViewport(0, 0, size.width(), size.height()); + glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); @@ -1540,9 +1546,8 @@ glm::vec3 Application::getMouseVoxelWorldCoordinates(const VoxelDetail& mouseVox FaceTracker* Application::getActiveFaceTracker() { return (_dde.isActive() ? static_cast(&_dde) : - (_faceshift.isActive() ? static_cast(&_faceshift) : - (_faceplus.isActive() ? static_cast(&_faceplus) : - (_visage.isActive() ? static_cast(&_visage) : NULL)))); + (_faceshift.isActive() ? static_cast(&_faceshift) : + (_visage.isActive() ? static_cast(&_visage) : NULL))); } struct SendVoxelsOperationArgs { @@ -1830,7 +1835,6 @@ void Application::init() { // initialize our face trackers after loading the menu settings _faceshift.init(); - _faceplus.init(); _visage.init(); Leapmotion::init(); @@ -3076,6 +3080,10 @@ glm::vec2 Application::getScaledScreenPoint(glm::vec2 projectedPoint) { } void Application::renderRearViewMirror(const QRect& region, bool billboard) { + // Grab current viewport to reset it at the end + int viewport[4]; + glGetIntegerv(GL_VIEWPORT, viewport); + bool eyeRelativeCamera = false; if (billboard) { _mirrorCamera.setFieldOfView(BILLBOARD_FIELD_OF_VIEW); // degees @@ -3107,14 +3115,17 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) { // set the bounds of rear mirror view if (billboard) { - glViewport(region.x(), _glWidget->getDeviceHeight() - region.y() - region.height(), region.width(), region.height()); - glScissor(region.x(), _glWidget->getDeviceHeight() - region.y() - region.height(), region.width(), region.height()); + QSize size = getTextureCache()->getFrameBufferSize(); + glViewport(region.x(), size.height() - region.y() - region.height(), region.width(), region.height()); + glScissor(region.x(), size.height() - region.y() - region.height(), region.width(), region.height()); } else { // if not rendering the billboard, the region is in device independent coordinates; must convert to device + QSize size = getTextureCache()->getFrameBufferSize(); float ratio = QApplication::desktop()->windowHandle()->devicePixelRatio(); + ratio = size.height() / (float)_glWidget->getDeviceHeight(); int x = region.x() * ratio, y = region.y() * ratio, width = region.width() * ratio, height = region.height() * ratio; - glViewport(x, _glWidget->getDeviceHeight() - y - height, width, height); - glScissor(x, _glWidget->getDeviceHeight() - y - height, width, height); + glViewport(x, size.height() - y - height, width, height); + glScissor(x, size.height() - y - height, width, height); } bool updateViewFrustum = false; updateProjectionMatrix(_mirrorCamera, updateViewFrustum); @@ -3182,7 +3193,7 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) { } // reset Viewport and projection matrix - glViewport(0, 0, _glWidget->getDeviceWidth(), _glWidget->getDeviceHeight()); + glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); glDisable(GL_SCISSOR_TEST); updateProjectionMatrix(_myCamera, updateViewFrustum); } @@ -3367,7 +3378,6 @@ void Application::resetSensors() { _mouseX = _glWidget->width() / 2; _mouseY = _glWidget->height() / 2; - _faceplus.reset(); _faceshift.reset(); _visage.reset(); _dde.reset(); @@ -4217,3 +4227,7 @@ void Application::takeSnapshot() { } _snapshotShareDialog->show(); } + +void Application::setRenderResolutionScale(float scale) { + _renderResolutionScale = scale; +} diff --git a/interface/src/Application.h b/interface/src/Application.h index ce9ed0c1a6..3e64357063 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -58,7 +58,6 @@ #include "avatar/Avatar.h" #include "avatar/AvatarManager.h" #include "avatar/MyAvatar.h" -#include "devices/Faceplus.h" #include "devices/Faceshift.h" #include "devices/PrioVR.h" #include "devices/SixenseManager.h" @@ -214,7 +213,6 @@ public: int getMouseX() const { return _mouseX; } int getMouseY() const { return _mouseY; } bool getLastMouseMoveWasSimulated() const { return _lastMouseMoveWasSimulated;; } - Faceplus* getFaceplus() { return &_faceplus; } Faceshift* getFaceshift() { return &_faceshift; } Visage* getVisage() { return &_visage; } DdeFaceTracker* getDDE() { return &_dde; } @@ -301,6 +299,8 @@ public: bool isLookingAtMyAvatar(Avatar* avatar); + float getRenderResolutionScale() const { return _renderResolutionScale; } + signals: /// Fired when we're simulating; allows external parties to hook in. @@ -363,6 +363,8 @@ public slots: void domainSettingsReceived(const QJsonObject& domainSettingsObject); + void setRenderResolutionScale(float scale); + void resetSensors(); private slots: @@ -402,7 +404,6 @@ private: // Various helper functions called during update() void updateLOD(); void updateMouseRay(); - void updateFaceplus(); void updateFaceshift(); void updateVisage(); void updateDDE(); @@ -503,7 +504,6 @@ private: AvatarManager _avatarManager; MyAvatar* _myAvatar; // TODO: move this and relevant code to AvatarManager (or MyAvatar as the case may be) - Faceplus _faceplus; Faceshift _faceshift; Visage _visage; DdeFaceTracker _dde; @@ -618,6 +618,8 @@ private: quint64 _lastNackTime; quint64 _lastSendDownstreamAudioStats; + + float _renderResolutionScale; }; #endif // hifi_Application_h diff --git a/interface/src/Audio.h b/interface/src/Audio.h index e94e5ab16c..e397f9564b 100644 --- a/interface/src/Audio.h +++ b/interface/src/Audio.h @@ -26,7 +26,6 @@ #include "AudioSourceTone.h" #include "AudioSourceNoise.h" #include "AudioGain.h" -#include "AudioPan.h" #include "AudioFilter.h" #include "AudioFilterBank.h" diff --git a/interface/src/BuckyBalls.cpp b/interface/src/BuckyBalls.cpp index 0ea73556c5..ece9dd7dda 100644 --- a/interface/src/BuckyBalls.cpp +++ b/interface/src/BuckyBalls.cpp @@ -35,7 +35,6 @@ BuckyBalls::BuckyBalls() { colors[1] = glm::vec3(0.64f, 0.16f, 0.16f); colors[2] = glm::vec3(0.31f, 0.58f, 0.80f); - qDebug("Creating buckyballs..."); for (int i = 0; i < NUM_BBALLS; i++) { _bballPosition[i] = CORNER_BBALLS + randVector() * RANGE_BBALLS; int element = (rand() % NUM_ELEMENTS); diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp index 0232e0e71d..3334219dbd 100644 --- a/interface/src/DatagramProcessor.cpp +++ b/interface/src/DatagramProcessor.cpp @@ -140,6 +140,7 @@ void DatagramProcessor::processDatagrams() { // output to the log so the user knows they got a denied connection request // and check and signal for an access token so that we can make sure they are logged in qDebug() << "The domain-server denied a connection request."; + qDebug() << "You may need to re-log to generate a keypair so you can provide a username signature."; AccountManager::getInstance().checkAndSignalForAccessToken(); break; } diff --git a/interface/src/Hair.cpp b/interface/src/Hair.cpp index 810833d750..e9c110d4ac 100644 --- a/interface/src/Hair.cpp +++ b/interface/src/Hair.cpp @@ -8,7 +8,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -// Creates single flexible vertlet-integrated strands that can be used for hair/fur/grass +// Creates single flexible verlet-integrated strands that can be used for hair/fur/grass #include "Hair.h" @@ -17,13 +17,13 @@ const float HAIR_DAMPING = 0.99f; const float CONSTRAINT_RELAXATION = 10.0f; -const float HAIR_ACCELERATION_COUPLING = 0.025f; -const float HAIR_ANGULAR_VELOCITY_COUPLING = 0.01f; -const float HAIR_ANGULAR_ACCELERATION_COUPLING = 0.001f; +const float HAIR_ACCELERATION_COUPLING = 0.045f; +const float HAIR_ANGULAR_VELOCITY_COUPLING = 0.020f; +const float HAIR_ANGULAR_ACCELERATION_COUPLING = 0.003f; const float HAIR_MAX_LINEAR_ACCELERATION = 4.0f; -const float HAIR_STIFFNESS = 0.005f; -const glm::vec3 HAIR_COLOR1(0.98f, 0.92f, 0.843f); -const glm::vec3 HAIR_COLOR2(0.545f, 0.533f, 0.47f); +const float HAIR_STIFFNESS = 0.00f; +const glm::vec3 HAIR_COLOR1(0.98f, 0.76f, 0.075f); +const glm::vec3 HAIR_COLOR2(0.912f, 0.184f, 0.101f); Hair::Hair(int strands, int links, @@ -38,7 +38,8 @@ Hair::Hair(int strands, _acceleration(0.0f), _angularVelocity(0.0f), _angularAcceleration(0.0f), - _gravity(0.0f) + _gravity(0.0f), + _loudness(0.0f) { _hairPosition = new glm::vec3[_strands * _links]; _hairOriginalPosition = new glm::vec3[_strands * _links]; @@ -48,12 +49,15 @@ Hair::Hair(int strands, _hairColors = new glm::vec3[_strands * _links]; _hairIsMoveable = new int[_strands * _links]; _hairConstraints = new int[_strands * _links * HAIR_CONSTRAINTS]; // Hair can link to two others - const float FACE_WIDTH = PI / 4.0f; glm::vec3 thisVertex; for (int strand = 0; strand < _strands; strand++) { float strandAngle = randFloat() * PI; - float azimuth = FACE_WIDTH / 2.0f + (randFloat() * (2.0 * PI - FACE_WIDTH)); - float elevation = PI_OVER_TWO - (randFloat() * 0.75 * PI); + float azimuth; + float elevation = PI_OVER_TWO - (randFloat() * 0.10f * PI); + azimuth = PI_OVER_TWO; + if (randFloat() < 0.5f) { + azimuth *= -1.0f; + } glm::vec3 thisStrand(sinf(azimuth) * cosf(elevation), sinf(elevation), -cosf(azimuth) * cosf(elevation)); thisStrand *= _radius; @@ -77,7 +81,7 @@ Hair::Hair(int strands, _hairOriginalPosition[vertexIndex] = _hairLastPosition[vertexIndex] = _hairPosition[vertexIndex] = thisVertex; _hairQuadDelta[vertexIndex] = glm::vec3(cos(strandAngle) * _hairThickness, 0.f, sin(strandAngle) * _hairThickness); - _hairQuadDelta[vertexIndex] *= 1.f - ((float)link / _links); + _hairQuadDelta[vertexIndex] *= ((float)link / _links); _hairNormals[vertexIndex] = glm::normalize(randVector()); if (randFloat() < elevation / PI_OVER_TWO) { _hairColors[vertexIndex] = HAIR_COLOR1 * ((float)(link + 1) / (float)_links); @@ -86,7 +90,9 @@ Hair::Hair(int strands, } } } - } +} + +const float SOUND_THRESHOLD = 50.0f; void Hair::simulate(float deltaTime) { deltaTime = glm::clamp(deltaTime, 0.0f, 1.0f / 30.0f); @@ -114,9 +120,15 @@ void Hair::simulate(float deltaTime) { _hairPosition[vertexIndex] += glm::normalize(_hairPosition[vertexIndex]) * (_radius - glm::length(_hairPosition[vertexIndex])); } - + // Add random thing driven by loudness + float loudnessFactor = (_loudness > SOUND_THRESHOLD) ? logf(_loudness - SOUND_THRESHOLD) / 8000.0f : 0.0f; + + const float QUIESCENT_LOUDNESS = 0.0f; + _hairPosition[vertexIndex] += randVector() * (QUIESCENT_LOUDNESS + loudnessFactor) * ((float)link / (float)_links); + // Add gravity - _hairPosition[vertexIndex] += _gravity * deltaTime; + const float SCALE_GRAVITY = 0.10f; + _hairPosition[vertexIndex] += _gravity * deltaTime * SCALE_GRAVITY; // Add linear acceleration _hairPosition[vertexIndex] -= acceleration * HAIR_ACCELERATION_COUPLING * deltaTime; @@ -168,7 +180,7 @@ void Hair::simulate(float deltaTime) { } } - // Store start position for next vertlet pass + // Store start position for next verlet pass _hairLastPosition[vertexIndex] = thisPosition; } } @@ -179,11 +191,23 @@ void Hair::render() { // // Before calling this function, translate/rotate to the origin of the owning object // + float loudnessFactor = (_loudness > SOUND_THRESHOLD) ? logf(_loudness - SOUND_THRESHOLD) / 16.0f : 0.0f; + const int SPARKLE_EVERY = 5; + const float HAIR_SETBACK = 0.0f; + int sparkleIndex = (int) (randFloat() * SPARKLE_EVERY); + glPushMatrix(); + glTranslatef(0.f, 0.f, HAIR_SETBACK); glBegin(GL_QUADS); for (int strand = 0; strand < _strands; strand++) { for (int link = 0; link < _links - 1; link++) { int vertexIndex = strand * _links + link; - glColor3fv(&_hairColors[vertexIndex].x); + glm::vec3 thisColor = _hairColors[vertexIndex]; + if (sparkleIndex % SPARKLE_EVERY == 0) { + thisColor.x += (1.f - thisColor.x) * loudnessFactor; + thisColor.y += (1.f - thisColor.y) * loudnessFactor; + thisColor.z += (1.f - thisColor.z) * loudnessFactor; + } + glColor3fv(&thisColor.x); glNormal3fv(&_hairNormals[vertexIndex].x); glVertex3f(_hairPosition[vertexIndex].x - _hairQuadDelta[vertexIndex].x, _hairPosition[vertexIndex].y - _hairQuadDelta[vertexIndex].y, @@ -198,9 +222,11 @@ void Hair::render() { glVertex3f(_hairPosition[vertexIndex + 1].x - _hairQuadDelta[vertexIndex].x, _hairPosition[vertexIndex + 1].y - _hairQuadDelta[vertexIndex].y, _hairPosition[vertexIndex + 1].z - _hairQuadDelta[vertexIndex].z); + sparkleIndex++; } } glEnd(); + glPopMatrix(); } diff --git a/interface/src/Hair.h b/interface/src/Hair.h index f5180f639d..f2b5ebde6a 100644 --- a/interface/src/Hair.h +++ b/interface/src/Hair.h @@ -23,11 +23,11 @@ const int HAIR_CONSTRAINTS = 2; -const int DEFAULT_HAIR_STRANDS = 50; +const int DEFAULT_HAIR_STRANDS = 20; const int DEFAULT_HAIR_LINKS = 10; const float DEFAULT_HAIR_RADIUS = 0.15f; -const float DEFAULT_HAIR_LINK_LENGTH = 0.03f; -const float DEFAULT_HAIR_THICKNESS = 0.015f; +const float DEFAULT_HAIR_LINK_LENGTH = 0.07f; +const float DEFAULT_HAIR_THICKNESS = 0.025f; class Hair { public: @@ -42,6 +42,7 @@ public: void setAngularVelocity(const glm::vec3& angularVelocity) { _angularVelocity = angularVelocity; } void setAngularAcceleration(const glm::vec3& angularAcceleration) { _angularAcceleration = angularAcceleration; } void setGravity(const glm::vec3& gravity) { _gravity = gravity; } + void setLoudness(const float loudness) { _loudness = loudness; } private: int _strands; @@ -61,7 +62,7 @@ private: glm::vec3 _angularVelocity; glm::vec3 _angularAcceleration; glm::vec3 _gravity; - + float _loudness; }; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 08fc5067fb..b3a0a200d9 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -122,7 +122,8 @@ Menu::Menu() : _hasLoginDialogDisplayed(false), _snapshotsLocation(), _scriptsLocation(), - _walletPrivateKey() + _walletPrivateKey(), + _shouldRenderTableNeedsRebuilding(true) { Application *appInstance = Application::getInstance(); @@ -158,7 +159,7 @@ Menu::Menu() : addActionToQMenuAndActionHash(fileMenu, MenuOption::LoadScriptURL, Qt::CTRL | Qt::SHIFT | Qt::Key_O, appInstance, SLOT(loadScriptURLDialog())); addActionToQMenuAndActionHash(fileMenu, MenuOption::StopAllScripts, 0, appInstance, SLOT(stopAllScripts())); - addActionToQMenuAndActionHash(fileMenu, MenuOption::ReloadAllScripts, Qt::CTRL | Qt::SHIFT | Qt::Key_R, + addActionToQMenuAndActionHash(fileMenu, MenuOption::ReloadAllScripts, Qt::CTRL | Qt::Key_R, appInstance, SLOT(reloadAllScripts())); addActionToQMenuAndActionHash(fileMenu, MenuOption::RunningScripts, Qt::CTRL | Qt::Key_J, appInstance, SLOT(toggleRunningScriptsWidget())); @@ -377,7 +378,15 @@ Menu::Menu() : shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::SimpleShadows, 0, false)); shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::CascadedShadows, 0, false)); - + QMenu* resolutionMenu = renderOptionsMenu->addMenu(MenuOption::RenderResolution); + QActionGroup* resolutionGroup = new QActionGroup(resolutionMenu); + resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionOne, 0, false)); + resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionTwoThird, 0, false)); + resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionHalf, 0, false)); + resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionThird, 0, true)); + resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionQuarter, 0, false)); + connect(resolutionMenu, SIGNAL(triggered(QAction*)), this, SLOT(changeRenderResolution(QAction*))); + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars, Qt::Key_Asterisk, true); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Voxels, @@ -386,11 +395,7 @@ Menu::Menu() : appInstance, SLOT(setRenderVoxels(bool))); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::EnableGlowEffect, 0, true); - addActionToQMenuAndActionHash(renderOptionsMenu, - MenuOption::GlowMode, - 0, - appInstance->getGlowEffect(), - SLOT(cycleRenderMode())); + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Wireframe, Qt::ALT | Qt::Key_W, false); addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools, Qt::SHIFT | Qt::Key_L, this, SLOT(lodTools())); @@ -403,10 +408,6 @@ Menu::Menu() : appInstance->getFaceshift(), SLOT(setTCPEnabled(bool))); #endif -#ifdef HAVE_FACEPLUS - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::Faceplus, 0, true, - appInstance->getFaceplus(), SLOT(updateEnabled())); -#endif #ifdef HAVE_VISAGE addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::Visage, 0, false, appInstance->getVisage(), SLOT(updateEnabled())); @@ -422,6 +423,10 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(modelDebugMenu, MenuOption::DisplayModelBounds, 0, false); addCheckableActionToQMenuAndActionHash(modelDebugMenu, MenuOption::DisplayModelElementProxy, 0, false); addCheckableActionToQMenuAndActionHash(modelDebugMenu, MenuOption::DisplayModelElementChildProxies, 0, false); + QMenu* modelCullingMenu = modelDebugMenu->addMenu("Culling"); + addCheckableActionToQMenuAndActionHash(modelCullingMenu, MenuOption::DontCullOutOfViewMeshParts, 0, false); + addCheckableActionToQMenuAndActionHash(modelCullingMenu, MenuOption::DontCullTooSmallMeshParts, 0, false); + addCheckableActionToQMenuAndActionHash(modelCullingMenu, MenuOption::DontReduceMaterialSwitches, 0, false); QMenu* voxelOptionsMenu = developerMenu->addMenu("Voxels"); addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::VoxelTextures); @@ -1231,6 +1236,23 @@ void Menu::muteEnvironment() { free(packet); } +void Menu::changeRenderResolution(QAction* action) { + QString text = action->text(); + if (text == MenuOption::RenderResolutionOne) { + Application::getInstance()->setRenderResolutionScale(1.f); + } else if (text == MenuOption::RenderResolutionTwoThird) { + Application::getInstance()->setRenderResolutionScale(0.666f); + } else if (text == MenuOption::RenderResolutionHalf) { + Application::getInstance()->setRenderResolutionScale(0.5f); + } else if (text == MenuOption::RenderResolutionThird) { + Application::getInstance()->setRenderResolutionScale(0.333f); + } else if (text == MenuOption::RenderResolutionQuarter) { + Application::getInstance()->setRenderResolutionScale(0.25f); + } else { + Application::getInstance()->setRenderResolutionScale(1.f); + } +} + void Menu::displayNameLocationResponse(const QString& errorString) { if (!errorString.isEmpty()) { @@ -1529,6 +1551,7 @@ void Menu::autoAdjustLOD(float currentFPS) { && _voxelSizeScale > ADJUST_LOD_MIN_SIZE_SCALE) { _voxelSizeScale *= ADJUST_LOD_DOWN_BY; + if (_voxelSizeScale < ADJUST_LOD_MIN_SIZE_SCALE) { _voxelSizeScale = ADJUST_LOD_MIN_SIZE_SCALE; } @@ -1551,6 +1574,7 @@ void Menu::autoAdjustLOD(float currentFPS) { } if (changed) { + _shouldRenderTableNeedsRebuilding = true; if (_lodToolsDialog) { _lodToolsDialog->reloadSliders(); } @@ -1565,14 +1589,56 @@ void Menu::resetLODAdjust() { void Menu::setVoxelSizeScale(float sizeScale) { _voxelSizeScale = sizeScale; + _shouldRenderTableNeedsRebuilding = true; bumpSettings(); } void Menu::setBoundaryLevelAdjust(int boundaryLevelAdjust) { _boundaryLevelAdjust = boundaryLevelAdjust; + _shouldRenderTableNeedsRebuilding = true; bumpSettings(); } +// TODO: This is essentially the same logic used to render voxels, but since models are more detailed then voxels +// I've added a voxelToModelRatio that adjusts how much closer to a model you have to be to see it. +bool Menu::shouldRenderMesh(float largestDimension, float distanceToCamera) { + const float voxelToMeshRatio = 4.0f; // must be this many times closer to a mesh than a voxel to see it. + float voxelSizeScale = getVoxelSizeScale(); + int boundaryLevelAdjust = getBoundaryLevelAdjust(); + float maxScale = (float)TREE_SCALE; + float visibleDistanceAtMaxScale = boundaryDistanceForRenderLevel(boundaryLevelAdjust, voxelSizeScale) / voxelToMeshRatio; + + if (_shouldRenderTableNeedsRebuilding) { + _shouldRenderTable.clear(); + + float SMALLEST_SCALE_IN_TABLE = 0.001f; // 1mm is plenty small + float scale = maxScale; + float visibleDistanceAtScale = visibleDistanceAtMaxScale; + + while (scale > SMALLEST_SCALE_IN_TABLE) { + scale /= 2.0f; + visibleDistanceAtScale /= 2.0f; + _shouldRenderTable[scale] = visibleDistanceAtScale; + } + _shouldRenderTableNeedsRebuilding = false; + } + + float closestScale = maxScale; + float visibleDistanceAtClosestScale = visibleDistanceAtMaxScale; + QMap::const_iterator lowerBound = _shouldRenderTable.lowerBound(largestDimension); + if (lowerBound != _shouldRenderTable.constEnd()) { + closestScale = lowerBound.key(); + visibleDistanceAtClosestScale = lowerBound.value(); + } + + if (closestScale < largestDimension) { + visibleDistanceAtClosestScale *= 2.0f; + } + + return (distanceToCamera <= visibleDistanceAtClosestScale); +} + + void Menu::lodTools() { if (!_lodToolsDialog) { _lodToolsDialog = new LodToolsDialog(Application::getInstance()->getGLWidget()); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 8fcce3dbf3..69613f6f54 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -143,6 +143,8 @@ public: void setBoundaryLevelAdjust(int boundaryLevelAdjust); int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; } + bool shouldRenderMesh(float largestDimension, float distanceToCamera); + #ifdef Q_OS_MAC SpeechRecognizer* getSpeechRecognizer() { return &_speechRecognizer; } #endif @@ -225,6 +227,7 @@ private slots: void displayAddressOfflineMessage(); void displayAddressNotFoundMessage(); void muteEnvironment(); + void changeRenderResolution(QAction* action); private: static Menu* _instance; @@ -309,6 +312,9 @@ private: QString _snapshotsLocation; QString _scriptsLocation; QByteArray _walletPrivateKey; + + bool _shouldRenderTableNeedsRebuilding; + QMap _shouldRenderTable; }; @@ -366,6 +372,9 @@ namespace MenuOption { const QString Collisions = "Collisions"; const QString Console = "Console..."; const QString ControlWithSpeech = "Control With Speech"; + const QString DontCullOutOfViewMeshParts = "Don't Cull Out Of View Mesh Parts"; + const QString DontCullTooSmallMeshParts = "Don't Cull Too Small Mesh Parts"; + const QString DontReduceMaterialSwitches = "Don't Attempt to Reduce Material Switches"; const QString DecreaseAvatarSize = "Decrease Avatar Size"; const QString DecreaseVoxelSize = "Decrease Voxel Size"; const QString DisableActivityLogger = "Disable Activity Logger"; @@ -390,7 +399,6 @@ namespace MenuOption { const QString ExpandOtherAvatarTiming = "Expand /otherAvatar"; const QString ExpandPaintGLTiming = "Expand /paintGL"; const QString ExpandUpdateTiming = "Expand /update"; - const QString Faceplus = "Faceplus"; const QString Faceshift = "Faceshift"; const QString FilterSixense = "Smooth Sixense Movement"; const QString FirstPerson = "First Person"; @@ -398,7 +406,6 @@ namespace MenuOption { const QString FrustumRenderMode = "Render Mode"; const QString Fullscreen = "Fullscreen"; const QString FullscreenMirror = "Fullscreen Mirror"; - const QString GlowMode = "Cycle Glow Mode"; const QString GlowWhenSpeaking = "Glow When Speaking"; const QString NamesAboveHeads = "Names Above Heads"; const QString GoToUser = "Go To User"; @@ -441,6 +448,12 @@ namespace MenuOption { const QString RenderHeadCollisionShapes = "Show Head Collision Shapes"; const QString RenderLookAtVectors = "Show Look-at Vectors"; const QString RenderSkeletonCollisionShapes = "Show Skeleton Collision Shapes"; + const QString RenderResolution = "Scale Resolution"; + const QString RenderResolutionOne = "1"; + const QString RenderResolutionTwoThird = "2/3"; + const QString RenderResolutionHalf = "1/2"; + const QString RenderResolutionThird = "1/3"; + const QString RenderResolutionQuarter = "1/4"; const QString ResetAvatarSize = "Reset Avatar Size"; const QString ResetSensors = "Reset Sensors"; const QString RunningScripts = "Running Scripts"; diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 24a1026ef4..8ed00be93b 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -23,6 +23,7 @@ #include +#include #include #include @@ -454,6 +455,36 @@ float MetavoxelSystem::getHeightfieldHeight(const glm::vec3& location) { return visitor.height; } +void MetavoxelSystem::paintHeightfieldColor(const glm::vec3& position, float radius, const QColor& color) { + MetavoxelEditMessage edit = { QVariant::fromValue(PaintHeightfieldMaterialEdit(position, radius, SharedObjectPointer(), color)) }; + applyEdit(edit, true); +} + +void MetavoxelSystem::paintHeightfieldMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material) { + MetavoxelEditMessage edit = { QVariant::fromValue(PaintHeightfieldMaterialEdit(position, radius, material)) }; + applyMaterialEdit(edit, true); +} + +void MetavoxelSystem::paintVoxelColor(const glm::vec3& position, float radius, const QColor& color) { + MetavoxelEditMessage edit = { QVariant::fromValue(PaintVoxelMaterialEdit(position, radius, SharedObjectPointer(), color)) }; + applyEdit(edit, true); +} + +void MetavoxelSystem::paintVoxelMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material) { + MetavoxelEditMessage edit = { QVariant::fromValue(PaintVoxelMaterialEdit(position, radius, material)) }; + applyMaterialEdit(edit, true); +} + +void MetavoxelSystem::setVoxelColor(const SharedObjectPointer& spanner, const QColor& color) { + MetavoxelEditMessage edit = { QVariant::fromValue(VoxelMaterialSpannerEdit(spanner, SharedObjectPointer(), color)) }; + applyEdit(edit, true); +} + +void MetavoxelSystem::setVoxelMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material) { + MetavoxelEditMessage edit = { QVariant::fromValue(VoxelMaterialSpannerEdit(spanner, material)) }; + applyMaterialEdit(edit, true); +} + class CursorRenderVisitor : public MetavoxelVisitor { public: @@ -563,6 +594,55 @@ void MetavoxelSystem::deleteTextures(int heightID, int colorID, int textureID) { glDeleteTextures(1, (GLuint*)&textureID); } +class MaterialEditApplier : public SignalHandler { +public: + + MaterialEditApplier(const MetavoxelEditMessage& message, const QSharedPointer texture); + + virtual void handle(); + +protected: + + MetavoxelEditMessage _message; + QSharedPointer _texture; +}; + +MaterialEditApplier::MaterialEditApplier(const MetavoxelEditMessage& message, const QSharedPointer texture) : + _message(message), + _texture(texture) { +} + +void MaterialEditApplier::handle() { + static_cast(_message.edit.data())->averageColor = _texture->getAverageColor(); + Application::getInstance()->getMetavoxels()->applyEdit(_message, true); + deleteLater(); +} + +void MetavoxelSystem::applyMaterialEdit(const MetavoxelEditMessage& message, bool reliable) { + const MaterialEdit* edit = static_cast(message.edit.constData()); + MaterialObject* material = static_cast(edit->material.data()); + if (material && material->getDiffuse().isValid()) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "applyMaterialEdit", Q_ARG(const MetavoxelEditMessage&, message), + Q_ARG(bool, reliable)); + return; + } + QSharedPointer texture = Application::getInstance()->getTextureCache()->getTexture( + material->getDiffuse(), SPLAT_TEXTURE); + if (texture->isLoaded()) { + MetavoxelEditMessage newMessage = message; + static_cast(newMessage.edit.data())->averageColor = texture->getAverageColor(); + applyEdit(newMessage, true); + + } else { + MaterialEditApplier* applier = new MaterialEditApplier(message, texture); + connect(texture.data(), &Resource::loaded, applier, &SignalHandler::handle); + } + } else { + applyEdit(message, true); + } +} + MetavoxelClient* MetavoxelSystem::createClient(const SharedNodePointer& node) { return new MetavoxelSystemClient(node, _updater); } diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index f479ba5e50..ac6a75c68b 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -54,6 +54,18 @@ public: Q_INVOKABLE float getHeightfieldHeight(const glm::vec3& location); + Q_INVOKABLE void paintHeightfieldColor(const glm::vec3& position, float radius, const QColor& color); + + Q_INVOKABLE void paintHeightfieldMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material); + + Q_INVOKABLE void paintVoxelColor(const glm::vec3& position, float radius, const QColor& color); + + Q_INVOKABLE void paintVoxelMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material); + + Q_INVOKABLE void setVoxelColor(const SharedObjectPointer& spanner, const QColor& color); + + Q_INVOKABLE void setVoxelMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material); + Q_INVOKABLE void deleteTextures(int heightID, int colorID, int textureID); signals: @@ -66,6 +78,8 @@ public slots: protected: + Q_INVOKABLE void applyMaterialEdit(const MetavoxelEditMessage& message, bool reliable = false); + virtual MetavoxelClient* createClient(const SharedNodePointer& node); private: @@ -81,6 +95,15 @@ private: Frustum _frustum; }; +/// Generic abstract base class for objects that handle a signal. +class SignalHandler : public QObject { + Q_OBJECT + +public slots: + + virtual void handle() = 0; +}; + /// Describes contents of a point in a point buffer. class BufferPoint { public: diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index 9d7f5518d0..b23b4fecb3 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -37,92 +37,7 @@ using namespace std; #define WORKAROUND_BROKEN_GLUT_STROKES // see http://www.opengl.org/resources/libraries/glut/spec3/node78.html -void eulerToOrthonormals(glm::vec3 * angles, glm::vec3 * front, glm::vec3 * right, glm::vec3 * up) { - // - // Converts from three euler angles to the associated orthonormal vectors - // - // Angles contains (pitch, yaw, roll) in radians - // - // First, create the quaternion associated with these euler angles - glm::quat q(glm::vec3(angles->x, -(angles->y), angles->z)); - - // Next, create a rotation matrix from that quaternion - glm::mat4 rotation; - rotation = glm::mat4_cast(q); - - // Transform the original vectors by the rotation matrix to get the new vectors - glm::vec4 qup(0,1,0,0); - glm::vec4 qright(-1,0,0,0); - glm::vec4 qfront(0,0,1,0); - glm::vec4 upNew = qup*rotation; - glm::vec4 rightNew = qright*rotation; - glm::vec4 frontNew = qfront*rotation; - - // Copy the answers to output vectors - up->x = upNew.x; up->y = upNew.y; up->z = upNew.z; - right->x = rightNew.x; right->y = rightNew.y; right->z = rightNew.z; - front->x = frontNew.x; front->y = frontNew.y; front->z = frontNew.z; -} - -void printVector(glm::vec3 vec) { - qDebug("%4.2f, %4.2f, %4.2f", vec.x, vec.y, vec.z); -} - - -// Return the azimuth angle (in radians) between two points. -float azimuth_to(glm::vec3 head_pos, glm::vec3 source_pos) { - return atan2(head_pos.x - source_pos.x, head_pos.z - source_pos.z); -} - -// Return the angle (in radians) between the head and an object in the scene. -// The value is zero if you are looking right at it. -// The angle is negative if the object is to your right. -float angle_to(glm::vec3 head_pos, glm::vec3 source_pos, float render_yaw, float head_yaw) { - return atan2(head_pos.x - source_pos.x, head_pos.z - source_pos.z) + render_yaw + head_yaw; -} - -// Draw a 3D vector floating in space -void drawVector(glm::vec3 * vector) { - glDisable(GL_LIGHTING); - glEnable(GL_POINT_SMOOTH); - glPointSize(3.0); - glLineWidth(2.0); - - // Draw axes - glBegin(GL_LINES); - glColor3f(1,0,0); - glVertex3f(0,0,0); - glVertex3f(1,0,0); - glColor3f(0,1,0); - glVertex3f(0,0,0); - glVertex3f(0, 1, 0); - glColor3f(0,0,1); - glVertex3f(0,0,0); - glVertex3f(0, 0, 1); - glEnd(); - - // Draw the vector itself - glBegin(GL_LINES); - glColor3f(1,1,1); - glVertex3f(0,0,0); - glVertex3f(vector->x, vector->y, vector->z); - glEnd(); - - // Draw spheres for magnitude - glPushMatrix(); - glColor3f(1,0,0); - glTranslatef(vector->x, 0, 0); - Application::getInstance()->getGeometryCache()->renderSphere(0.02f, 10, 10); - glColor3f(0,1,0); - glTranslatef(-vector->x, vector->y, 0); - Application::getInstance()->getGeometryCache()->renderSphere(0.02f, 10, 10); - glColor3f(0,0,1); - glTranslatef(0, -vector->y, vector->z); - Application::getInstance()->getGeometryCache()->renderSphere(0.02f, 10, 10); - glPopMatrix(); - -} void renderWorldBox() { // Show edge of world @@ -201,10 +116,6 @@ int widthText(float scale, int mono, char const* string) { return textRenderer(mono)->computeWidth(string) * (scale / 0.10); } -float widthChar(float scale, int mono, char ch) { - return textRenderer(mono)->computeWidth(ch) * (scale / 0.10); -} - void drawText(int x, int y, float scale, float radians, int mono, char const* string, const float* color) { // @@ -219,29 +130,6 @@ void drawText(int x, int y, float scale, float radians, int mono, glPopMatrix(); } - -void drawvec3(int x, int y, float scale, float radians, float thick, int mono, glm::vec3 vec, float r, float g, float b) { - // - // Draws vec3 on screen as stroked so it can be resized - // - char vectext[20]; - sprintf(vectext,"%3.1f,%3.1f,%3.1f", vec.x, vec.y, vec.z); - int len, i; - glPushMatrix(); - glTranslatef(static_cast(x), static_cast(y), 0); - glColor3f(r,g,b); - glRotated(180.0 + double(radians * DEGREES_PER_RADIAN), 0.0, 0.0, 1.0); - glRotated(180.0, 0.0, 1.0, 0.0); - glLineWidth(thick); - glScalef(scale, scale, 1.f); - len = (int) strlen(vectext); - for (i = 0; i < len; i++) { - if (!mono) glutStrokeCharacter(GLUT_STROKE_ROMAN, int(vectext[i])); - else glutStrokeCharacter(GLUT_STROKE_MONO_ROMAN, int(vectext[i])); - } - glPopMatrix(); -} - void renderCollisionOverlay(int width, int height, float magnitude, float red, float blue, float green) { const float MIN_VISIBLE_COLLISION = 0.01f; if (magnitude > MIN_VISIBLE_COLLISION) { @@ -255,27 +143,6 @@ void renderCollisionOverlay(int width, int height, float magnitude, float red, f } } -void renderSphereOutline(glm::vec3 position, float radius, int numSides, glm::vec3 cameraPosition) { - glm::vec3 vectorToPosition(glm::normalize(position - cameraPosition)); - glm::vec3 right = glm::cross(vectorToPosition, glm::vec3(0.0f, 1.0f, 0.0f)); - glm::vec3 up = glm::cross(right, vectorToPosition); - - glBegin(GL_LINE_STRIP); - for (int i=0; i MAX_POINTS_CORNER) { - numPointsCorner = MAX_POINTS_CORNER; - } - - // Precompute sin and cos for [0, PI/2) for the number of points (numPointCorner) - double radiusTimesSin[MAX_POINTS_CORNER]; - double radiusTimesCos[MAX_POINTS_CORNER]; - int i = 0; - for (int i = 0; i < numPointsCorner; i++) { - double t = (double)i * (double)PI_OVER_TWO / (double)(numPointsCorner - 1); - radiusTimesSin[i] = radius * sin(t); - radiusTimesCos[i] = radius * cos(t); - } - - glm::dvec2 cornerCenter; - glBegin(GL_POINTS); - - // Top left corner - cornerCenter = glm::vec2(x + radius, y + height - radius); - for (i = 0; i < numPointsCorner; i++) { - glVertex2d(cornerCenter.x - radiusTimesCos[i], cornerCenter.y + radiusTimesSin[i]); - } - - // Top rigth corner - cornerCenter = glm::vec2(x + width - radius, y + height - radius); - for (i = 0; i < numPointsCorner; i++) { - glVertex2d(cornerCenter.x + radiusTimesSin[i], cornerCenter.y + radiusTimesCos[i]); - } - - // Bottom right - cornerCenter = glm::vec2(x + width - radius, y + radius); - for (i = 0; i < numPointsCorner; i++) { - glVertex2d(cornerCenter.x + radiusTimesCos[i], cornerCenter.y - radiusTimesSin[i]); - } - - // Bottom left - cornerCenter = glm::vec2(x + radius, y + radius); - for (i = 0; i < numPointsCorner; i++) { - glVertex2d(cornerCenter.x - radiusTimesSin[i], cornerCenter.y - radiusTimesCos[i]); - } - glEnd(); -} void renderOrientationDirections(glm::vec3 position, const glm::quat& orientation, float size) { @@ -395,12 +214,6 @@ void renderOrientationDirections(glm::vec3 position, const glm::quat& orientatio glEnd(); } -bool closeEnoughForGovernmentWork(float a, float b) { - float distance = std::abs(a-b); - //qDebug("closeEnoughForGovernmentWork() a=%1.10f b=%1.10f distance=%1.10f\n",a,b,distance); - return (distance < 0.00001f); -} - // Do some basic timing tests and report the results void runTimingTests() { // How long does it take to make a call to get the time? diff --git a/interface/src/Util.h b/interface/src/Util.h index 02cfd99f9a..b2c5ee3b2f 100644 --- a/interface/src/Util.h +++ b/interface/src/Util.h @@ -16,35 +16,20 @@ #include #include -void eulerToOrthonormals(glm::vec3 * angles, glm::vec3 * fwd, glm::vec3 * left, glm::vec3 * up); - -float azimuth_to(glm::vec3 head_pos, glm::vec3 source_pos); -float angle_to(glm::vec3 head_pos, glm::vec3 source_pos, float render_yaw, float head_yaw); - float randFloat(); const glm::vec3 randVector(); void renderWorldBox(); int widthText(float scale, int mono, char const* string); -float widthChar(float scale, int mono, char ch); void drawText(int x, int y, float scale, float radians, int mono, char const* string, const float* color); -void drawvec3(int x, int y, float scale, float radians, float thick, int mono, glm::vec3 vec, - float r=1.0, float g=1.0, float b=1.0); - -void drawVector(glm::vec3* vector); - -void printVector(glm::vec3 vec); - void renderCollisionOverlay(int width, int height, float magnitude, float red = 0, float blue = 0, float green = 0); void renderOrientationDirections( glm::vec3 position, const glm::quat& orientation, float size ); -void renderSphereOutline(glm::vec3 position, float radius, int numSides, glm::vec3 cameraPosition); -void renderCircle(glm::vec3 position, float radius, glm::vec3 surfaceNormal, int numSides ); -void renderRoundedCornersRect(int x, int y, int width, int height, int radius, int numPointsCorner); + void renderBevelCornersRect(int x, int y, int width, int height, int bevelDistance); void runTimingTests(); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 0477f6e04e..43a7949490 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -192,6 +192,7 @@ void Avatar::simulate(float deltaTime) { _hair.setAngularVelocity((getAngularVelocity() + getHead()->getAngularVelocity()) * getHead()->getFinalOrientationInWorldFrame()); _hair.setAngularAcceleration(getAngularAcceleration() * getHead()->getFinalOrientationInWorldFrame()); _hair.setGravity(Application::getInstance()->getEnvironment()->getGravity(getPosition()) * getHead()->getFinalOrientationInWorldFrame()); + _hair.setLoudness((float) getHeadData()->getAudioLoudness()); _hair.simulate(deltaTime); } } @@ -694,7 +695,7 @@ void Avatar::renderDisplayName() { if (success) { double textWindowHeight = abs(result1[1] - result0[1]); - float scaleFactor = QApplication::desktop()->windowHandle()->devicePixelRatio() * + float scaleFactor = Application::getInstance()->getRenderResolutionScale() * ((textWindowHeight > EPSILON) ? 1.0f / textWindowHeight : 1.0f); glScalef(scaleFactor, scaleFactor, 1.0); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 36035880fd..b674254466 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -218,6 +218,7 @@ void MyAvatar::simulate(float deltaTime) { _hair.setAngularVelocity((getAngularVelocity() + getHead()->getAngularVelocity()) * getHead()->getFinalOrientationInWorldFrame()); _hair.setAngularAcceleration(getAngularAcceleration() * getHead()->getFinalOrientationInWorldFrame()); _hair.setGravity(Application::getInstance()->getEnvironment()->getGravity(getPosition()) * getHead()->getFinalOrientationInWorldFrame()); + _hair.setLoudness((float)getHeadData()->getAudioLoudness()); _hair.simulate(deltaTime); } } diff --git a/interface/src/devices/Faceplus.cpp b/interface/src/devices/Faceplus.cpp deleted file mode 100644 index 00bc9d2676..0000000000 --- a/interface/src/devices/Faceplus.cpp +++ /dev/null @@ -1,254 +0,0 @@ -// -// Faceplus.cpp -// interface/src/devices -// -// Created by Andrzej Kapolka on 4/9/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include - -#ifdef HAVE_FACEPLUS -#include -#endif - -#include - -#include "Application.h" -#include "Faceplus.h" - -static int floatVectorMetaTypeId = qRegisterMetaType >(); - -Faceplus::Faceplus() : - _enabled(false), - _active(false) { - -#ifdef HAVE_FACEPLUS - // these are ignored--any values will do - faceplus_log_in("username", "password"); -#endif -} - -Faceplus::~Faceplus() { - setEnabled(false); -} - -void Faceplus::init() { - connect(Application::getInstance()->getFaceshift(), SIGNAL(connectionStateChanged()), SLOT(updateEnabled())); - updateEnabled(); -} - -void Faceplus::reset() { - if (_enabled) { - QMetaObject::invokeMethod(_reader, "reset"); - } -} - -void Faceplus::setState(const glm::vec3& headTranslation, const glm::quat& headRotation, - float estimatedEyePitch, float estimatedEyeYaw, const QVector& blendshapeCoefficients) { - _headTranslation = headTranslation; - _headRotation = headRotation; - _estimatedEyePitch = estimatedEyePitch; - _estimatedEyeYaw = estimatedEyeYaw; - _blendshapeCoefficients = blendshapeCoefficients; - _active = true; -} - -void Faceplus::updateEnabled() { - setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Faceplus) && - !(Menu::getInstance()->isOptionChecked(MenuOption::Faceshift) && - Application::getInstance()->getFaceshift()->isConnectedOrConnecting())); -} - -void Faceplus::setEnabled(bool enabled) { - if (_enabled == enabled) { - return; - } - if ((_enabled = enabled)) { - _reader = new FaceplusReader(); - QThread* readerThread = new QThread(this); - _reader->moveToThread(readerThread); - readerThread->start(); - QMetaObject::invokeMethod(_reader, "init"); - - } else { - QThread* readerThread = _reader->thread(); - QMetaObject::invokeMethod(_reader, "shutdown"); - readerThread->wait(); - delete readerThread; - _active = false; - } -} - -#ifdef HAVE_FACEPLUS -static QMultiHash > createChannelNameMap() { - QMultiHash > blendshapeMap; - blendshapeMap.insert("EyeBlink_L", QPair("Mix::Blink_Left", 1.0f)); - blendshapeMap.insert("EyeBlink_R", QPair("Mix::Blink_Right", 1.0f)); - blendshapeMap.insert("BrowsD_L", QPair("Mix::BrowsDown_Left", 1.0f)); - blendshapeMap.insert("BrowsD_R", QPair("Mix::BrowsDown_Right", 1.0f)); - blendshapeMap.insert("...", QPair("Mix::BrowsIn_Left", 1.0f)); - blendshapeMap.insert("...", QPair("Mix::BrowsIn_Right", 1.0f)); - blendshapeMap.insert("...", QPair("Mix::BrowsOuterLower_Left", 1.0f)); - blendshapeMap.insert("...", QPair("Mix::BrowsOuterLower_Right", 1.0f)); - blendshapeMap.insert("BrowsU_L", QPair("Mix::BrowsUp_Left", 10.0f)); - blendshapeMap.insert("BrowsU_R", QPair("Mix::BrowsUp_Right", 10.0f)); - blendshapeMap.insert("EyeOpen_L", QPair("Mix::EyesWide_Left", 1.0f)); - blendshapeMap.insert("EyeOpen_R", QPair("Mix::EyesWide_Right", 1.0f)); - blendshapeMap.insert("MouthFrown_L", QPair("Mix::Frown_Left", 1.0f)); - blendshapeMap.insert("MouthFrown_R", QPair("Mix::Frown_Right", 1.0f)); - blendshapeMap.insert("JawLeft", QPair("Mix::Jaw_RotateY_Left", 1.0f)); - blendshapeMap.insert("JawRight", QPair("Mix::Jaw_RotateY_Right", 1.0f)); - blendshapeMap.insert("LipsLowerDown", QPair("Mix::LowerLipDown_Left", 0.5f)); - blendshapeMap.insert("LipsLowerDown", QPair("Mix::LowerLipDown_Right", 0.5f)); - blendshapeMap.insert("...", QPair("Mix::LowerLipIn", 1.0f)); - blendshapeMap.insert("...", QPair("Mix::LowerLipOut", 1.0f)); - blendshapeMap.insert("MouthLeft", QPair("Mix::Midmouth_Left", 1.0f)); - blendshapeMap.insert("MouthRight", QPair("Mix::Midmouth_Right", 1.0f)); - blendshapeMap.insert("...", QPair("Mix::MouthDown", 1.0f)); - blendshapeMap.insert("...", QPair("Mix::MouthNarrow_Left", 1.0f)); - blendshapeMap.insert("...", QPair("Mix::MouthNarrow_Right", 1.0f)); - blendshapeMap.insert("JawOpen", QPair("Mix::MouthOpen", 1.0f)); - blendshapeMap.insert("...", QPair("Mix::MouthUp", 1.0f)); - blendshapeMap.insert("LipsPucker", QPair("Mix::MouthWhistle_NarrowAdjust_Left", 0.5f)); - blendshapeMap.insert("LipsPucker", QPair("Mix::MouthWhistle_NarrowAdjust_Right", 0.5f)); - blendshapeMap.insert("Sneer", QPair("Mix::NoseScrunch_Left", 0.5f)); - blendshapeMap.insert("Sneer", QPair("Mix::NoseScrunch_Right", 0.5f)); - blendshapeMap.insert("MouthSmile_L", QPair("Mix::Smile_Left", 1.0f)); - blendshapeMap.insert("MouthSmile_R", QPair("Mix::Smile_Right", 1.0f)); - blendshapeMap.insert("EyeSquint_L", QPair("Mix::Squint_Left", 1.0f)); - blendshapeMap.insert("EyeSquint_R", QPair("Mix::Squint_Right", 1.0f)); - blendshapeMap.insert("...", QPair("Mix::UpperLipIn", 1.0f)); - blendshapeMap.insert("...", QPair("Mix::UpperLipOut", 1.0f)); - blendshapeMap.insert("LipsUpperUp", QPair("Mix::UpperLipUp_Left", 0.5f)); - blendshapeMap.insert("LipsUpperUp", QPair("Mix::UpperLipUp_Right", 0.5f)); - - QMultiHash > channelNameMap; - for (int i = 0;; i++) { - QByteArray blendshape = FACESHIFT_BLENDSHAPES[i]; - if (blendshape.isEmpty()) { - break; - } - for (QMultiHash >::const_iterator it = blendshapeMap.constFind(blendshape); - it != blendshapeMap.constEnd() && it.key() == blendshape; it++) { - channelNameMap.insert(it.value().first, QPair(i, it.value().second)); - } - } - - return channelNameMap; -} - -static const QMultiHash >& getChannelNameMap() { - static QMultiHash > channelNameMap = createChannelNameMap(); - return channelNameMap; -} -#endif - -FaceplusReader::~FaceplusReader() { -#ifdef HAVE_FACEPLUS - if (faceplus_teardown()) { - qDebug() << "Faceplus torn down."; - } -#endif -} - -void FaceplusReader::init() { -#ifdef HAVE_FACEPLUS - if (!faceplus_init("hHD")) { - qDebug() << "Failed to initialized Faceplus."; - return; - } - qDebug() << "Faceplus initialized."; - - int channelCount = faceplus_output_channels_count(); - _outputVector.resize(channelCount); - - int maxIndex = -1; - _channelIndexMap.clear(); - for (int i = 0; i < channelCount; i++) { - QByteArray name = faceplus_output_channel_name(i); - if (name == "Head_Joint::Rotation_X") { - _headRotationIndices[0] = i; - - } else if (name == "Head_Joint::Rotation_Y") { - _headRotationIndices[1] = i; - - } else if (name == "Head_Joint::Rotation_Z") { - _headRotationIndices[2] = i; - - } else if (name == "Left_Eye_Joint::Rotation_X") { - _leftEyeRotationIndices[0] = i; - - } else if (name == "Left_Eye_Joint::Rotation_Y") { - _leftEyeRotationIndices[1] = i; - - } else if (name == "Right_Eye_Joint::Rotation_X") { - _rightEyeRotationIndices[0] = i; - - } else if (name == "Right_Eye_Joint::Rotation_Y") { - _rightEyeRotationIndices[1] = i; - } - for (QMultiHash >::const_iterator it = getChannelNameMap().constFind(name); - it != getChannelNameMap().constEnd() && it.key() == name; it++) { - _channelIndexMap.insert(i, it.value()); - maxIndex = qMax(maxIndex, it.value().first); - } - } - _blendshapeCoefficients.resize(maxIndex + 1); - _referenceInitialized = false; - - QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection); -#endif -} - -void FaceplusReader::shutdown() { - deleteLater(); - thread()->quit(); -} - -void FaceplusReader::update() { -#ifdef HAVE_FACEPLUS - float x, y, rotation, scale; - if (!(faceplus_synchronous_track() && faceplus_current_face_location(&x, &y, &rotation, &scale) && !glm::isnan(x) && - faceplus_current_output_vector(_outputVector.data()))) { - QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection); - return; - } - if (!_referenceInitialized) { - _referenceX = x; - _referenceY = y; - _referenceInitialized = true; - } - const float TRANSLATION_SCALE = 10.0f; - glm::vec3 headTranslation((x - _referenceX) * TRANSLATION_SCALE, (y - _referenceY) * TRANSLATION_SCALE, 0.0f); - glm::quat headRotation(glm::radians(glm::vec3(-_outputVector.at(_headRotationIndices[0]), - _outputVector.at(_headRotationIndices[1]), -_outputVector.at(_headRotationIndices[2])))); - float estimatedEyePitch = (_outputVector.at(_leftEyeRotationIndices[0]) + - _outputVector.at(_rightEyeRotationIndices[0])) * -0.5f; - float estimatedEyeYaw = (_outputVector.at(_leftEyeRotationIndices[1]) + - _outputVector.at(_rightEyeRotationIndices[1])) * 0.5f; - - qFill(_blendshapeCoefficients.begin(), _blendshapeCoefficients.end(), 0.0f); - for (int i = 0; i < _outputVector.size(); i++) { - for (QMultiHash >::const_iterator it = _channelIndexMap.constFind(i); - it != _channelIndexMap.constEnd() && it.key() == i; it++) { - _blendshapeCoefficients[it.value().first] += _outputVector.at(i) * it.value().second; - } - } - - QMetaObject::invokeMethod(Application::getInstance()->getFaceplus(), "setState", Q_ARG(const glm::vec3&, headTranslation), - Q_ARG(const glm::quat&, headRotation), Q_ARG(float, estimatedEyePitch), Q_ARG(float, estimatedEyeYaw), - Q_ARG(const QVector&, _blendshapeCoefficients)); - - QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection); -#endif -} - -void FaceplusReader::reset() { -#ifdef HAVE_FACEPLUS - _referenceInitialized = false; -#endif -} diff --git a/interface/src/devices/Faceplus.h b/interface/src/devices/Faceplus.h deleted file mode 100644 index d52740ca5f..0000000000 --- a/interface/src/devices/Faceplus.h +++ /dev/null @@ -1,84 +0,0 @@ -// -// Faceplus.h -// interface/src/devices -// -// Created by Andrzej Kapolka on 4/9/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_Faceplus_h -#define hifi_Faceplus_h - -#include -#include -#include - -#include "FaceTracker.h" - -class FaceplusReader; - -/// Interface for Mixamo FacePlus. -class Faceplus : public FaceTracker { - Q_OBJECT - -public: - - Faceplus(); - virtual ~Faceplus(); - - void init(); - void reset(); - - bool isActive() const { return _active; } - - Q_INVOKABLE void setState(const glm::vec3& headTranslation, const glm::quat& headRotation, - float estimatedEyePitch, float estimatedEyeYaw, const QVector& blendshapeCoefficients); - -public slots: - - void updateEnabled(); - -private: - - void setEnabled(bool enabled); - - bool _enabled; - bool _active; - - FaceplusReader* _reader; -}; - -Q_DECLARE_METATYPE(QVector) - -/// The reader object that lives in its own thread. -class FaceplusReader : public QObject { - Q_OBJECT - -public: - - virtual ~FaceplusReader(); - - Q_INVOKABLE void init(); - Q_INVOKABLE void shutdown(); - Q_INVOKABLE void update(); - Q_INVOKABLE void reset(); - -private: - -#ifdef HAVE_FACEPLUS - QMultiHash > _channelIndexMap; - QVector _outputVector; - int _headRotationIndices[3]; - int _leftEyeRotationIndices[2]; - int _rightEyeRotationIndices[2]; - float _referenceX; - float _referenceY; - bool _referenceInitialized; - QVector _blendshapeCoefficients; -#endif -}; - -#endif // hifi_Faceplus_h diff --git a/interface/src/devices/Joystick.cpp b/interface/src/devices/Joystick.cpp index 5ca2925ba8..25b8bc142d 100644 --- a/interface/src/devices/Joystick.cpp +++ b/interface/src/devices/Joystick.cpp @@ -15,9 +15,9 @@ #include "Joystick.h" -const float MAX_AXIS = 32768.0f; #ifdef HAVE_SDL2 +const float MAX_AXIS = 32768.0f; Joystick::Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController) : _sdlGameController(sdlGameController), diff --git a/interface/src/devices/Visage.cpp b/interface/src/devices/Visage.cpp index 5de2746b07..9c7416c219 100644 --- a/interface/src/devices/Visage.cpp +++ b/interface/src/devices/Visage.cpp @@ -170,7 +170,6 @@ void Visage::reset() { void Visage::updateEnabled() { setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Visage) && - !Menu::getInstance()->isOptionChecked(MenuOption::Faceplus) && !(Menu::getInstance()->isOptionChecked(MenuOption::Faceshift) && Application::getInstance()->getFaceshift()->isConnectedOrConnecting())); } diff --git a/interface/src/entities/EntityTreeRenderer.cpp b/interface/src/entities/EntityTreeRenderer.cpp index e744d74da3..2c7c970376 100644 --- a/interface/src/entities/EntityTreeRenderer.cpp +++ b/interface/src/entities/EntityTreeRenderer.cpp @@ -162,36 +162,6 @@ void renderElementProxy(EntityTreeElement* entityTreeElement) { } } -float EntityTreeRenderer::distanceToCamera(const glm::vec3& center, const ViewFrustum& viewFrustum) const { - glm::vec3 temp = viewFrustum.getPosition() - center; - float distanceToVoxelCenter = sqrtf(glm::dot(temp, temp)); - return distanceToVoxelCenter; -} - -// TODO: This could be optimized to be a table, or something that doesn't require recalculation on every -// render call for every entity -// TODO: This is essentially the same logic used to render voxels, but since models are more detailed then voxels -// I've added a voxelToModelRatio that adjusts how much closer to a model you have to be to see it. -bool EntityTreeRenderer::shouldRenderEntity(float largestDimension, float distanceToCamera) const { - const float voxelToModelRatio = 4.0f; // must be this many times closer to a model than a voxel to see it. - float voxelSizeScale = Menu::getInstance()->getVoxelSizeScale(); - int boundaryLevelAdjust = Menu::getInstance()->getBoundaryLevelAdjust(); - - float scale = (float)TREE_SCALE; - float visibleDistanceAtScale = boundaryDistanceForRenderLevel(boundaryLevelAdjust, voxelSizeScale) / voxelToModelRatio; - - while (scale > largestDimension) { - scale /= 2.0f; - visibleDistanceAtScale /= 2.0f; - } - - if (scale < largestDimension) { - visibleDistanceAtScale *= 2.0f; - } - - return (distanceToCamera <= visibleDistanceAtScale); -} - void EntityTreeRenderer::renderProxies(const EntityItem* entity, RenderArgs* args) { bool isShadowMode = args->_renderMode == OctreeRenderer::SHADOW_RENDER_MODE; bool displayModelBounds = Menu::getInstance()->isOptionChecked(MenuOption::DisplayModelBounds); @@ -287,7 +257,7 @@ void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) // TODO: some entity types (like lights) might want to be rendered even // when they are outside of the view frustum... - float distance = distanceToCamera(entityBox.calcCenter(), *args->_viewFrustum); + float distance = args->_viewFrustum->distanceToCamera(entityBox.calcCenter()); if (wantDebug) { qDebug() << "------- renderElement() ----------"; @@ -299,23 +269,28 @@ void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) qDebug() << " entityBox:" << entityItem->getAABox(); qDebug() << " dimensions:" << entityItem->getDimensionsInMeters() << "in meters"; qDebug() << " largestDimension:" << entityBox.getLargestDimension() << "in meters"; - qDebug() << " shouldRender:" << shouldRenderEntity(entityBox.getLargestDimension(), distance); + qDebug() << " shouldRender:" << Menu::getInstance()->shouldRenderMesh(entityBox.getLargestDimension(), distance); qDebug() << " in frustum:" << (args->_viewFrustum->boxInFrustum(entityBox) != ViewFrustum::OUTSIDE); } - - if (shouldRenderEntity(entityBox.getLargestDimension(), distance) && - args->_viewFrustum->boxInFrustum(entityBox) != ViewFrustum::OUTSIDE) { - - - renderProxies(entityItem, args); - Glower* glower = NULL; - if (entityItem->getGlowLevel() > 0.0f) { - glower = new Glower(entityItem->getGlowLevel()); - } - entityItem->render(args); - if (glower) { - delete glower; + bool outOfView = args->_viewFrustum->boxInFrustum(entityBox) == ViewFrustum::OUTSIDE; + if (!outOfView) { + bool bigEnoughToRender = Menu::getInstance()->shouldRenderMesh(entityBox.getLargestDimension(), distance); + + if (bigEnoughToRender) { + renderProxies(entityItem, args); + + Glower* glower = NULL; + if (entityItem->getGlowLevel() > 0.0f) { + glower = new Glower(entityItem->getGlowLevel()); + } + entityItem->render(args); + args->_itemsRendered++; + if (glower) { + delete glower; + } + } else { + args->_itemsTooSmall++; } } else { args->_itemsOutOfView++; diff --git a/interface/src/entities/EntityTreeRenderer.h b/interface/src/entities/EntityTreeRenderer.h index 9e0368b61b..7ce984a97d 100644 --- a/interface/src/entities/EntityTreeRenderer.h +++ b/interface/src/entities/EntityTreeRenderer.h @@ -76,11 +76,7 @@ public: void deleteReleasedModels(); private: QList _releasedModels; - - float distanceToCamera(const glm::vec3& center, const ViewFrustum& viewFrustum) const; - bool shouldRenderEntity(float largestDimension, float distanceToCamera) const; void renderProxies(const EntityItem* entity, RenderArgs* args); - }; #endif // hifi_EntityTreeRenderer_h diff --git a/interface/src/entities/RenderableModelEntityItem.cpp b/interface/src/entities/RenderableModelEntityItem.cpp index e2a0933fab..ec4c5b4008 100644 --- a/interface/src/entities/RenderableModelEntityItem.cpp +++ b/interface/src/entities/RenderableModelEntityItem.cpp @@ -59,7 +59,7 @@ int RenderableModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned c void RenderableModelEntityItem::render(RenderArgs* args) { - PerformanceTimer perfTimer("RenderableModelEntityItem::render"); + PerformanceTimer perfTimer("RMEIrender"); assert(getType() == EntityTypes::Model); bool drawAsModel = hasModel(); @@ -119,7 +119,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) { // TODO: this is the majority of model render time. And rendering of a cube model vs the basic Box render // is significantly more expensive. Is there a way to call this that doesn't cost us as much? PerformanceTimer perfTimer("model->render"); - _model->render(alpha, modelRenderMode); + _model->render(alpha, modelRenderMode, args); } else { // if we couldn't get a model, then just draw a cube glColor3ub(getColor()[RED_INDEX],getColor()[GREEN_INDEX],getColor()[BLUE_INDEX]); diff --git a/interface/src/renderer/GlowEffect.cpp b/interface/src/renderer/GlowEffect.cpp index 8341bb960c..9e9c1f2ed5 100644 --- a/interface/src/renderer/GlowEffect.cpp +++ b/interface/src/renderer/GlowEffect.cpp @@ -23,7 +23,6 @@ GlowEffect::GlowEffect() : _initialized(false), - _renderMode(DIFFUSE_ADD_MODE), _isOddFrame(false), _isFirstFrame(true), _intensity(0.0f) { @@ -41,9 +40,9 @@ GlowEffect::~GlowEffect() { } QOpenGLFramebufferObject* GlowEffect::getFreeFramebufferObject() const { - return (_renderMode == DIFFUSE_ADD_MODE && !_isOddFrame) ? - Application::getInstance()->getTextureCache()->getTertiaryFramebufferObject() : - Application::getInstance()->getTextureCache()->getSecondaryFramebufferObject(); + return (_isOddFrame ? + Application::getInstance()->getTextureCache()->getSecondaryFramebufferObject(): + Application::getInstance()->getTextureCache()->getTertiaryFramebufferObject()); } static ProgramObject* createProgram(const QString& name) { @@ -137,31 +136,28 @@ QOpenGLFramebufferObject* GlowEffect::render(bool toTexture) { glDisable(GL_BLEND); glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); - + QOpenGLFramebufferObject* destFBO = toTexture ? Application::getInstance()->getTextureCache()->getSecondaryFramebufferObject() : NULL; - if (!Menu::getInstance()->isOptionChecked(MenuOption::EnableGlowEffect) || (_isEmpty && _renderMode != DIFFUSE_ADD_MODE)) { + if (!Menu::getInstance()->isOptionChecked(MenuOption::EnableGlowEffect) || _isEmpty) { // copy the primary to the screen - if (QOpenGLFramebufferObject::hasOpenGLFramebufferBlit()) { + if (destFBO && QOpenGLFramebufferObject::hasOpenGLFramebufferBlit()) { QOpenGLFramebufferObject::blitFramebuffer(destFBO, primaryFBO); } else { maybeBind(destFBO); + if (!destFBO) { + glViewport(0, 0, Application::getInstance()->getGLWidget()->getDeviceWidth(), + Application::getInstance()->getGLWidget()->getDeviceHeight()); + } glEnable(GL_TEXTURE_2D); glDisable(GL_LIGHTING); glColor3f(1.0f, 1.0f, 1.0f); - renderFullscreenQuad(); + renderFullscreenQuad(); glDisable(GL_TEXTURE_2D); glEnable(GL_LIGHTING); maybeRelease(destFBO); } - } else if (_renderMode == ADD_MODE) { - maybeBind(destFBO); - _addProgram->bind(); - renderFullscreenQuad(); - _addProgram->release(); - maybeRelease(destFBO); - - } else if (_renderMode == DIFFUSE_ADD_MODE) { + } else { // diffuse into the secondary/tertiary (alternating between frames) QOpenGLFramebufferObject* oldDiffusedFBO = Application::getInstance()->getTextureCache()->getSecondaryFramebufferObject(); @@ -197,6 +193,11 @@ QOpenGLFramebufferObject* GlowEffect::render(bool toTexture) { destFBO = oldDiffusedFBO; } maybeBind(destFBO); + if (!destFBO) { + glViewport(0, 0, + Application::getInstance()->getGLWidget()->getDeviceWidth(), + Application::getInstance()->getGLWidget()->getDeviceHeight()); + } _addSeparateProgram->bind(); renderFullscreenQuad(); _addSeparateProgram->release(); @@ -205,70 +206,6 @@ QOpenGLFramebufferObject* GlowEffect::render(bool toTexture) { glBindTexture(GL_TEXTURE_2D, 0); glActiveTexture(GL_TEXTURE0); - } else { // _renderMode == BLUR_ADD_MODE || _renderMode == BLUR_PERSIST_ADD_MODE - // render the primary to the secondary with the horizontal blur - QOpenGLFramebufferObject* secondaryFBO = - Application::getInstance()->getTextureCache()->getSecondaryFramebufferObject(); - secondaryFBO->bind(); - - _horizontalBlurProgram->bind(); - renderFullscreenQuad(); - _horizontalBlurProgram->release(); - - secondaryFBO->release(); - - if (_renderMode == BLUR_ADD_MODE) { - // render the secondary to the screen with the vertical blur - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, secondaryFBO->texture()); - - if (toTexture) { - destFBO = Application::getInstance()->getTextureCache()->getTertiaryFramebufferObject(); - } - maybeBind(destFBO); - _verticalBlurAddProgram->bind(); - renderFullscreenQuad(); - _verticalBlurAddProgram->release(); - maybeRelease(destFBO); - - } else { // _renderMode == BLUR_PERSIST_ADD_MODE - // render the secondary to the tertiary with vertical blur and persistence - QOpenGLFramebufferObject* tertiaryFBO = - Application::getInstance()->getTextureCache()->getTertiaryFramebufferObject(); - tertiaryFBO->bind(); - - glEnable(GL_BLEND); - glBlendFunc(GL_ONE_MINUS_CONSTANT_ALPHA, GL_CONSTANT_ALPHA); - const float PERSISTENCE_SMOOTHING = 0.9f; - glBlendColor(0.0f, 0.0f, 0.0f, _isFirstFrame ? 0.0f : PERSISTENCE_SMOOTHING); - - glBindTexture(GL_TEXTURE_2D, secondaryFBO->texture()); - - _verticalBlurProgram->bind(); - renderFullscreenQuad(); - _verticalBlurProgram->release(); - - glBlendColor(0.0f, 0.0f, 0.0f, 0.0f); - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); - glDisable(GL_BLEND); - - // now add the tertiary to the primary buffer - tertiaryFBO->release(); - - glBindTexture(GL_TEXTURE_2D, primaryFBO->texture()); - - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, tertiaryFBO->texture()); - - maybeBind(destFBO); - _addSeparateProgram->bind(); - renderFullscreenQuad(); - _addSeparateProgram->release(); - maybeRelease(destFBO); - } - - glBindTexture(GL_TEXTURE_2D, 0); - glActiveTexture(GL_TEXTURE0); } glPopMatrix(); @@ -286,28 +223,6 @@ QOpenGLFramebufferObject* GlowEffect::render(bool toTexture) { return destFBO; } -void GlowEffect::cycleRenderMode() { - switch(_renderMode = (RenderMode)((_renderMode + 1) % RENDER_MODE_COUNT)) { - case ADD_MODE: - qDebug() << "Glow mode: Add"; - break; - - case BLUR_ADD_MODE: - qDebug() << "Glow mode: Blur/add"; - break; - - case BLUR_PERSIST_ADD_MODE: - qDebug() << "Glow mode: Blur/persist/add"; - break; - - default: - case DIFFUSE_ADD_MODE: - qDebug() << "Glow mode: Diffuse/add"; - break; - } - _isFirstFrame = true; -} - Glower::Glower(float amount) { Application::getInstance()->getGlowEffect()->begin(amount); } diff --git a/interface/src/renderer/GlowEffect.h b/interface/src/renderer/GlowEffect.h index 9f15a8b860..f02774accc 100644 --- a/interface/src/renderer/GlowEffect.h +++ b/interface/src/renderer/GlowEffect.h @@ -51,17 +51,10 @@ public: /// \return the framebuffer object to which we rendered, or NULL if to the frame buffer QOpenGLFramebufferObject* render(bool toTexture = false); -public slots: - - void cycleRenderMode(); - private: - - enum RenderMode { ADD_MODE, BLUR_ADD_MODE, BLUR_PERSIST_ADD_MODE, DIFFUSE_ADD_MODE, RENDER_MODE_COUNT }; - + bool _initialized; - RenderMode _renderMode; ProgramObject* _addProgram; ProgramObject* _horizontalBlurProgram; ProgramObject* _verticalBlurAddProgram; diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 55eefb0bde..3800e78008 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -44,7 +45,9 @@ Model::Model(QObject* parent) : _pupilDilation(0.0f), _url("http://invalid.com"), _blendNumber(0), - _appliedBlendNumber(0) { + _appliedBlendNumber(0), + _calculatedMeshBoxesValid(false), + _meshGroupsKnown(false) { // we may have been created in the network thread, but we live in the main thread moveToThread(Application::getInstance()->thread()); @@ -269,6 +272,8 @@ void Model::reset() { for (int i = 0; i < _jointStates.size(); i++) { _jointStates[i].setRotationInConstrainedFrame(geometry.joints.at(i).rotation, 0.0f); } + + _meshGroupsKnown = false; } bool Model::updateGeometry() { @@ -318,6 +323,7 @@ bool Model::updateGeometry() { deleteGeometry(); _dilatedTextures.clear(); _geometry = geometry; + _meshGroupsKnown = false; setJointStates(newJointStates); needToRebuild = true; } else if (_jointStates.isEmpty()) { @@ -384,7 +390,7 @@ void Model::setJointStates(QVector states) { _boundingRadius = radius; } -bool Model::render(float alpha, RenderMode mode) { +bool Model::render(float alpha, RenderMode mode, RenderArgs* args) { // render the attachments foreach (Model* attachment, _attachments) { attachment->render(alpha, mode); @@ -392,6 +398,23 @@ bool Model::render(float alpha, RenderMode mode) { if (_meshStates.isEmpty()) { return false; } + + // if we don't have valid mesh boxes, calculate them now, this only matters in cases + // where our caller has passed RenderArgs which will include a view frustum we can cull + // against. We cache the results of these calculations so long as the model hasn't been + // simulated and the mesh hasn't changed. + if (args && !_calculatedMeshBoxesValid) { + PerformanceTimer perfTimer("calculatedMeshBoxes"); + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + int numberOfMeshes = geometry.meshes.size(); + _calculatedMeshBoxes.resize(numberOfMeshes); + for (int i = 0; i < numberOfMeshes; i++) { + const FBXMesh& mesh = geometry.meshes.at(i); + Extents scaledMeshExtents = calculateScaledOffsetExtents(mesh.meshExtents); + _calculatedMeshBoxes[i] = AABox(scaledMeshExtents); + } + _calculatedMeshBoxesValid = true; + } // set up dilated textures on first render after load/simulate const FBXGeometry& geometry = _geometry->getFBXGeometry(); @@ -402,6 +425,10 @@ bool Model::render(float alpha, RenderMode mode) { _dilatedTextures.append(dilated); } } + + if (!_meshGroupsKnown) { + segregateMeshGroups(); + } glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); @@ -431,11 +458,31 @@ bool Model::render(float alpha, RenderMode mode) { mode == DEFAULT_RENDER_MODE || mode == NORMAL_RENDER_MODE, mode == DEFAULT_RENDER_MODE); - renderMeshes(mode, false); + const float DEFAULT_ALPHA_THRESHOLD = 0.5f; + + //renderMeshes(RenderMode mode, bool translucent, float alphaThreshold, bool hasTangents, bool hasSpecular, book isSkinned, args); + int opaqueMeshPartsRendered = 0; + opaqueMeshPartsRendered += renderMeshes(mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, false, args); + opaqueMeshPartsRendered += renderMeshes(mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, true, args); + opaqueMeshPartsRendered += renderMeshes(mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, false, args); + opaqueMeshPartsRendered += renderMeshes(mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, true, args); + opaqueMeshPartsRendered += renderMeshes(mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, false, args); + opaqueMeshPartsRendered += renderMeshes(mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, true, args); + opaqueMeshPartsRendered += renderMeshes(mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, false, args); + opaqueMeshPartsRendered += renderMeshes(mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, true, args); // render translucent meshes afterwards Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(false, true, true); - renderMeshes(mode, true, 0.75f); + int translucentMeshPartsRendered = 0; + const float MOSTLY_OPAQUE_THRESHOLD = 0.75f; + translucentMeshPartsRendered += renderMeshes(mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, false, args); + translucentMeshPartsRendered += renderMeshes(mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, true, args); + translucentMeshPartsRendered += renderMeshes(mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, false, args); + translucentMeshPartsRendered += renderMeshes(mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, true, args); + translucentMeshPartsRendered += renderMeshes(mode, true, MOSTLY_OPAQUE_THRESHOLD, true, false, false, args); + translucentMeshPartsRendered += renderMeshes(mode, true, MOSTLY_OPAQUE_THRESHOLD, true, false, true, args); + translucentMeshPartsRendered += renderMeshes(mode, true, MOSTLY_OPAQUE_THRESHOLD, true, true, false, args); + translucentMeshPartsRendered += renderMeshes(mode, true, MOSTLY_OPAQUE_THRESHOLD, true, false, true, args); glDisable(GL_ALPHA_TEST); glEnable(GL_BLEND); @@ -445,7 +492,15 @@ bool Model::render(float alpha, RenderMode mode) { Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true); if (mode == DEFAULT_RENDER_MODE || mode == DIFFUSE_RENDER_MODE) { - renderMeshes(mode, true, 0.0f); + const float MOSTLY_TRANSPARENT_THRESHOLD = 0.0f; + translucentMeshPartsRendered += renderMeshes(mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, false, args); + translucentMeshPartsRendered += renderMeshes(mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, true, args); + translucentMeshPartsRendered += renderMeshes(mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, false, args); + translucentMeshPartsRendered += renderMeshes(mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, true, args); + translucentMeshPartsRendered += renderMeshes(mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, false, false, args); + translucentMeshPartsRendered += renderMeshes(mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, false, true, args); + translucentMeshPartsRendered += renderMeshes(mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, true, false, args); + translucentMeshPartsRendered += renderMeshes(mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, false, true, args); } glDepthMask(true); @@ -468,6 +523,11 @@ bool Model::render(float alpha, RenderMode mode) { // restore all the default material settings Application::getInstance()->setupWorldLight(); + + if (args) { + args->_translucentMeshPartsRendered = translucentMeshPartsRendered; + args->_opaqueMeshPartsRendered = opaqueMeshPartsRendered; + } return true; } @@ -511,6 +571,22 @@ Extents Model::getUnscaledMeshExtents() const { return scaledExtents; } +Extents Model::calculateScaledOffsetExtents(const Extents& extents) const { + // we need to include any fst scaling, translation, and rotation, which is captured in the offset matrix + glm::vec3 minimum = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(extents.minimum, 1.0f)); + glm::vec3 maximum = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(extents.maximum, 1.0f)); + + Extents scaledOffsetExtents = { ((minimum + _offset) * _scale), + ((maximum + _offset) * _scale) }; + + Extents rotatedExtents = scaledOffsetExtents.getRotated(_rotation); + + Extents translatedExtents = { rotatedExtents.minimum + _translation, + rotatedExtents.maximum + _translation }; + return translatedExtents; +} + + bool Model::getJointState(int index, glm::quat& rotation) const { if (index == -1 || index >= _jointStates.size()) { return false; @@ -790,6 +866,8 @@ void Model::simulate(float deltaTime, bool fullUpdate) { || (_snapModelToRegistrationPoint && !_snappedToRegistrationPoint); if (isActive() && fullUpdate) { + _calculatedMeshBoxesValid = false; // if we have to simulate, we need to assume our mesh boxes are all invalid + // check for scale to fit if (_scaleToFit && !_scaledToFit) { scaleToFit(); @@ -1169,6 +1247,7 @@ void Model::applyNextGeometry() { // we retain a reference to the base geometry so that its reference count doesn't fall to zero _baseGeometry = _nextBaseGeometry; _geometry = _nextGeometry; + _meshGroupsKnown = false; _nextBaseGeometry.reset(); _nextGeometry.reset(); } @@ -1200,19 +1279,358 @@ void Model::deleteGeometry() { _blendedBlendshapeCoefficients.clear(); } -void Model::renderMeshes(RenderMode mode, bool translucent, float alphaThreshold) { +void Model::segregateMeshGroups() { + _meshesTranslucentTangents.clear(); + _meshesTranslucent.clear(); + _meshesTranslucentTangentsSpecular.clear(); + _meshesTranslucentSpecular.clear(); + + _meshesTranslucentTangentsSkinned.clear(); + _meshesTranslucentSkinned.clear(); + _meshesTranslucentTangentsSpecularSkinned.clear(); + _meshesTranslucentSpecularSkinned.clear(); + + _meshesOpaqueTangents.clear(); + _meshesOpaque.clear(); + _meshesOpaqueTangentsSpecular.clear(); + _meshesOpaqueSpecular.clear(); + + _meshesOpaqueTangentsSkinned.clear(); + _meshesOpaqueSkinned.clear(); + _meshesOpaqueTangentsSpecularSkinned.clear(); + _meshesOpaqueSpecularSkinned.clear(); + + _unsortedMeshesTranslucentTangents.clear(); + _unsortedMeshesTranslucent.clear(); + _unsortedMeshesTranslucentTangentsSpecular.clear(); + _unsortedMeshesTranslucentSpecular.clear(); + + _unsortedMeshesTranslucentTangentsSkinned.clear(); + _unsortedMeshesTranslucentSkinned.clear(); + _unsortedMeshesTranslucentTangentsSpecularSkinned.clear(); + _unsortedMeshesTranslucentSpecularSkinned.clear(); + + _unsortedMeshesOpaqueTangents.clear(); + _unsortedMeshesOpaque.clear(); + _unsortedMeshesOpaqueTangentsSpecular.clear(); + _unsortedMeshesOpaqueSpecular.clear(); + + _unsortedMeshesOpaqueTangentsSkinned.clear(); + _unsortedMeshesOpaqueSkinned.clear(); + _unsortedMeshesOpaqueTangentsSpecularSkinned.clear(); + _unsortedMeshesOpaqueSpecularSkinned.clear(); + + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + const QVector& networkMeshes = _geometry->getMeshes(); + + + // Run through all of the meshes, and place them into their segregated, but unsorted buckets + for (int i = 0; i < networkMeshes.size(); i++) { + const NetworkMesh& networkMesh = networkMeshes.at(i); + const FBXMesh& mesh = geometry.meshes.at(i); + const MeshState& state = _meshStates.at(i); + + bool translucentMesh = networkMesh.getTranslucentPartCount(mesh) == networkMesh.parts.size(); + bool hasTangents = !mesh.tangents.isEmpty(); + bool hasSpecular = mesh.hasSpecularTexture(); + bool isSkinned = state.clusterMatrices.size() > 1; + QString materialID; + + // create a material name from all the parts. If there's one part, this will be a single material and its + // true name. If however the mesh has multiple parts the name will be all the part's materials mashed together + // which will result in those parts being sorted away from single material parts. + QString lastPartMaterialID; + foreach(FBXMeshPart part, mesh.parts) { + if (part.materialID != lastPartMaterialID) { + materialID += part.materialID; + } + lastPartMaterialID = part.materialID; + } + const bool wantDebug = false; + if (wantDebug) { + qDebug() << "materialID:" << materialID << "parts:" << mesh.parts.size(); + } + + if (translucentMesh && !hasTangents && !hasSpecular && !isSkinned) { + + _unsortedMeshesTranslucent.insertMulti(materialID, i); + + } else if (translucentMesh && hasTangents && !hasSpecular && !isSkinned) { + + _unsortedMeshesTranslucentTangents.insertMulti(materialID, i); + + } else if (translucentMesh && hasTangents && hasSpecular && !isSkinned) { + + _unsortedMeshesTranslucentTangentsSpecular.insertMulti(materialID, i); + + } else if (translucentMesh && !hasTangents && hasSpecular && !isSkinned) { + + _unsortedMeshesTranslucentSpecular.insertMulti(materialID, i); + + } else if (translucentMesh && hasTangents && !hasSpecular && isSkinned) { + + _unsortedMeshesTranslucentTangentsSkinned.insertMulti(materialID, i); + + } else if (translucentMesh && !hasTangents && !hasSpecular && isSkinned) { + + _unsortedMeshesTranslucentSkinned.insertMulti(materialID, i); + + } else if (translucentMesh && hasTangents && hasSpecular && isSkinned) { + + _unsortedMeshesTranslucentTangentsSpecularSkinned.insertMulti(materialID, i); + + } else if (translucentMesh && !hasTangents && hasSpecular && isSkinned) { + + _unsortedMeshesTranslucentSpecularSkinned.insertMulti(materialID, i); + + } else if (!translucentMesh && !hasTangents && !hasSpecular && !isSkinned) { + + _unsortedMeshesOpaque.insertMulti(materialID, i); + + } else if (!translucentMesh && hasTangents && !hasSpecular && !isSkinned) { + + _unsortedMeshesOpaqueTangents.insertMulti(materialID, i); + + } else if (!translucentMesh && hasTangents && hasSpecular && !isSkinned) { + + _unsortedMeshesOpaqueTangentsSpecular.insertMulti(materialID, i); + + } else if (!translucentMesh && !hasTangents && hasSpecular && !isSkinned) { + + _unsortedMeshesOpaqueSpecular.insertMulti(materialID, i); + + } else if (!translucentMesh && hasTangents && !hasSpecular && isSkinned) { + + _unsortedMeshesOpaqueTangentsSkinned.insertMulti(materialID, i); + + } else if (!translucentMesh && !hasTangents && !hasSpecular && isSkinned) { + + _unsortedMeshesOpaqueSkinned.insertMulti(materialID, i); + + } else if (!translucentMesh && hasTangents && hasSpecular && isSkinned) { + + _unsortedMeshesOpaqueTangentsSpecularSkinned.insertMulti(materialID, i); + + } else if (!translucentMesh && !hasTangents && hasSpecular && isSkinned) { + + _unsortedMeshesOpaqueSpecularSkinned.insertMulti(materialID, i); + } else { + qDebug() << "unexpected!!! this mesh didn't fall into any or our groups???"; + } + } + + foreach(int i, _unsortedMeshesTranslucent) { + _meshesTranslucent.append(i); + } + + foreach(int i, _unsortedMeshesTranslucentTangents) { + _meshesTranslucentTangents.append(i); + } + + foreach(int i, _unsortedMeshesTranslucentTangentsSpecular) { + _meshesTranslucentTangentsSpecular.append(i); + } + + foreach(int i, _unsortedMeshesTranslucentSpecular) { + _meshesTranslucentSpecular.append(i); + } + + foreach(int i, _unsortedMeshesTranslucentSkinned) { + _meshesTranslucentSkinned.append(i); + } + + foreach(int i, _unsortedMeshesTranslucentTangentsSkinned) { + _meshesTranslucentTangentsSkinned.append(i); + } + + foreach(int i, _unsortedMeshesTranslucentTangentsSpecularSkinned) { + _meshesTranslucentTangentsSpecularSkinned.append(i); + } + + foreach(int i, _unsortedMeshesTranslucentSpecularSkinned) { + _meshesTranslucentSpecularSkinned.append(i); + } + + foreach(int i, _unsortedMeshesOpaque) { + _meshesOpaque.append(i); + } + + foreach(int i, _unsortedMeshesOpaqueTangents) { + _meshesOpaqueTangents.append(i); + } + + foreach(int i, _unsortedMeshesOpaqueTangentsSpecular) { + _meshesOpaqueTangentsSpecular.append(i); + } + + foreach(int i, _unsortedMeshesOpaqueSpecular) { + _meshesOpaqueSpecular.append(i); + } + + foreach(int i, _unsortedMeshesOpaqueSkinned) { + _meshesOpaqueSkinned.append(i); + } + + foreach(int i, _unsortedMeshesOpaqueTangentsSkinned) { + _meshesOpaqueTangentsSkinned.append(i); + } + + foreach(int i, _unsortedMeshesOpaqueTangentsSpecularSkinned) { + _meshesOpaqueTangentsSpecularSkinned.append(i); + } + + foreach(int i, _unsortedMeshesOpaqueSpecularSkinned) { + _meshesOpaqueSpecularSkinned.append(i); + } + + _unsortedMeshesTranslucentTangents.clear(); + _unsortedMeshesTranslucent.clear(); + _unsortedMeshesTranslucentTangentsSpecular.clear(); + _unsortedMeshesTranslucentSpecular.clear(); + + _unsortedMeshesTranslucentTangentsSkinned.clear(); + _unsortedMeshesTranslucentSkinned.clear(); + _unsortedMeshesTranslucentTangentsSpecularSkinned.clear(); + _unsortedMeshesTranslucentSpecularSkinned.clear(); + + _unsortedMeshesOpaqueTangents.clear(); + _unsortedMeshesOpaque.clear(); + _unsortedMeshesOpaqueTangentsSpecular.clear(); + _unsortedMeshesOpaqueSpecular.clear(); + + _unsortedMeshesOpaqueTangentsSkinned.clear(); + _unsortedMeshesOpaqueSkinned.clear(); + _unsortedMeshesOpaqueTangentsSpecularSkinned.clear(); + _unsortedMeshesOpaqueSpecularSkinned.clear(); + + _meshGroupsKnown = true; +} + +int Model::renderMeshes(RenderMode mode, bool translucent, float alphaThreshold, + bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args) { + + bool dontCullOutOfViewMeshParts = Menu::getInstance()->isOptionChecked(MenuOption::DontCullOutOfViewMeshParts); + bool cullTooSmallMeshParts = !Menu::getInstance()->isOptionChecked(MenuOption::DontCullTooSmallMeshParts); + bool dontReduceMaterialSwitches = Menu::getInstance()->isOptionChecked(MenuOption::DontReduceMaterialSwitches); + + QString lastMaterialID; + int meshPartsRendered = 0; updateVisibleJointStates(); const FBXGeometry& geometry = _geometry->getFBXGeometry(); const QVector& networkMeshes = _geometry->getMeshes(); + + // depending on which parameters we were called with, pick the correct mesh group to render + QVector* whichList = NULL; + if (translucent && !hasTangents && !hasSpecular && !isSkinned) { + whichList = &_meshesTranslucent; + } else if (translucent && hasTangents && !hasSpecular && !isSkinned) { + whichList = &_meshesTranslucentTangents; + } else if (translucent && hasTangents && hasSpecular && !isSkinned) { + whichList = &_meshesTranslucentTangentsSpecular; + } else if (translucent && !hasTangents && hasSpecular && !isSkinned) { + whichList = &_meshesTranslucentSpecular; + } else if (translucent && hasTangents && !hasSpecular && isSkinned) { + whichList = &_meshesTranslucentTangentsSkinned; + } else if (translucent && !hasTangents && !hasSpecular && isSkinned) { + whichList = &_meshesTranslucentSkinned; + } else if (translucent && hasTangents && hasSpecular && isSkinned) { + whichList = &_meshesTranslucentTangentsSpecularSkinned; + } else if (translucent && !hasTangents && hasSpecular && isSkinned) { + whichList = &_meshesTranslucentSpecularSkinned; + } else if (!translucent && !hasTangents && !hasSpecular && !isSkinned) { + whichList = &_meshesOpaque; + } else if (!translucent && hasTangents && !hasSpecular && !isSkinned) { + whichList = &_meshesOpaqueTangents; + } else if (!translucent && hasTangents && hasSpecular && !isSkinned) { + whichList = &_meshesOpaqueTangentsSpecular; + } else if (!translucent && !hasTangents && hasSpecular && !isSkinned) { + whichList = &_meshesOpaqueSpecular; + } else if (!translucent && hasTangents && !hasSpecular && isSkinned) { + whichList = &_meshesOpaqueTangentsSkinned; + } else if (!translucent && !hasTangents && !hasSpecular && isSkinned) { + whichList = &_meshesOpaqueSkinned; + } else if (!translucent && hasTangents && hasSpecular && isSkinned) { + whichList = &_meshesOpaqueTangentsSpecularSkinned; + } else if (!translucent && !hasTangents && hasSpecular && isSkinned) { + whichList = &_meshesOpaqueSpecularSkinned; + } else { + qDebug() << "unexpected!!! this mesh didn't fall into any or our groups???"; + } - for (int i = 0; i < networkMeshes.size(); i++) { + if (!whichList) { + qDebug() << "unexpected!!! we don't know which list of meshes to render..."; + return 0; + } + QVector& list = *whichList; + + // If this list has nothing to render, then don't bother proceeding. This saves us on binding to programs + if (list.size() == 0) { + return 0; + } + + ProgramObject* program = &_program; + Locations* locations = &_locations; + ProgramObject* skinProgram = &_skinProgram; + SkinLocations* skinLocations = &_skinLocations; + GLenum specularTextureUnit = 0; + if (mode == SHADOW_RENDER_MODE) { + program = &_shadowProgram; + skinProgram = &_skinShadowProgram; + skinLocations = &_skinShadowLocations; + } else if (translucent && alphaThreshold == 0.0f) { + program = &_translucentProgram; + locations = &_translucentLocations; + skinProgram = &_skinTranslucentProgram; + skinLocations = &_skinTranslucentLocations; + + } else if (hasTangents) { + if (hasSpecular) { + program = &_normalSpecularMapProgram; + locations = &_normalSpecularMapLocations; + skinProgram = &_skinNormalSpecularMapProgram; + skinLocations = &_skinNormalSpecularMapLocations; + specularTextureUnit = GL_TEXTURE2; + } else { + program = &_normalMapProgram; + locations = &_normalMapLocations; + skinProgram = &_skinNormalMapProgram; + skinLocations = &_skinNormalMapLocations; + } + } else if (hasSpecular) { + program = &_specularMapProgram; + locations = &_specularMapLocations; + skinProgram = &_skinSpecularMapProgram; + skinLocations = &_skinSpecularMapLocations; + specularTextureUnit = GL_TEXTURE1; + } + + ProgramObject* activeProgram = program; + Locations* activeLocations = locations; + + if (isSkinned) { + skinProgram->bind(); + activeProgram = skinProgram; + activeLocations = skinLocations; + } else { + program->bind(); + } + activeProgram->setUniformValue(activeLocations->alphaThreshold, alphaThreshold); + + // i is the "index" from the original networkMeshes QVector... + foreach (int i, list) { + + // if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown + // to false to rebuild out mesh groups. + + if (i < 0 || i >= networkMeshes.size() || i > geometry.meshes.size()) { + _meshGroupsKnown = false; // regenerate these lists next time around. + continue; + } + // exit early if the translucency doesn't match what we're drawing const NetworkMesh& networkMesh = networkMeshes.at(i); const FBXMesh& mesh = geometry.meshes.at(i); - if (translucent ? (networkMesh.getTranslucentPartCount(mesh) == 0) : - (networkMesh.getTranslucentPartCount(mesh) == networkMesh.parts.size())) { - continue; - } + const_cast(networkMesh.indexBuffer).bind(); int vertexCount = mesh.vertices.size(); @@ -1221,54 +1639,40 @@ void Model::renderMeshes(RenderMode mode, bool translucent, float alphaThreshold continue; } - const_cast(networkMesh.vertexBuffer).bind(); - - ProgramObject* program = &_program; - Locations* locations = &_locations; - ProgramObject* skinProgram = &_skinProgram; - SkinLocations* skinLocations = &_skinLocations; - GLenum specularTextureUnit = 0; - if (mode == SHADOW_RENDER_MODE) { - program = &_shadowProgram; - skinProgram = &_skinShadowProgram; - skinLocations = &_skinShadowLocations; - - } else if (translucent && alphaThreshold == 0.0f) { - program = &_translucentProgram; - locations = &_translucentLocations; - skinProgram = &_skinTranslucentProgram; - skinLocations = &_skinTranslucentLocations; - - } else if (!mesh.tangents.isEmpty()) { - if (mesh.hasSpecularTexture()) { - program = &_normalSpecularMapProgram; - locations = &_normalSpecularMapLocations; - skinProgram = &_skinNormalSpecularMapProgram; - skinLocations = &_skinNormalSpecularMapLocations; - specularTextureUnit = GL_TEXTURE2; - - } else { - program = &_normalMapProgram; - locations = &_normalMapLocations; - skinProgram = &_skinNormalMapProgram; - skinLocations = &_skinNormalMapLocations; + // if we got here, then check to see if this mesh is in view + if (args) { + bool shouldRender = true; + args->_meshesConsidered++; + + if (args->_viewFrustum) { + shouldRender = dontCullOutOfViewMeshParts || + args->_viewFrustum->boxInFrustum(_calculatedMeshBoxes.at(i)) != ViewFrustum::OUTSIDE; + if (shouldRender && cullTooSmallMeshParts) { + float distance = args->_viewFrustum->distanceToCamera(_calculatedMeshBoxes.at(i).calcCenter()); + shouldRender = Menu::getInstance()->shouldRenderMesh(_calculatedMeshBoxes.at(i).getLargestDimension(), + distance); + if (!shouldRender) { + args->_meshesTooSmall++; + } + } else { + args->_meshesOutOfView++; + } + } + + if (shouldRender) { + args->_meshesRendered++; + } else { + continue; // skip this mesh } - } else if (mesh.hasSpecularTexture()) { - program = &_specularMapProgram; - locations = &_specularMapLocations; - skinProgram = &_skinSpecularMapProgram; - skinLocations = &_skinSpecularMapLocations; - specularTextureUnit = GL_TEXTURE1; } - const MeshState& state = _meshStates.at(i); - ProgramObject* activeProgram = program; - Locations* activeLocations = locations; + const_cast(networkMesh.vertexBuffer).bind(); + glPushMatrix(); Application::getInstance()->loadTranslatedViewMatrix(_translation); - + + const MeshState& state = _meshStates.at(i); if (state.clusterMatrices.size() > 1) { - skinProgram->bind(); glUniformMatrix4fvARB(skinLocations->clusterMatrices, state.clusterMatrices.size(), false, (const float*)state.clusterMatrices.constData()); int offset = (mesh.tangents.size() + mesh.colors.size()) * sizeof(glm::vec3) + @@ -1279,16 +1683,10 @@ void Model::renderMeshes(RenderMode mode, bool translucent, float alphaThreshold offset + vertexCount * sizeof(glm::vec4), 4); skinProgram->enableAttributeArray(skinLocations->clusterIndices); skinProgram->enableAttributeArray(skinLocations->clusterWeights); - activeProgram = skinProgram; - activeLocations = skinLocations; - } else { glMultMatrixf((const GLfloat*)&state.clusterMatrices[0]); - program->bind(); } - activeProgram->setUniformValue(activeLocations->alphaThreshold, alphaThreshold); - if (mesh.blendshapes.isEmpty()) { if (!(mesh.tangents.isEmpty() || mode == SHADOW_RENDER_MODE)) { activeProgram->setAttributeBuffer(activeLocations->tangent, GL_FLOAT, vertexCount * 2 * sizeof(glm::vec3), 3); @@ -1333,45 +1731,74 @@ void Model::renderMeshes(RenderMode mode, bool translucent, float alphaThreshold glBindTexture(GL_TEXTURE_2D, 0); } else { - glm::vec4 diffuse = glm::vec4(part.diffuseColor, part.opacity); - if (!(translucent && alphaThreshold == 0.0f)) { - glAlphaFunc(GL_EQUAL, diffuse.a = Application::getInstance()->getGlowEffect()->getIntensity()); - } - glm::vec4 specular = glm::vec4(part.specularColor, 1.0f); - glMaterialfv(GL_FRONT, GL_AMBIENT, (const float*)&diffuse); - glMaterialfv(GL_FRONT, GL_DIFFUSE, (const float*)&diffuse); - glMaterialfv(GL_FRONT, GL_SPECULAR, (const float*)&specular); - glMaterialf(GL_FRONT, GL_SHININESS, part.shininess); + if (dontReduceMaterialSwitches || lastMaterialID != part.materialID) { + const bool wantDebug = false; + if (wantDebug) { + qDebug() << "Material Changed ---------------------------------------------"; + qDebug() << "part INDEX:" << j; + qDebug() << "NEW part.materialID:" << part.materialID; + } + + glm::vec4 diffuse = glm::vec4(part.diffuseColor, part.opacity); + if (!(translucent && alphaThreshold == 0.0f)) { + glAlphaFunc(GL_EQUAL, diffuse.a = Application::getInstance()->getGlowEffect()->getIntensity()); + } + glm::vec4 specular = glm::vec4(part.specularColor, 1.0f); + glMaterialfv(GL_FRONT, GL_AMBIENT, (const float*)&diffuse); + glMaterialfv(GL_FRONT, GL_DIFFUSE, (const float*)&diffuse); + glMaterialfv(GL_FRONT, GL_SPECULAR, (const float*)&specular); + glMaterialf(GL_FRONT, GL_SHININESS, part.shininess); - Texture* diffuseMap = networkPart.diffuseTexture.data(); - if (mesh.isEye && diffuseMap) { - diffuseMap = (_dilatedTextures[i][j] = - static_cast(diffuseMap)->getDilatedTexture(_pupilDilation)).data(); - } - glBindTexture(GL_TEXTURE_2D, !diffuseMap ? - Application::getInstance()->getTextureCache()->getWhiteTextureID() : diffuseMap->getID()); + Texture* diffuseMap = networkPart.diffuseTexture.data(); + if (mesh.isEye && diffuseMap) { + diffuseMap = (_dilatedTextures[i][j] = + static_cast(diffuseMap)->getDilatedTexture(_pupilDilation)).data(); + } + glBindTexture(GL_TEXTURE_2D, !diffuseMap ? + Application::getInstance()->getTextureCache()->getWhiteTextureID() : diffuseMap->getID()); - if (!mesh.tangents.isEmpty()) { - glActiveTexture(GL_TEXTURE1); - Texture* normalMap = networkPart.normalTexture.data(); - glBindTexture(GL_TEXTURE_2D, !normalMap ? - Application::getInstance()->getTextureCache()->getBlueTextureID() : normalMap->getID()); - glActiveTexture(GL_TEXTURE0); - } + if (!mesh.tangents.isEmpty()) { + glActiveTexture(GL_TEXTURE1); + Texture* normalMap = networkPart.normalTexture.data(); + glBindTexture(GL_TEXTURE_2D, !normalMap ? + Application::getInstance()->getTextureCache()->getBlueTextureID() : normalMap->getID()); + glActiveTexture(GL_TEXTURE0); + } - if (specularTextureUnit) { - glActiveTexture(specularTextureUnit); - Texture* specularMap = networkPart.specularTexture.data(); - glBindTexture(GL_TEXTURE_2D, !specularMap ? - Application::getInstance()->getTextureCache()->getWhiteTextureID() : specularMap->getID()); - glActiveTexture(GL_TEXTURE0); + if (specularTextureUnit) { + glActiveTexture(specularTextureUnit); + Texture* specularMap = networkPart.specularTexture.data(); + glBindTexture(GL_TEXTURE_2D, !specularMap ? + Application::getInstance()->getTextureCache()->getWhiteTextureID() : specularMap->getID()); + glActiveTexture(GL_TEXTURE0); + } + if (args) { + args->_materialSwitches++; + } + } + lastMaterialID = part.materialID; + } + + meshPartsRendered++; + + if (part.quadIndices.size() > 0) { + glDrawRangeElementsEXT(GL_QUADS, 0, vertexCount - 1, part.quadIndices.size(), GL_UNSIGNED_INT, (void*)offset); + offset += part.quadIndices.size() * sizeof(int); + } + + if (part.triangleIndices.size() > 0) { + glDrawRangeElementsEXT(GL_TRIANGLES, 0, vertexCount - 1, part.triangleIndices.size(), + GL_UNSIGNED_INT, (void*)offset); + offset += part.triangleIndices.size() * sizeof(int); + } + + if (args) { + const int INDICES_PER_TRIANGLE = 3; + const int INDICES_PER_QUAD = 4; + args->_trianglesRendered += part.triangleIndices.size() / INDICES_PER_TRIANGLE; + args->_quadsRendered += part.quadIndices.size() / INDICES_PER_QUAD; } - glDrawRangeElementsEXT(GL_QUADS, 0, vertexCount - 1, part.quadIndices.size(), GL_UNSIGNED_INT, (void*)offset); - offset += part.quadIndices.size() * sizeof(int); - glDrawRangeElementsEXT(GL_TRIANGLES, 0, vertexCount - 1, part.triangleIndices.size(), - GL_UNSIGNED_INT, (void*)offset); - offset += part.triangleIndices.size() * sizeof(int); } if (!mesh.colors.isEmpty()) { @@ -1401,8 +1828,10 @@ void Model::renderMeshes(RenderMode mode, bool translucent, float alphaThreshold } glPopMatrix(); - activeProgram->release(); } + activeProgram->release(); + + return meshPartsRendered; } void AnimationHandle::setURL(const QUrl& url) { diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index b82db73624..86fa0c2b7a 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -16,9 +16,9 @@ #include #include -#include - +#include #include +#include #include "GeometryCache.h" #include "InterfaceConfig.h" @@ -30,6 +30,8 @@ class QScriptEngine; class AnimationHandle; class Shape; +class RenderArgs; +class ViewFrustum; typedef QSharedPointer AnimationHandlePointer; typedef QWeakPointer WeakAnimationHandlePointer; @@ -84,7 +86,7 @@ public: enum RenderMode { DEFAULT_RENDER_MODE, SHADOW_RENDER_MODE, DIFFUSE_RENDER_MODE, NORMAL_RENDER_MODE }; - bool render(float alpha = 1.0f, RenderMode mode = DEFAULT_RENDER_MODE); + bool render(float alpha = 1.0f, RenderMode mode = DEFAULT_RENDER_MODE, RenderArgs* args = NULL); /// Sets the URL of the model to render. /// \param fallback the URL of a fallback model to render if the requested model fails to load @@ -107,6 +109,9 @@ public: /// Returns the unscaled extents of the model's mesh Extents getUnscaledMeshExtents() const; + /// Returns the scaled equivalent of some extents in model space. + Extents calculateScaledOffsetExtents(const Extents& extents) const; + /// Returns a reference to the shared geometry. const QSharedPointer& getGeometry() const { return _geometry; } @@ -247,7 +252,7 @@ private: void applyNextGeometry(); void deleteGeometry(); - void renderMeshes(RenderMode mode, bool translucent, float alphaThreshold = 0.5f); + int renderMeshes(RenderMode mode, bool translucent, float alphaThreshold, bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args = NULL); QVector createJointStates(const FBXGeometry& geometry); void initJointTransforms(); @@ -325,6 +330,54 @@ private: static SkinLocations _skinTranslucentLocations; static void initSkinProgram(ProgramObject& program, SkinLocations& locations, int specularTextureUnit = 1); + + QVector _calculatedMeshBoxes; + bool _calculatedMeshBoxesValid; + + void segregateMeshGroups(); // used to calculate our list of translucent vs opaque meshes + + bool _meshGroupsKnown; + + QMap _unsortedMeshesTranslucent; + QMap _unsortedMeshesTranslucentTangents; + QMap _unsortedMeshesTranslucentTangentsSpecular; + QMap _unsortedMeshesTranslucentSpecular; + + QMap _unsortedMeshesTranslucentSkinned; + QMap _unsortedMeshesTranslucentTangentsSkinned; + QMap _unsortedMeshesTranslucentTangentsSpecularSkinned; + QMap _unsortedMeshesTranslucentSpecularSkinned; + + QMap _unsortedMeshesOpaque; + QMap _unsortedMeshesOpaqueTangents; + QMap _unsortedMeshesOpaqueTangentsSpecular; + QMap _unsortedMeshesOpaqueSpecular; + + QMap _unsortedMeshesOpaqueSkinned; + QMap _unsortedMeshesOpaqueTangentsSkinned; + QMap _unsortedMeshesOpaqueTangentsSpecularSkinned; + QMap _unsortedMeshesOpaqueSpecularSkinned; + + QVector _meshesTranslucent; + QVector _meshesTranslucentTangents; + QVector _meshesTranslucentTangentsSpecular; + QVector _meshesTranslucentSpecular; + + QVector _meshesTranslucentSkinned; + QVector _meshesTranslucentTangentsSkinned; + QVector _meshesTranslucentTangentsSpecularSkinned; + QVector _meshesTranslucentSpecularSkinned; + + QVector _meshesOpaque; + QVector _meshesOpaqueTangents; + QVector _meshesOpaqueTangentsSpecular; + QVector _meshesOpaqueSpecular; + + QVector _meshesOpaqueSkinned; + QVector _meshesOpaqueTangentsSkinned; + QVector _meshesOpaqueTangentsSpecularSkinned; + QVector _meshesOpaqueSpecularSkinned; + }; Q_DECLARE_METATYPE(QPointer) diff --git a/interface/src/renderer/TextureCache.cpp b/interface/src/renderer/TextureCache.cpp index 00e9fcd482..f40f0e3faf 100644 --- a/interface/src/renderer/TextureCache.cpp +++ b/interface/src/renderer/TextureCache.cpp @@ -210,7 +210,7 @@ QOpenGLFramebufferObject* TextureCache::getPrimaryFramebufferObject() { if (!_primaryFramebufferObject) { _primaryFramebufferObject = createFramebufferObject(); - + glGenTextures(1, &_primaryDepthTextureID); glBindTexture(GL_TEXTURE_2D, _primaryDepthTextureID); glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, _frameBufferSize.width(), _frameBufferSize.height(), diff --git a/interface/src/renderer/TextureCache.h b/interface/src/renderer/TextureCache.h index be4d16832b..11ce312fa3 100644 --- a/interface/src/renderer/TextureCache.h +++ b/interface/src/renderer/TextureCache.h @@ -38,6 +38,7 @@ public: /// Sets the desired texture resolution for the framebuffer objects. void setFrameBufferSize(QSize frameBufferSize); + const QSize& getFrameBufferSize() const { return _frameBufferSize; } /// Returns the ID of the permutation/normal texture used for Perlin noise shader programs. This texture /// has two lines: the first, a set of random numbers in [0, 255] to be used as permutation offsets, and diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index ee7cf63789..b7e650a42d 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -1392,16 +1392,14 @@ void VoxelMaterialBoxTool::applyValue(const glm::vec3& minimum, const glm::vec3& } else { material = SharedObjectPointer(); } - QColor color = _color->getColor(); - color.setAlphaF(color.alphaF() > 0.5f ? 1.0f : 0.0f); - Cuboid* cuboid = new Cuboid(); cuboid->setTranslation((maximum + minimum) * 0.5f); glm::vec3 vector = (maximum - minimum) * 0.5f; cuboid->setScale(vector.x); cuboid->setAspectY(vector.y / vector.x); cuboid->setAspectZ(vector.z / vector.x); - MetavoxelEditMessage message = { QVariant::fromValue(VoxelMaterialSpannerEdit(SharedObjectPointer(cuboid), material, color)) }; + MetavoxelEditMessage message = { QVariant::fromValue(VoxelMaterialSpannerEdit(SharedObjectPointer(cuboid), + material, _color->getColor())) }; Application::getInstance()->getMetavoxels()->applyEdit(message, true); } @@ -1489,9 +1487,7 @@ void VoxelMaterialSpannerTool::applyEdit(const AttributePointer& attribute, cons } else { material = SharedObjectPointer(); } - QColor color = _color->getColor(); - color.setAlphaF(color.alphaF() > 0.5f ? 1.0f : 0.0f); - MetavoxelEditMessage message = { QVariant::fromValue(VoxelMaterialSpannerEdit(spanner, material, color)) }; + MetavoxelEditMessage message = { QVariant::fromValue(VoxelMaterialSpannerEdit(spanner, material, _color->getColor())) }; Application::getInstance()->getMetavoxels()->applyEdit(message, true); } diff --git a/interface/src/ui/RunningScriptsWidget.cpp b/interface/src/ui/RunningScriptsWidget.cpp index baf3995d0e..29c004bdb7 100644 --- a/interface/src/ui/RunningScriptsWidget.cpp +++ b/interface/src/ui/RunningScriptsWidget.cpp @@ -40,6 +40,9 @@ RunningScriptsWidget::RunningScriptsWidget(QWidget* parent) : connect(&_proxyModel, &QSortFilterProxyModel::modelReset, this, &RunningScriptsWidget::selectFirstInList); + QString shortcutText = Menu::getInstance()->getActionForOption(MenuOption::ReloadAllScripts)->shortcut().toString(QKeySequence::NativeText); + ui->tipLabel->setText("Tip: Use " + shortcutText + " to reload all scripts."); + _proxyModel.setSourceModel(&_scriptsModel); _proxyModel.sort(0, Qt::AscendingOrder); _proxyModel.setDynamicSortFilter(true); @@ -86,7 +89,7 @@ void RunningScriptsWidget::setBoundary(const QRect& rect) { void RunningScriptsWidget::setRunningScripts(const QStringList& list) { setUpdatesEnabled(false); QLayoutItem* widget; - while ((widget = ui->scrollAreaWidgetContents->layout()->takeAt(0)) != NULL) { + while ((widget = ui->scriptListWidget->layout()->takeAt(0)) != NULL) { delete widget->widget(); delete widget; } @@ -96,7 +99,7 @@ void RunningScriptsWidget::setRunningScripts(const QStringList& list) { if (!hash.contains(list.at(i))) { hash.insert(list.at(i), 1); } - QWidget* row = new QWidget(ui->scrollAreaWidgetContents); + QWidget* row = new QWidget(ui->scriptListWidget); row->setLayout(new QHBoxLayout(row)); QUrl url = QUrl(list.at(i)); @@ -130,17 +133,16 @@ void RunningScriptsWidget::setRunningScripts(const QStringList& list) { line->setFrameShape(QFrame::HLine); line->setStyleSheet("color: #E1E1E1; margin-left: 6px; margin-right: 6px;"); - ui->scrollAreaWidgetContents->layout()->addWidget(row); - ui->scrollAreaWidgetContents->layout()->addWidget(line); + ui->scriptListWidget->layout()->addWidget(row); + ui->scriptListWidget->layout()->addWidget(line); } ui->noRunningScriptsLabel->setVisible(list.isEmpty()); - ui->runningScriptsList->setVisible(!list.isEmpty()); ui->reloadAllButton->setVisible(!list.isEmpty()); ui->stopAllButton->setVisible(!list.isEmpty()); - ui->scrollAreaWidgetContents->updateGeometry(); + ui->scriptListWidget->updateGeometry(); setUpdatesEnabled(true); Application::processEvents(); repaint(); diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index dd9ac67837..95263282b3 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -221,8 +221,30 @@ void Stats::display( int totalServers = NodeList::getInstance()->size(); lines = _expanded ? 5 : 3; - drawBackground(backgroundColor, horizontalOffset, 0, _generalStatsWidth, lines * STATS_PELS_PER_LINE + 10); + int columnOneWidth = _generalStatsWidth; + + PerformanceTimer::tallyAllTimerRecords(); // do this even if we're not displaying them, so they don't stack up + + if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayTimingDetails)) { + + columnOneWidth = _generalStatsWidth + _pingStatsWidth + _geoStatsWidth; // make it 3 columns wide... + // we will also include room for 1 line per timing record and a header of 4 lines + lines += 4; + + const QMap& allRecords = PerformanceTimer::getAllTimerRecords(); + QMapIterator i(allRecords); + while (i.hasNext()) { + i.next(); + if (includeTimingRecord(i.key())) { + lines++; + } + } + } + + drawBackground(backgroundColor, horizontalOffset, 0, columnOneWidth, lines * STATS_PELS_PER_LINE + 10); horizontalOffset += 5; + + int columnOneHorizontalOffset = horizontalOffset; char serverNodes[30]; sprintf(serverNodes, "Servers: %d", totalServers); @@ -249,6 +271,46 @@ void Stats::display( verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, averageMegabitsPerSecond, color); } + + // TODO: the display of these timing details should all be moved to JavaScript + if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayTimingDetails)) { + // Timing details... + const int TIMER_OUTPUT_LINE_LENGTH = 1000; + char perfLine[TIMER_OUTPUT_LINE_LENGTH]; + verticalOffset += STATS_PELS_PER_LINE * 4; // skip 4 lines to be under the other columns + drawText(columnOneHorizontalOffset, verticalOffset, scale, rotation, font, + "-------------------------------------------------------- Function " + "------------------------------------------------------- --msecs- -calls--", color); + + // First iterate all the records, and for the ones that should be included, insert them into + // a new Map sorted by average time... + QMap sortedRecords; + const QMap& allRecords = PerformanceTimer::getAllTimerRecords(); + QMapIterator i(allRecords); + + while (i.hasNext()) { + i.next(); + if (includeTimingRecord(i.key())) { + float averageTime = (float)i.value().getMovingAverage() / (float)USECS_PER_MSEC; + sortedRecords.insertMulti(averageTime, i.key()); + } + } + + QMapIterator j(sortedRecords); + j.toBack(); + while (j.hasPrevious()) { + j.previous(); + QString functionName = j.value(); + const PerformanceTimerRecord& record = allRecords.value(functionName); + + sprintf(perfLine, "%120s: %8.4f [%6llu]", qPrintable(functionName), + (float)record.getMovingAverage() / (float)USECS_PER_MSEC, + record.getCount()); + + verticalOffset += STATS_PELS_PER_LINE; + drawText(columnOneHorizontalOffset, verticalOffset, scale, rotation, font, perfLine, color); + } + } verticalOffset = 0; horizontalOffset = _lastHorizontalOffset + _generalStatsWidth +1; @@ -283,7 +345,11 @@ void Stats::display( } lines = _expanded ? 4 : 3; - drawBackground(backgroundColor, horizontalOffset, 0, _pingStatsWidth, lines * STATS_PELS_PER_LINE + 10); + + // only draw our background if column one didn't draw a wide background + if (columnOneWidth == _generalStatsWidth) { + drawBackground(backgroundColor, horizontalOffset, 0, _pingStatsWidth, lines * STATS_PELS_PER_LINE + 10); + } horizontalOffset += 5; @@ -319,7 +385,9 @@ void Stats::display( lines = _expanded ? 8 : 3; - drawBackground(backgroundColor, horizontalOffset, 0, _geoStatsWidth, lines * STATS_PELS_PER_LINE + 10); + if (columnOneWidth == _generalStatsWidth) { + drawBackground(backgroundColor, horizontalOffset, 0, _geoStatsWidth, lines * STATS_PELS_PER_LINE + 10); + } horizontalOffset += 5; char avatarPosition[200]; @@ -391,39 +459,63 @@ void Stats::display( VoxelSystem* voxels = Application::getInstance()->getVoxels(); - lines = _expanded ? 11 : 3; + lines = _expanded ? 14 : 3; if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessing)) { - lines += 9; // spatial audio processing adds 1 spacing line and 8 extra lines of info + lines += 10; // spatial audio processing adds 1 spacing line and 8 extra lines of info } - if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayTimingDetails)) { - // we will also include room for 1 line per timing record and a header - lines += 1; - - const QMap& allRecords = PerformanceTimer::getAllTimerRecords(); - QMapIterator i(allRecords); - while (i.hasNext()) { - i.next(); - if (includeTimingRecord(i.key())) { - lines++; - } - } - } - drawBackground(backgroundColor, horizontalOffset, 0, glWidget->width() - horizontalOffset, lines * STATS_PELS_PER_LINE + 10); horizontalOffset += 5; + // Model/Entity render details + EntityTreeRenderer* entities = Application::getInstance()->getEntities(); + voxelStats.str(""); + voxelStats << "Entity Items rendered: " << entities->getItemsRendered() + << " / Out of view:" << entities->getItemsOutOfView() + << " / Too small:" << entities->getItemsTooSmall(); + verticalOffset += STATS_PELS_PER_LINE; + drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color); + + if (_expanded) { + voxelStats.str(""); + voxelStats << " Meshes rendered: " << entities->getMeshesRendered() + << " / Out of view:" << entities->getMeshesOutOfView() + << " / Too small:" << entities->getMeshesTooSmall(); + verticalOffset += STATS_PELS_PER_LINE; + drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color); + + voxelStats.str(""); + voxelStats << " Triangles: " << entities->getTrianglesRendered() + << " / Quads:" << entities->getQuadsRendered() + << " / Material Switches:" << entities->getMaterialSwitches(); + verticalOffset += STATS_PELS_PER_LINE; + drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color); + + voxelStats.str(""); + voxelStats << " Mesh Parts Rendered Opaque: " << entities->getOpaqueMeshPartsRendered() + << " / Translucent:" << entities->getTranslucentMeshPartsRendered(); + verticalOffset += STATS_PELS_PER_LINE; + drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color); + } + + voxelStats.str(""); + voxelStats.precision(4); + voxelStats << "Voxels Drawn: " << voxels->getVoxelsWritten() / 1000.f << "K " << + "Abandoned: " << voxels->getAbandonedVoxels() / 1000.f << "K "; + verticalOffset += STATS_PELS_PER_LINE; + drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color); + if (_expanded) { // Local Voxel Memory Usage voxelStats.str(""); - voxelStats << "Voxels Memory Nodes: " << VoxelTreeElement::getTotalMemoryUsage() / 1000000.f << "MB"; + voxelStats << " Voxels Memory Nodes: " << VoxelTreeElement::getTotalMemoryUsage() / 1000000.f << "MB"; verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color); voxelStats.str(""); voxelStats << - "Geometry RAM: " << voxels->getVoxelMemoryUsageRAM() / 1000000.f << "MB / " << + " Geometry RAM: " << voxels->getVoxelMemoryUsageRAM() / 1000000.f << "MB / " << "VBO: " << voxels->getVoxelMemoryUsageVBO() / 1000000.f << "MB"; if (voxels->hasVoxelMemoryUsageGPU()) { voxelStats << " / GPU: " << voxels->getVoxelMemoryUsageGPU() / 1000000.f << "MB"; @@ -434,18 +526,11 @@ void Stats::display( // Voxel Rendering voxelStats.str(""); voxelStats.precision(4); - voxelStats << "Voxel Rendering Slots Max: " << voxels->getMaxVoxels() / 1000.f << "K"; + voxelStats << " Voxel Rendering Slots Max: " << voxels->getMaxVoxels() / 1000.f << "K"; verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color); } - voxelStats.str(""); - voxelStats.precision(4); - voxelStats << "Drawn: " << voxels->getVoxelsWritten() / 1000.f << "K " << - "Abandoned: " << voxels->getAbandonedVoxels() / 1000.f << "K "; - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color); - // iterate all the current voxel stats, and list their sending modes, and total voxel counts std::stringstream sendingMode(""); sendingMode << "Octree Sending Mode: ["; @@ -516,44 +601,44 @@ void Stats::display( } QString serversTotalString = locale.toString((uint)totalNodes); // consider adding: .rightJustified(10, ' '); + unsigned long localTotal = VoxelTreeElement::getNodeCount(); + QString localTotalString = locale.toString((uint)localTotal); // consider adding: .rightJustified(10, ' '); - // Server Voxels - voxelStats.str(""); - voxelStats << "Server voxels: " << qPrintable(serversTotalString); - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color); - - if (_expanded) { - QString serversInternalString = locale.toString((uint)totalInternal); - QString serversLeavesString = locale.toString((uint)totalLeaves); - + // Server Octree Elements + if (!_expanded) { voxelStats.str(""); - voxelStats << - "Internal: " << qPrintable(serversInternalString) << " " << - "Leaves: " << qPrintable(serversLeavesString) << ""; + voxelStats << "Octree Elements Server: " << qPrintable(serversTotalString) + << " Local:" << qPrintable(localTotalString); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color); } - unsigned long localTotal = VoxelTreeElement::getNodeCount(); - QString localTotalString = locale.toString((uint)localTotal); // consider adding: .rightJustified(10, ' '); - - // Local Voxels - voxelStats.str(""); - voxelStats << "Local voxels: " << qPrintable(localTotalString); - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color); - if (_expanded) { + voxelStats.str(""); + voxelStats << "Octree Elements -"; + verticalOffset += STATS_PELS_PER_LINE; + drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color); + + QString serversInternalString = locale.toString((uint)totalInternal); + QString serversLeavesString = locale.toString((uint)totalLeaves); + + voxelStats.str(""); + voxelStats << " Server: " << qPrintable(serversTotalString) << + " Internal: " << qPrintable(serversInternalString) << + " Leaves: " << qPrintable(serversLeavesString); + verticalOffset += STATS_PELS_PER_LINE; + drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color); + + // Local Voxels unsigned long localInternal = VoxelTreeElement::getInternalNodeCount(); unsigned long localLeaves = VoxelTreeElement::getLeafNodeCount(); QString localInternalString = locale.toString((uint)localInternal); QString localLeavesString = locale.toString((uint)localLeaves); voxelStats.str(""); - voxelStats << - "Internal: " << qPrintable(localInternalString) << " " << - "Leaves: " << qPrintable(localLeavesString) << ""; + voxelStats << " Local: " << qPrintable(serversTotalString) << + " Internal: " << qPrintable(localInternalString) << + " Leaves: " << qPrintable(localLeavesString) << ""; verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color); } @@ -567,32 +652,6 @@ void Stats::display( drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color); } - PerformanceTimer::tallyAllTimerRecords(); - - // TODO: the display of these timing details should all be moved to JavaScript - if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayTimingDetails)) { - // Timing details... - const int TIMER_OUTPUT_LINE_LENGTH = 300; - char perfLine[TIMER_OUTPUT_LINE_LENGTH]; - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, - "--------------------- Function -------------------- --msecs- -calls--", color); - - const QMap& allRecords = PerformanceTimer::getAllTimerRecords(); - QMapIterator i(allRecords); - while (i.hasNext()) { - i.next(); - if (includeTimingRecord(i.key())) { - sprintf(perfLine, "%50s: %8.4f [%6llu]", qPrintable(i.key()), - (float)i.value().getMovingAverage() / (float)USECS_PER_MSEC, - i.value().getCount()); - - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, perfLine, color); - } - } - } - if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessing)) { verticalOffset += STATS_PELS_PER_LINE; // space one line... diff --git a/interface/src/world.h b/interface/src/world.h index 8e680f3d95..3af2390f83 100644 --- a/interface/src/world.h +++ b/interface/src/world.h @@ -13,6 +13,5 @@ #define hifi_world_h const float GRAVITY_EARTH = 9.80665f; -const float EDGE_SIZE_GROUND_PLANE = 20.f; #endif // hifi_world_h diff --git a/interface/ui/runningScriptsWidget.ui b/interface/ui/runningScriptsWidget.ui index a55e949d7a..b70200e9f2 100644 --- a/interface/ui/runningScriptsWidget.ui +++ b/interface/ui/runningScriptsWidget.ui @@ -6,8 +6,8 @@ 0 0 - 319 - 481 + 364 + 728 @@ -16,19 +16,7 @@ * { font-family: Helvetica, Arial, sans-serif; } - - - 14 - - - 20 - - - 14 - - - 20 - + @@ -195,6 +183,25 @@ font: bold 16px; 0 + + + + + 0 + 0 + + + + font: 14px; color: #5f5f5f; margin: 2px; + + + There are no scripts running. + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + @@ -219,10 +226,10 @@ font: bold 16px; 0 - Qt::ScrollBarAlwaysOn + Qt::ScrollBarAsNeeded - Qt::ScrollBarAsNeeded + Qt::ScrollBarAlwaysOff QAbstractScrollArea::AdjustToContents @@ -238,8 +245,8 @@ font: bold 16px; 0 0 - 264 - 16 + 328 + 18 @@ -254,7 +261,7 @@ font: bold 16px; font-size: 14px; - + 0 @@ -270,29 +277,41 @@ font: bold 16px; 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + font: 14px; color: #5f5f5f; margin: 2px; + + + Tip + + + - - - - - 0 - 0 - - - - font: 14px; color: #5f5f5f; - - - There are no scripts running. - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - diff --git a/libraries/audio/src/PositionalAudioStream.cpp b/libraries/audio/src/PositionalAudioStream.cpp index ae30022268..6334ac0efb 100644 --- a/libraries/audio/src/PositionalAudioStream.cpp +++ b/libraries/audio/src/PositionalAudioStream.cpp @@ -30,8 +30,7 @@ PositionalAudioStream::PositionalAudioStream(PositionalAudioStream::Type type, b _shouldLoopbackForNode(false), _isStereo(isStereo), _lastPopOutputTrailingLoudness(0.0f), - _lastPopOutputLoudness(0.0f), - _listenerUnattenuatedZone(NULL) + _lastPopOutputLoudness(0.0f) { } diff --git a/libraries/audio/src/PositionalAudioStream.h b/libraries/audio/src/PositionalAudioStream.h index 2b615a575b..6ac189352b 100644 --- a/libraries/audio/src/PositionalAudioStream.h +++ b/libraries/audio/src/PositionalAudioStream.h @@ -42,9 +42,6 @@ public: PositionalAudioStream::Type getType() const { return _type; } const glm::vec3& getPosition() const { return _position; } const glm::quat& getOrientation() const { return _orientation; } - AABox* getListenerUnattenuatedZone() const { return _listenerUnattenuatedZone; } - - void setListenerUnattenuatedZone(AABox* listenerUnattenuatedZone) { _listenerUnattenuatedZone = listenerUnattenuatedZone; } protected: // disallow copying of PositionalAudioStream objects @@ -63,7 +60,6 @@ protected: float _lastPopOutputTrailingLoudness; float _lastPopOutputLoudness; - AABox* _listenerUnattenuatedZone; }; #endif // hifi_PositionalAudioStream_h diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 47120bda7a..43d23160f7 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -659,6 +659,7 @@ public: glm::vec3 emissive; float shininess; float opacity; + QString id; }; class Cluster { @@ -1319,7 +1320,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) } } } - materials.insert(getID(object.properties), material); + material.id = getID(object.properties); + materials.insert(material.id, material); } else if (object.name == "Deformer") { if (object.properties.last() == "Cluster") { @@ -1621,6 +1623,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) if (!specularTexture.filename.isNull()) { part.specularTexture = specularTexture; } + part.materialID = material.id; } } materialIndex++; diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index 423c810418..6a7a0904da 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -115,6 +115,8 @@ public: FBXTexture diffuseTexture; FBXTexture normalTexture; FBXTexture specularTexture; + + QString materialID; }; /// A single mesh (with optional blendshapes) extracted from an FBX document. diff --git a/libraries/metavoxels/src/MetavoxelClientManager.cpp b/libraries/metavoxels/src/MetavoxelClientManager.cpp index a2d3410314..57f7b53cc3 100644 --- a/libraries/metavoxels/src/MetavoxelClientManager.cpp +++ b/libraries/metavoxels/src/MetavoxelClientManager.cpp @@ -74,6 +74,11 @@ void MetavoxelClientManager::setSpanner(const SharedObjectPointer& object, bool applyEdit(edit, reliable); } +void MetavoxelClientManager::paintHeightfieldHeight(const glm::vec3& position, float radius, float height) { + MetavoxelEditMessage edit = { QVariant::fromValue(PaintHeightfieldHeightEdit(position, radius, height)) }; + applyEdit(edit, true); +} + void MetavoxelClientManager::applyEdit(const MetavoxelEditMessage& edit, bool reliable) { QMetaObject::invokeMethod(_updater, "applyEdit", Q_ARG(const MetavoxelEditMessage&, edit), Q_ARG(bool, reliable)); } diff --git a/libraries/metavoxels/src/MetavoxelClientManager.h b/libraries/metavoxels/src/MetavoxelClientManager.h index 333b709b9e..7ddee2d68d 100644 --- a/libraries/metavoxels/src/MetavoxelClientManager.h +++ b/libraries/metavoxels/src/MetavoxelClientManager.h @@ -41,6 +41,8 @@ public: Q_INVOKABLE void setSpanner(const SharedObjectPointer& object, bool reliable = false); + Q_INVOKABLE void paintHeightfieldHeight(const glm::vec3& position, float radius, float height); + Q_INVOKABLE void applyEdit(const MetavoxelEditMessage& edit, bool reliable = false); /// Returns the current LOD. This must be thread-safe, as it will be called from the updater thread. diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 29fbe182d3..c83a953f03 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -18,6 +18,10 @@ void MetavoxelEditMessage::apply(MetavoxelData& data, const WeakSharedObjectHash MetavoxelEdit::~MetavoxelEdit() { } +void MetavoxelEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { + // nothing by default +} + BoxSetEdit::BoxSetEdit(const Box& region, float granularity, const OwnedAttributeValue& value) : region(region), granularity(granularity), value(value) { } @@ -408,6 +412,11 @@ void PaintHeightfieldHeightEdit::apply(MetavoxelData& data, const WeakSharedObje data.guide(visitor); } +MaterialEdit::MaterialEdit(const SharedObjectPointer& material, const QColor& averageColor) : + material(material), + averageColor(averageColor) { +} + class PaintHeightfieldMaterialEditVisitor : public MetavoxelVisitor { public: @@ -595,10 +604,9 @@ int PaintHeightfieldMaterialEditVisitor::visit(MetavoxelInfo& info) { PaintHeightfieldMaterialEdit::PaintHeightfieldMaterialEdit(const glm::vec3& position, float radius, const SharedObjectPointer& material, const QColor& averageColor) : + MaterialEdit(material, averageColor), position(position), - radius(radius), - material(material), - averageColor(averageColor) { + radius(radius) { } void PaintHeightfieldMaterialEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { @@ -613,9 +621,8 @@ const int VOXEL_BLOCK_VOLUME = VOXEL_BLOCK_AREA * VOXEL_BLOCK_SAMPLES; VoxelMaterialSpannerEdit::VoxelMaterialSpannerEdit(const SharedObjectPointer& spanner, const SharedObjectPointer& material, const QColor& averageColor) : - spanner(spanner), - material(material), - averageColor(averageColor) { + MaterialEdit(material, averageColor), + spanner(spanner) { } class VoxelMaterialSpannerEditVisitor : public MetavoxelVisitor { @@ -845,16 +852,18 @@ void VoxelMaterialSpannerEdit::apply(MetavoxelData& data, const WeakSharedObject while (!data.getBounds().contains(spanner->getBounds())) { data.expand(); } - VoxelMaterialSpannerEditVisitor visitor(spanner, material, averageColor); + // make sure it's either 100% transparent or 100% opaque + QColor color = averageColor; + color.setAlphaF(color.alphaF() > 0.5f ? 1.0f : 0.0f); + VoxelMaterialSpannerEditVisitor visitor(spanner, material, color); data.guide(visitor); } PaintVoxelMaterialEdit::PaintVoxelMaterialEdit(const glm::vec3& position, float radius, const SharedObjectPointer& material, const QColor& averageColor) : + MaterialEdit(material, averageColor), position(position), - radius(radius), - material(material), - averageColor(averageColor) { + radius(radius) { } class PaintVoxelMaterialEditVisitor : public MetavoxelVisitor { @@ -950,6 +959,9 @@ int PaintVoxelMaterialEditVisitor::visit(MetavoxelInfo& info) { } void PaintVoxelMaterialEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { - PaintVoxelMaterialEditVisitor visitor(position, radius, material, averageColor); + // make sure it's 100% opaque + QColor color = averageColor; + color.setAlphaF(1.0f); + PaintVoxelMaterialEditVisitor visitor(position, radius, material, color); data.guide(visitor); } diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index 0ac0bf59ec..9a7e254571 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -91,7 +91,7 @@ public: virtual ~MetavoxelEdit(); - virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const = 0; + virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; }; /// An edit that sets the region within a box to a value. @@ -224,16 +224,28 @@ public: DECLARE_STREAMABLE_METATYPE(PaintHeightfieldHeightEdit) +/// Base class for edits that have materials. +class MaterialEdit : public MetavoxelEdit { + STREAMABLE + +public: + + STREAM SharedObjectPointer material; + STREAM QColor averageColor; + + MaterialEdit(const SharedObjectPointer& material = SharedObjectPointer(), const QColor& averageColor = QColor()); +}; + +DECLARE_STREAMABLE_METATYPE(MaterialEdit) + /// An edit that sets a region of a heightfield material. -class PaintHeightfieldMaterialEdit : public MetavoxelEdit { +class PaintHeightfieldMaterialEdit : STREAM public MaterialEdit { STREAMABLE public: STREAM glm::vec3 position; STREAM float radius; - STREAM SharedObjectPointer material; - STREAM QColor averageColor; PaintHeightfieldMaterialEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f, const SharedObjectPointer& material = SharedObjectPointer(), const QColor& averageColor = QColor()); @@ -244,14 +256,12 @@ public: DECLARE_STREAMABLE_METATYPE(PaintHeightfieldMaterialEdit) /// An edit that sets the materials of voxels within a spanner to a value. -class VoxelMaterialSpannerEdit : public MetavoxelEdit { +class VoxelMaterialSpannerEdit : STREAM public MaterialEdit { STREAMABLE public: STREAM SharedObjectPointer spanner; - STREAM SharedObjectPointer material; - STREAM QColor averageColor; VoxelMaterialSpannerEdit(const SharedObjectPointer& spanner = SharedObjectPointer(), const SharedObjectPointer& material = SharedObjectPointer(), const QColor& averageColor = QColor()); @@ -262,15 +272,13 @@ public: DECLARE_STREAMABLE_METATYPE(VoxelMaterialSpannerEdit) /// An edit that sets a region of a voxel material. -class PaintVoxelMaterialEdit : public MetavoxelEdit { +class PaintVoxelMaterialEdit : STREAM public MaterialEdit { STREAMABLE public: STREAM glm::vec3 position; STREAM float radius; - STREAM SharedObjectPointer material; - STREAM QColor averageColor; PaintVoxelMaterialEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f, const SharedObjectPointer& material = SharedObjectPointer(), const QColor& averageColor = QColor()); diff --git a/libraries/networking/src/DataServerAccountInfo.cpp b/libraries/networking/src/DataServerAccountInfo.cpp index 61b1bbf418..600eb57166 100644 --- a/libraries/networking/src/DataServerAccountInfo.cpp +++ b/libraries/networking/src/DataServerAccountInfo.cpp @@ -150,9 +150,6 @@ const QByteArray& DataServerAccountInfo::getUsernameSignature() { qDebug() << "Could not create RSA struct from QByteArray private key."; qDebug() << "Will re-attempt on next domain-server check in."; } - } else { - qDebug() << "No private key present in DataServerAccountInfo. Re-log to generate new key."; - qDebug() << "Returning empty username signature."; } } diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 13ebfb59b8..f8da7cdbfa 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -315,8 +315,6 @@ void NodeList::sendDomainServerCheckIn() { if (!usernameSignature.isEmpty()) { qDebug() << "Including username signature in domain connect request."; packetStream << usernameSignature; - } else { - qDebug() << "Private key not present - domain connect request cannot include username signature"; } } diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index 21f0755aa0..be4fb68bfb 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -85,7 +85,7 @@ PacketVersion versionForPacketType(PacketType type) { case PacketTypeAudioStreamStats: return 1; case PacketTypeMetavoxelData: - return 6; + return 7; case PacketTypeVoxelData: return VERSION_VOXELS_HAS_FILE_BREAKS; default: diff --git a/libraries/networking/src/RSAKeypairGenerator.cpp b/libraries/networking/src/RSAKeypairGenerator.cpp index 5b53a84dc4..a51a17c0ca 100644 --- a/libraries/networking/src/RSAKeypairGenerator.cpp +++ b/libraries/networking/src/RSAKeypairGenerator.cpp @@ -66,11 +66,11 @@ void RSAKeypairGenerator::generateKeypair() { // cleanup the public and private key DER data, if required if (publicKeyLength > 0) { - delete publicKeyDER; + OPENSSL_free(publicKeyDER); } if (privateKeyLength > 0) { - delete privateKeyDER; + OPENSSL_free(privateKeyDER); } return; @@ -84,8 +84,8 @@ void RSAKeypairGenerator::generateKeypair() { QByteArray privateKeyArray(reinterpret_cast(privateKeyDER), privateKeyLength); // cleanup the publicKeyDER and publicKeyDER data - delete publicKeyDER; - delete privateKeyDER; + OPENSSL_free(publicKeyDER); + OPENSSL_free(privateKeyDER); emit generatedKeypair(publicKeyArray, privateKeyArray); } \ No newline at end of file diff --git a/libraries/octree/src/OctreeRenderer.cpp b/libraries/octree/src/OctreeRenderer.cpp index b2fc1241f7..d6f3caef6c 100644 --- a/libraries/octree/src/OctreeRenderer.cpp +++ b/libraries/octree/src/OctreeRenderer.cpp @@ -162,12 +162,30 @@ bool OctreeRenderer::renderOperation(OctreeElement* element, void* extraData) { } void OctreeRenderer::render(RenderMode renderMode) { - RenderArgs args = { this, _viewFrustum, getSizeScale(), getBoundaryLevelAdjust(), renderMode, 0, 0, 0 }; + RenderArgs args = { this, _viewFrustum, getSizeScale(), getBoundaryLevelAdjust(), renderMode, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; if (_tree) { _tree->lockForRead(); _tree->recurseTreeWithOperation(renderOperation, &args); _tree->unlock(); } + _meshesConsidered = args._meshesConsidered; + _meshesRendered = args._meshesRendered; + _meshesOutOfView = args._meshesOutOfView; + _meshesTooSmall = args._meshesTooSmall; + + _elementsTouched = args._elementsTouched; + _itemsRendered = args._itemsRendered; + _itemsOutOfView = args._itemsOutOfView; + _itemsTooSmall = args._itemsTooSmall; + + _materialSwitches = args._materialSwitches; + _trianglesRendered = args._trianglesRendered; + _quadsRendered = args._quadsRendered; + + _translucentMeshPartsRendered = args._translucentMeshPartsRendered; + _opaqueMeshPartsRendered = args._opaqueMeshPartsRendered; + } void OctreeRenderer::clear() { diff --git a/libraries/octree/src/OctreeRenderer.h b/libraries/octree/src/OctreeRenderer.h index d61ed3afce..e37f20529f 100644 --- a/libraries/octree/src/OctreeRenderer.h +++ b/libraries/octree/src/OctreeRenderer.h @@ -63,10 +63,45 @@ public: /// clears the tree virtual void clear(); + + int getElementsTouched() const { return _elementsTouched; } + int getItemsRendered() const { return _itemsRendered; } + int getItemsOutOfView() const { return _itemsOutOfView; } + int getItemsTooSmall() const { return _itemsTooSmall; } + + int getMeshesConsidered() const { return _meshesConsidered; } + int getMeshesRendered() const { return _meshesRendered; } + int getMeshesOutOfView() const { return _meshesOutOfView; } + int getMeshesTooSmall() const { return _meshesTooSmall; } + + int getMaterialSwitches() const { return _materialSwitches; } + int getTrianglesRendered() const { return _trianglesRendered; } + int getQuadsRendered() const { return _quadsRendered; } + + int getTranslucentMeshPartsRendered() const { return _translucentMeshPartsRendered; } + int getOpaqueMeshPartsRendered() const { return _opaqueMeshPartsRendered; } + protected: Octree* _tree; bool _managedTree; ViewFrustum* _viewFrustum; + + int _elementsTouched; + int _itemsRendered; + int _itemsOutOfView; + int _itemsTooSmall; + + int _meshesConsidered; + int _meshesRendered; + int _meshesOutOfView; + int _meshesTooSmall; + + int _materialSwitches; + int _trianglesRendered; + int _quadsRendered; + + int _translucentMeshPartsRendered; + int _opaqueMeshPartsRendered; }; class RenderArgs { @@ -80,6 +115,19 @@ public: int _elementsTouched; int _itemsRendered; int _itemsOutOfView; + int _itemsTooSmall; + + int _meshesConsidered; + int _meshesRendered; + int _meshesOutOfView; + int _meshesTooSmall; + + int _materialSwitches; + int _trianglesRendered; + int _quadsRendered; + + int _translucentMeshPartsRendered; + int _opaqueMeshPartsRendered; }; diff --git a/libraries/octree/src/ViewFrustum.cpp b/libraries/octree/src/ViewFrustum.cpp index 1e8dc41cdd..c1348e28c7 100644 --- a/libraries/octree/src/ViewFrustum.cpp +++ b/libraries/octree/src/ViewFrustum.cpp @@ -876,3 +876,10 @@ void ViewFrustum::getFurthestPointFromCameraVoxelScale(const AACube& box, glm::v } } +float ViewFrustum::distanceToCamera(const glm::vec3& point) const { + glm::vec3 temp = getPosition() - point; + float distanceToPoint = sqrtf(glm::dot(temp, temp)); + return distanceToPoint; +} + + diff --git a/libraries/octree/src/ViewFrustum.h b/libraries/octree/src/ViewFrustum.h index a23ec2af92..4b2b57fab2 100644 --- a/libraries/octree/src/ViewFrustum.h +++ b/libraries/octree/src/ViewFrustum.h @@ -118,6 +118,8 @@ public: // assumes box is in voxel scale, not TREE_SCALE, will scale view frustum's position accordingly void getFurthestPointFromCameraVoxelScale(const AACube& box, glm::vec3& furthestPoint) const; + + float distanceToCamera(const glm::vec3& point) const; private: // Used for keyhole calculations diff --git a/libraries/shared/src/AABox.h b/libraries/shared/src/AABox.h index 5e5cff4574..010a7523c5 100644 --- a/libraries/shared/src/AABox.h +++ b/libraries/shared/src/AABox.h @@ -88,13 +88,11 @@ inline bool operator==(const AABox& a, const AABox& b) { } inline QDebug operator<<(QDebug debug, const AABox& box) { - const int TREE_SCALE = 16384; // ~10 miles.. This is the number of meters of the 0.0 to 1.0 voxel universe - debug << "AABox[ (" - << box.getCorner().x * (float)TREE_SCALE << "," << box.getCorner().y * (float)TREE_SCALE << "," << box.getCorner().z * (float)TREE_SCALE << " ) to (" - << box.calcTopFarLeft().x * (float)TREE_SCALE << "," << box.calcTopFarLeft().y * (float)TREE_SCALE << "," << box.calcTopFarLeft().z * (float)TREE_SCALE << ") size: (" - << box.getDimensions().x * (float)TREE_SCALE << "," << box.getDimensions().y * (float)TREE_SCALE << "," << box.getDimensions().z * (float)TREE_SCALE << ")" - << " in meters]"; + << box.getCorner().x << "," << box.getCorner().y << "," << box.getCorner().z << " ) to (" + << box.calcTopFarLeft().x << "," << box.calcTopFarLeft().y << "," << box.calcTopFarLeft().z << ") size: (" + << box.getDimensions().x << "," << box.getDimensions().y << "," << box.getDimensions().z << ")" + << "]"; return debug; } diff --git a/libraries/shared/src/AACube.h b/libraries/shared/src/AACube.h index f56f2dee2e..7067175f3a 100644 --- a/libraries/shared/src/AACube.h +++ b/libraries/shared/src/AACube.h @@ -79,12 +79,11 @@ inline bool operator==(const AACube& a, const AACube& b) { } inline QDebug operator<<(QDebug debug, const AACube& cube) { - const int TREE_SCALE = 16384; // ~10 miles.. This is the number of meters of the 0.0 to 1.0 voxel universe debug << "AACube[ (" - << cube.getCorner().x * (float)TREE_SCALE << "," << cube.getCorner().y * (float)TREE_SCALE << "," << cube.getCorner().z * (float)TREE_SCALE << " ) to (" - << cube.calcTopFarLeft().x * (float)TREE_SCALE << "," << cube.calcTopFarLeft().y * (float)TREE_SCALE << "," << cube.calcTopFarLeft().z * (float)TREE_SCALE << ") size: (" - << cube.getDimensions().x * (float)TREE_SCALE << "," << cube.getDimensions().y * (float)TREE_SCALE << "," << cube.getDimensions().z * (float)TREE_SCALE << ")" - << " in meters]"; + << cube.getCorner().x << "," << cube.getCorner().y << "," << cube.getCorner().z << " ) to (" + << cube.calcTopFarLeft().x << "," << cube.calcTopFarLeft().y << "," << cube.calcTopFarLeft().z << ") size: (" + << cube.getDimensions().x << "," << cube.getDimensions().y << "," << cube.getDimensions().z << ")" + << "]"; return debug; } diff --git a/libraries/shared/src/ByteCountCoding.h b/libraries/shared/src/ByteCountCoding.h index ddaa750e86..1ca0deaa3a 100644 --- a/libraries/shared/src/ByteCountCoding.h +++ b/libraries/shared/src/ByteCountCoding.h @@ -129,26 +129,31 @@ template inline void ByteCountCoded::decode(const QByteArray& fro // next, read the leading bits to determine the correct number of bytes to decode (may not match the QByteArray) int encodedByteCount = 0; + int leadBits = 1; int bitAt; for (bitAt = 0; bitAt < bitCount; bitAt++) { if (encodedBits.at(bitAt)) { encodedByteCount++; + leadBits++; } else { break; } } encodedByteCount++; // always at least one byte int expectedBitCount = encodedByteCount * BITS_IN_BYTE; - - // Now, keep reading... - int valueStartsAt = bitAt + 1; + T value = 0; - T bitValue = 1; - for (bitAt = valueStartsAt; bitAt < expectedBitCount; bitAt++) { - if(encodedBits.at(bitAt)) { - value += bitValue; + + if (expectedBitCount <= (encodedBits.size() - leadBits)) { + // Now, keep reading... + int valueStartsAt = bitAt + 1; + T bitValue = 1; + for (bitAt = valueStartsAt; bitAt < expectedBitCount; bitAt++) { + if(encodedBits.at(bitAt)) { + value += bitValue; + } + bitValue *= 2; } - bitValue *= 2; } data = value; } diff --git a/libraries/shared/src/Extents.h b/libraries/shared/src/Extents.h index 2da3042467..95f242c30b 100644 --- a/libraries/shared/src/Extents.h +++ b/libraries/shared/src/Extents.h @@ -15,6 +15,9 @@ #include +#include +#include "StreamUtils.h" + class Extents { public: /// set minimum and maximum to FLT_MAX and -FLT_MAX respectively @@ -54,4 +57,15 @@ public: glm::vec3 maximum; }; +inline QDebug operator<<(QDebug debug, const Extents& extents) { + debug << "Extents[ (" + << extents.minimum << " ) to (" + << extents.maximum << ") size: (" + << (extents.maximum - extents.minimum) << ")" + << " ]"; + + return debug; +} + + #endif // hifi_Extents_h \ No newline at end of file diff --git a/libraries/shared/src/PropertyFlags.h b/libraries/shared/src/PropertyFlags.h index 04fe1475f7..7614ad7b7c 100644 --- a/libraries/shared/src/PropertyFlags.h +++ b/libraries/shared/src/PropertyFlags.h @@ -214,10 +214,12 @@ template inline void PropertyFlags::decode(const QByteArray // next, read the leading bits to determine the correct number of bytes to decode (may not match the QByteArray) int encodedByteCount = 0; + int leadBits = 1; int bitAt; for (bitAt = 0; bitAt < bitCount; bitAt++) { if (encodedBits.at(bitAt)) { encodedByteCount++; + leadBits++; } else { break; } @@ -228,10 +230,12 @@ template inline void PropertyFlags::decode(const QByteArray int expectedBitCount = encodedByteCount * BITS_PER_BYTE; // Now, keep reading... - int flagsStartAt = bitAt + 1; - for (bitAt = flagsStartAt; bitAt < expectedBitCount; bitAt++) { - if (encodedBits.at(bitAt)) { - setHasProperty((Enum)(bitAt - flagsStartAt)); + if (expectedBitCount <= (encodedBits.size() - leadBits)) { + int flagsStartAt = bitAt + 1; + for (bitAt = flagsStartAt; bitAt < expectedBitCount; bitAt++) { + if (encodedBits.at(bitAt)) { + setHasProperty((Enum)(bitAt - flagsStartAt)); + } } } } diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index ec9ef14e3c..7dafde9e43 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -10,6 +10,7 @@ // #include +#include #include #include "RegisteredMetaTypes.h" @@ -29,6 +30,7 @@ void registerMetaTypes(QScriptEngine* engine) { qScriptRegisterMetaType(engine, quatToScriptValue, quatFromScriptValue); qScriptRegisterMetaType(engine, xColorToScriptValue, xColorFromScriptValue); qScriptRegisterMetaType(engine, qColorToScriptValue, qColorFromScriptValue); + qScriptRegisterMetaType(engine, qURLToScriptValue, qURLFromScriptValue); qScriptRegisterMetaType(engine, pickRayToScriptValue, pickRayFromScriptValue); qScriptRegisterMetaType(engine, collisionToScriptValue, collisionFromScriptValue); qScriptRegisterMetaType(engine, quuidToScriptValue, quuidFromScriptValue); @@ -129,6 +131,14 @@ void qColorFromScriptValue(const QScriptValue& object, QColor& color) { } } +QScriptValue qURLToScriptValue(QScriptEngine* engine, const QUrl& url) { + return url.toString(); +} + +void qURLFromScriptValue(const QScriptValue& object, QUrl& url) { + url = object.toString(); +} + QScriptValue pickRayToScriptValue(QScriptEngine* engine, const PickRay& pickRay) { QScriptValue obj = engine->newObject(); QScriptValue origin = vec3toScriptValue(engine, pickRay.origin); diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index d3d31752b6..a1c6fdf710 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -20,6 +20,7 @@ #include "SharedUtil.h" class QColor; +class QUrl; Q_DECLARE_METATYPE(glm::vec4) Q_DECLARE_METATYPE(glm::vec3) @@ -47,6 +48,9 @@ void xColorFromScriptValue(const QScriptValue &object, xColor& color); QScriptValue qColorToScriptValue(QScriptEngine* engine, const QColor& color); void qColorFromScriptValue(const QScriptValue& object, QColor& color); +QScriptValue qURLToScriptValue(QScriptEngine* engine, const QUrl& url); +void qURLFromScriptValue(const QScriptValue& object, QUrl& url); + class PickRay { public: PickRay() : origin(0), direction(0) { }; diff --git a/libraries/voxels/src/VoxelDetail.h b/libraries/voxels/src/VoxelDetail.h index b94601392b..5ef7f0ff4e 100644 --- a/libraries/voxels/src/VoxelDetail.h +++ b/libraries/voxels/src/VoxelDetail.h @@ -15,6 +15,7 @@ #include #include +#include #include #include "VoxelConstants.h" @@ -53,8 +54,6 @@ void rayToVoxelIntersectionResultFromScriptValue(const QScriptValue& object, Ray inline QDebug operator<<(QDebug debug, const VoxelDetail& details) { - const int TREE_SCALE = 16384; // ~10 miles.. This is the number of meters of the 0.0 to 1.0 voxel universe - debug << "VoxelDetail[ (" << details.x * (float)TREE_SCALE << "," << details.y * (float)TREE_SCALE << "," << details.z * (float)TREE_SCALE << " ) to ("