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 42ea8a7341..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 @@ -58,8 +57,6 @@ #include "AvatarAudioStream.h" #include "InjectedAudioStream.h" - - #include "AudioMixer.h" const float LOUDNESS_TO_DISTANCE_RATIO = 0.00001f; @@ -197,20 +194,12 @@ int AudioMixer::addStreamToMixForListeningNodeWithStream(AudioMixerClientData* l attenuationCoefficient *= offAxisCoefficient; } - - bool wantBreak = false; + float attenuationPerDoublingInDistance = _attenuationPerDoublingInDistance; - foreach (const QString& source, _attenuationCoefficients.keys()) { - if (_audioZones[source].contains(streamToAdd->getPosition())) { - foreach (const QString& listener, _attenuationCoefficients[source].keys()) { - if (_audioZones[listener].contains(listeningNodeStream->getPosition())) { - attenuationPerDoublingInDistance = _attenuationCoefficients[source][listener]; - wantBreak = true; - break; - } - } - } - if (wantBreak) { + 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; } } @@ -1028,21 +1017,18 @@ void AudioMixer::parseSettingsObject(const QJsonObject &settingsObject) { coefficientObject.contains(LISTENER) && coefficientObject.contains(COEFFICIENT)) { - bool ok; - QString source = coefficientObject.value(SOURCE).toString(); - QString listener = coefficientObject.value(LISTENER).toString(); - float coefficient = coefficientObject.value(COEFFICIENT).toString().toFloat(&ok); + ZonesSettings settings; - if (ok && coefficient >= 0.0f && coefficient <= 1.0f && - _audioZones.contains(source) && _audioZones.contains(listener)) { + 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)) { - if (!_attenuationCoefficients.contains(source)) { - _attenuationCoefficients.insert(source, QHash()); - } - if (!_attenuationCoefficients[source].contains(listener)) { - _attenuationCoefficients[source].insert(listener, coefficient); - qDebug() << "Added Coefficient:" << source << listener << coefficient; - } + _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 7f07c846a4..3cfa5443a8 100644 --- a/assignment-client/src/audio/AudioMixer.h +++ b/assignment-client/src/audio/AudioMixer.h @@ -74,8 +74,14 @@ private: int _numStatFrames; int _sumListeners; int _sumMixes; + QHash _audioZones; - QHash > _attenuationCoefficients; + struct ZonesSettings { + QString source; + QString listener; + float coefficient; + }; + QVector _zonesSettings; static InboundAudioStream::Settings _streamSettings; diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 42af8ff1b9..d0a17287cb 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -861,8 +861,6 @@ void OctreeServer::readPendingDatagram(const QByteArray& receivedPacket, const H } } else if (packetType == PacketTypeJurisdictionRequest) { _jurisdictionSender->queueReceivedPacket(matchingNode, receivedPacket); - } else if (packetType == PacketTypeSignedTransactionPayment) { - handleSignedTransactionPayment(packetType, receivedPacket); } else if (_octreeInboundPacketProcessor && getOctree()->handlesEditPacketType(packetType)) { _octreeInboundPacketProcessor->queueReceivedPacket(matchingNode, receivedPacket); } else { @@ -1245,51 +1243,6 @@ QString OctreeServer::getStatusLink() { return result; } -void OctreeServer::handleSignedTransactionPayment(PacketType packetType, const QByteArray& datagram) { - // for now we're not verifying that this is actual payment for any octree edits - // just use the AccountManager to send it up to the data server and have it redeemed - AccountManager& accountManager = AccountManager::getInstance(); - - const int NUM_BYTES_SIGNED_TRANSACTION_BINARY_MESSAGE = 72; - const int NUM_BYTES_SIGNED_TRANSACTION_BINARY_SIGNATURE = 256; - - int numBytesPacketHeader = numBytesForPacketHeaderGivenPacketType(packetType); - - // pull out the transaction message in binary - QByteArray messageHex = datagram.mid(numBytesPacketHeader, NUM_BYTES_SIGNED_TRANSACTION_BINARY_MESSAGE).toHex(); - // pull out the binary signed message digest - QByteArray signatureHex = datagram.mid(numBytesPacketHeader + NUM_BYTES_SIGNED_TRANSACTION_BINARY_MESSAGE, - NUM_BYTES_SIGNED_TRANSACTION_BINARY_SIGNATURE).toHex(); - - // setup the QJSONObject we are posting - QJsonObject postObject; - - const QString TRANSACTION_OBJECT_MESSAGE_KEY = "message"; - const QString TRANSACTION_OBJECT_SIGNATURE_KEY = "signature"; - const QString POST_OBJECT_TRANSACTION_KEY = "transaction"; - - QJsonObject transactionObject; - transactionObject.insert(TRANSACTION_OBJECT_MESSAGE_KEY, QString(messageHex)); - transactionObject.insert(TRANSACTION_OBJECT_SIGNATURE_KEY, QString(signatureHex)); - - postObject.insert(POST_OBJECT_TRANSACTION_KEY, transactionObject); - - // setup our callback params - JSONCallbackParameters callbackParameters; - callbackParameters.jsonCallbackReceiver = this; - callbackParameters.jsonCallbackMethod = "handleSignedTransactionPaymentResponse"; - - accountManager.unauthenticatedRequest("/api/v1/transactions/redeem", QNetworkAccessManager::PostOperation, - callbackParameters, QJsonDocument(postObject).toJson()); - -} - -void OctreeServer::handleSignedTransactionPaymentResponse(const QJsonObject& jsonObject) { - // pull the ID to debug the transaction - QString transactionIDString = jsonObject["data"].toObject()["transaction"].toObject()["id"].toString(); - qDebug() << "Redeemed transaction with ID" << transactionIDString << "successfully."; -} - void OctreeServer::sendStatsPacket() { // TODO: we have too many stats to fit in a single MTU... so for now, we break it into multiple JSON objects and // send them separately. What we really should do is change the NodeList::sendStatsToDomainServer() to handle the diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h index aa0c419dd4..2fcaae8c78 100644 --- a/assignment-client/src/octree/OctreeServer.h +++ b/assignment-client/src/octree/OctreeServer.h @@ -127,8 +127,6 @@ public slots: void nodeKilled(SharedNodePointer node); void sendStatsPacket(); - void handleSignedTransactionPaymentResponse(const QJsonObject& jsonObject); - void readPendingDatagrams() { }; // this will not be called since our datagram processing thread will handle void readPendingDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr); @@ -141,7 +139,6 @@ protected: QString getConfiguration(); QString getStatusLink(); - void handleSignedTransactionPayment(PacketType packetType, const QByteArray& datagram); void setupDatagramProcessingThread(); int _argc; diff --git a/domain-server/CMakeLists.txt b/domain-server/CMakeLists.txt index 17d8b61b11..9090a1b637 100644 --- a/domain-server/CMakeLists.txt +++ b/domain-server/CMakeLists.txt @@ -38,4 +38,18 @@ endif () # link the shared hifi libraries link_hifi_libraries(embedded-webserver networking shared) +# find OpenSSL +find_package(OpenSSL REQUIRED) + +if (APPLE AND ${OPENSSL_INCLUDE_DIR} STREQUAL "/usr/include") + # this is a user on OS X using system OpenSSL, which is going to throw warnings since they're deprecating for their common crypto + message(WARNING "The found version of OpenSSL is the OS X system version. This will produce deprecation warnings." + "\nWe recommend you install a newer version (at least 1.0.1h) in a different directory and set OPENSSL_ROOT_DIR in your env so Cmake can find it.") +endif () + +include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}") + +# append OpenSSL to our list of libraries to link +list(APPEND ${TARGET_NAME}_LIBRARIES_TO_LINK "${OPENSSL_LIBRARIES}") + link_shared_dependencies() \ No newline at end of file diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 38df52c8eb..b8bc783aa1 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -59,6 +59,20 @@ "type": "password", "help": "Password used for basic HTTP authentication. Leave this blank if you do not want to change it.", "value-hidden": true + }, + { + "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.", + "numbered": false, + "columns": [ + { + "name": "username", + "label": "Username", + "can_set": true + } + ] } ] }, @@ -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", @@ -144,7 +159,7 @@ }, { "name": "audio_buffer", - "label": "Audio Buffer", + "label": "Audio Buffers", "assignment-types": [0], "settings": [ { 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 2b8b42d029..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' }; @@ -110,6 +118,14 @@ $(document).ready(function(){ $('#settings-form').on('click', '.' + Settings.DEL_ROW_BUTTON_CLASS, function(){ deleteTableRow(this); }) + + $('#settings-form').on('click', '.' + Settings.MOVE_UP_BUTTON_CLASS, function(){ + moveTableRow(this, true); + }) + + $('#settings-form').on('click', '.' + Settings.MOVE_DOWN_BUTTON_CLASS, function(){ + moveTableRow(this, false); + }) $('#settings-form').on('keypress', 'table input', function(e){ if (e.keyCode == 13) { @@ -120,7 +136,7 @@ $(document).ready(function(){ if (sibling.hasClass(Settings.DATA_COL_CLASS)) { // set focus to next input sibling.find('input').focus() - } else if (sibling.hasClass(Settings.TABLE_BUTTONS_CLASS)) { + } else if (sibling.hasClass(Settings.ADD_DEL_BUTTONS_CLASS)) { sibling.find('.' + Settings.ADD_ROW_BUTTON_CLASS).click() // set focus to the first input in the new row @@ -239,6 +255,10 @@ $('body').on('click', '.save-button', function(e){ function makeTable(setting, setting_name, setting_value) { var isArray = !_.has(setting, 'key') + if (!isArray && setting.can_order) { + setting.can_order = false; + } + var html = (setting.label) ? "" : "" html += "" + setting.help + "" html += "" + col.label + "" // Data }) - html += "" + if (setting.can_order) { + html += ""; + } + html += "" // populate rows in the table from existing values var row_num = 1 @@ -279,13 +303,13 @@ function makeTable(setting, setting_name, setting_value) { html += "" }) - html += "" + if (setting.can_order) { + html += "" + } + html += "" html += "" row_num++ @@ -324,8 +354,12 @@ function makeTableInputs(setting) { \ " }) - - html += "" + + if (setting.can_order) { + html += "" + } + html += "" html += "" return html @@ -418,7 +452,10 @@ function addTableRow(add_glyphicon) { } else { $(element).html(1) } - } else if ($(element).hasClass("buttons")) { + } else if ($(element).hasClass(Settings.REORDER_BUTTONS_CLASS)) { + $(element).html("") + } else if ($(element).hasClass(Settings.ADD_DEL_BUTTONS_CLASS)) { // Change buttons var span = $(element).children("span") span.removeClass(Settings.ADD_ROW_SPAN_CLASSES) @@ -499,7 +536,32 @@ function deleteTableRow(delete_glyphicon) { // we need to fire a change event on one of the remaining inputs so that the sidebar badge is updated badgeSidebarForDifferences($(table)) -} +} + +function moveTableRow(move_glyphicon, move_up) { + var row = $(move_glyphicon).closest('tr') + + var table = $(row).closest('table') + var isArray = table.data('setting-type') === 'array' + if (!isArray) { + return; + } + + if (move_up) { + var prev_row = row.prev() + if (prev_row.hasClass(Settings.DATA_ROW_CLASS)) { + prev_row.before(row) + } + } else { + var next_row = row.next() + if (next_row.hasClass(Settings.DATA_ROW_CLASS)) { + next_row.after(row) + } + } + + // we need to fire a change event on one of the remaining inputs so that the sidebar badge is updated + badgeSidebarForDifferences($(table)) +} function updateDataChangedForSiblingRows(row, forceTrue) { // anytime a new row is added to an array we need to set data-changed for all sibling row inputs to true diff --git a/domain-server/resources/web/settings/index.shtml b/domain-server/resources/web/settings/index.shtml index 9bc8d4bf8f..598f137285 100644 --- a/domain-server/resources/web/settings/index.shtml +++ b/domain-server/resources/web/settings/index.shtml @@ -22,7 +22,7 @@ - + @@ -32,25 +32,32 @@ - +
diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index fa7a0fe012..74bb3b6a2b 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -9,6 +9,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include +#include + #include #include #include @@ -44,8 +47,6 @@ DomainServer::DomainServer(int argc, char* argv[]) : _oauthProviderURL(), _oauthClientID(), _hostname(), - _networkReplyUUIDMap(), - _sessionAuthenticationHash(), _webAuthenticationStateSet(), _cookieSessionHash(), _settingsManager() @@ -80,6 +81,9 @@ DomainServer::DomainServer(int argc, char* argv[]) : // setup automatic networking settings with data server setupAutomaticNetworking(); + + // preload some user public keys so they can connect on first request + preloadAllowedUserPublicKeys(); } } @@ -507,8 +511,6 @@ void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSetwriteUnverifiedDatagram(oauthRequestByteArray, senderSockAddr); - - return; - } + QList nodeInterestList; + QString username; + QByteArray usernameSignature; + + packetStream >> nodeInterestList >> username >> usernameSignature; + + if (!isAssignment && !shouldAllowConnectionFromNode(username, usernameSignature, senderSockAddr)) { + // this is an agent and we've decided we won't let them connect - send them a packet to deny connection + QByteArray usernameRequestByteArray = byteArrayWithPopulatedHeader(PacketTypeDomainConnectionDenied); + + // send this oauth request datagram back to the client + LimitedNodeList::getInstance()->writeUnverifiedDatagram(usernameRequestByteArray, senderSockAddr); + + return; } if ((!isAssignment && !STATICALLY_ASSIGNED_NODES.contains(nodeType)) @@ -610,15 +602,109 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock } // if we have a username from an OAuth connect request, set it on the DomainServerNodeData - nodeData->setUsername(connectedUsername); - + nodeData->setUsername(username); nodeData->setSendingSockAddr(senderSockAddr); // reply back to the user with a PacketTypeDomainList - sendDomainListToNode(newNode, senderSockAddr, nodeInterestListFromPacket(packet, numPreInterestBytes)); + sendDomainListToNode(newNode, senderSockAddr, nodeInterestList.toSet()); } } +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(), + ALLOWED_USERS_SETTINGS_KEYPATH); + static QStringList allowedUsers = allowedUsersVariant ? allowedUsersVariant->toStringList() : QStringList(); + + 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."; + } + } + + requestUserPublicKey(username); + } + } + } else { + // since we have no allowed user list, let them all in + return true; + } + + return false; +} + +void DomainServer::preloadAllowedUserPublicKeys() { + const QVariant* allowedUsersVariant = valueForKeyPath(_settingsManager.getSettingsMap(), ALLOWED_USERS_SETTINGS_KEYPATH); + QStringList allowedUsers = allowedUsersVariant ? allowedUsersVariant->toStringList() : QStringList(); + + if (allowedUsers.size() > 0) { + // in the future we may need to limit how many requests here - for now assume that lists of allowed users are not + // going to create > 100 requests + foreach(const QString& username, allowedUsers) { + requestUserPublicKey(username); + } + } +} + +void DomainServer::requestUserPublicKey(const QString& username) { + // even if we have a public key for them right now, request a new one in case it has just changed + JSONCallbackParameters callbackParams; + callbackParams.jsonCallbackReceiver = this; + callbackParams.jsonCallbackMethod = "publicKeyJSONCallback"; + + const QString USER_PUBLIC_KEY_PATH = "api/v1/users/%1/public_key"; + + qDebug() << "Requesting public key for user" << username; + + AccountManager::getInstance().unauthenticatedRequest(USER_PUBLIC_KEY_PATH.arg(username), + QNetworkAccessManager::GetOperation, callbackParams); +} + QUrl DomainServer::oauthRedirectURL() { return QString("https://%1:%2/oauth").arg(_hostname).arg(_httpsManager->serverPort()); } @@ -653,12 +739,9 @@ QUrl DomainServer::oauthAuthorizationURL(const QUuid& stateUUID) { return authorizationURL; } -int DomainServer::parseNodeDataFromByteArray(NodeType_t& nodeType, HifiSockAddr& publicSockAddr, - HifiSockAddr& localSockAddr, const QByteArray& packet, - const HifiSockAddr& senderSockAddr) { - QDataStream packetStream(packet); - packetStream.skipRawData(numBytesForPacketHeader(packet)); - +int DomainServer::parseNodeDataFromByteArray(QDataStream& packetStream, NodeType_t& nodeType, + HifiSockAddr& publicSockAddr, HifiSockAddr& localSockAddr, + const HifiSockAddr& senderSockAddr) { packetStream >> nodeType; packetStream >> publicSockAddr >> localSockAddr; @@ -925,7 +1008,30 @@ void DomainServer::sendPendingTransactionsToServer() { ++i; } } +} +void DomainServer::publicKeyJSONCallback(QNetworkReply& requestReply) { + QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object(); + + if (jsonObject["status"].toString() == "success") { + // figure out which user this is for + + const QString PUBLIC_KEY_URL_REGEX_STRING = "api\\/v1\\/users\\/([A-Za-z0-9_\\.]+)\\/public_key"; + QRegExp usernameRegex(PUBLIC_KEY_URL_REGEX_STRING); + + if (usernameRegex.indexIn(requestReply.url().toString()) != -1) { + QString username = usernameRegex.cap(1); + + qDebug() << "Storing a public key for user" << username; + + // pull the public key as a QByteArray from this response + const QString JSON_DATA_KEY = "data"; + const QString JSON_PUBLIC_KEY_KEY = "public_key"; + + _userPublicKeys[username] = + QByteArray::fromBase64(jsonObject[JSON_DATA_KEY].toObject()[JSON_PUBLIC_KEY_KEY].toString().toUtf8()); + } + } } void DomainServer::transactionJSONCallback(const QJsonObject& data) { @@ -1095,17 +1201,24 @@ void DomainServer::processDatagram(const QByteArray& receivedPacket, const HifiS if (!nodeUUID.isNull() && nodeList->nodeWithUUID(nodeUUID)) { NodeType_t throwawayNodeType; HifiSockAddr nodePublicAddress, nodeLocalAddress; + + QDataStream packetStream(receivedPacket); + packetStream.skipRawData(numBytesForPacketHeader(receivedPacket)); - int numNodeInfoBytes = parseNodeDataFromByteArray(throwawayNodeType, nodePublicAddress, nodeLocalAddress, - receivedPacket, 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; @@ -1545,13 +1658,6 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u // we've redirected the user back to our homepage return true; - } else { - qDebug() << "Requesting a token for user with session UUID" << uuidStringWithoutCurlyBraces(stateUUID); - - // insert this to our pending token replies so we can associate the returned access token with the right UUID - _networkReplyUUIDMap.insert(tokenReply, stateUUID); - - connect(tokenReply, &QNetworkReply::finished, this, &DomainServer::handleTokenRequestFinished); } } @@ -1695,22 +1801,6 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl const QString OAUTH_JSON_ACCESS_TOKEN_KEY = "access_token"; -void DomainServer::handleTokenRequestFinished() { - QNetworkReply* networkReply = reinterpret_cast(sender()); - QUuid matchingSessionUUID = _networkReplyUUIDMap.take(networkReply); - - if (!matchingSessionUUID.isNull() && networkReply->error() == QNetworkReply::NoError) { - - qDebug() << "Received access token for user with UUID" << uuidStringWithoutCurlyBraces(matchingSessionUUID) - << "-" << "requesting profile."; - - QNetworkReply* profileReply = profileRequestGivenTokenReply(networkReply); - - connect(profileReply, &QNetworkReply::finished, this, &DomainServer::handleProfileRequestFinished); - - _networkReplyUUIDMap.insert(profileReply, matchingSessionUUID); - } -} QNetworkReply* DomainServer::profileRequestGivenTokenReply(QNetworkReply* tokenReply) { // pull the access token from the returned JSON and store it with the matching session UUID @@ -1719,54 +1809,12 @@ QNetworkReply* DomainServer::profileRequestGivenTokenReply(QNetworkReply* tokenR // fire off a request to get this user's identity so we can see if we will let them in QUrl profileURL = _oauthProviderURL; - profileURL.setPath("/api/v1/users/profile"); + profileURL.setPath("/api/v1/user/profile"); profileURL.setQuery(QString("%1=%2").arg(OAUTH_JSON_ACCESS_TOKEN_KEY, accessToken)); return NetworkAccessManager::getInstance().get(QNetworkRequest(profileURL)); } -void DomainServer::handleProfileRequestFinished() { - QNetworkReply* networkReply = reinterpret_cast(sender()); - QUuid matchingSessionUUID = _networkReplyUUIDMap.take(networkReply); - - if (!matchingSessionUUID.isNull() && networkReply->error() == QNetworkReply::NoError) { - QJsonDocument profileJSON = QJsonDocument::fromJson(networkReply->readAll()); - - if (profileJSON.object()["status"].toString() == "success") { - // pull the user roles from the response - QJsonArray userRolesArray = profileJSON.object()["data"].toObject()["user"].toObject()["roles"].toArray(); - - QStringList allowedRolesArray = _settingsManager.getSettingsMap().value(ALLOWED_ROLES_CONFIG_KEY).toStringList(); - - QString connectableUsername; - QString profileUsername = profileJSON.object()["data"].toObject()["user"].toObject()["username"].toString(); - - foreach(const QJsonValue& roleValue, userRolesArray) { - if (allowedRolesArray.contains(roleValue.toString())) { - // the user has a role that lets them in - // set the bool to true and break - connectableUsername = profileUsername; - break; - } - } - - if (connectableUsername.isEmpty()) { - qDebug() << "User" << profileUsername << "with session UUID" - << uuidStringWithoutCurlyBraces(matchingSessionUUID) - << "does not have an allowable role. Refusing connection."; - } else { - qDebug() << "User" << profileUsername << "with session UUID" - << uuidStringWithoutCurlyBraces(matchingSessionUUID) - << "has an allowable role. Can connect."; - } - - // insert this UUID and a flag that indicates if they are allowed to connect - _sessionAuthenticationHash.insert(matchingSessionUUID, connectableUsername); - } - } -} - - const QString DS_SETTINGS_SESSIONS_GROUP = "web-sessions"; Headers DomainServer::setupCookieHeadersFromProfileReply(QNetworkReply* profileReply) { diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 0ad2aae8a8..5e4da00601 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -52,6 +52,7 @@ public slots: /// Called by NodeList to inform us a node has been killed void nodeKilled(SharedNodePointer node); + void publicKeyJSONCallback(QNetworkReply& requestReply); void transactionJSONCallback(const QJsonObject& data); void restart(); @@ -82,8 +83,17 @@ private: void processDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr); void handleConnectRequest(const QByteArray& packet, const HifiSockAddr& senderSockAddr); - int parseNodeDataFromByteArray(NodeType_t& nodeType, HifiSockAddr& publicSockAddr, - HifiSockAddr& localSockAddr, const QByteArray& packet, const HifiSockAddr& senderSockAddr); + bool shouldAllowConnectionFromNode(const QString& username, const QByteArray& usernameSignature, + const HifiSockAddr& senderSockAddr); + + void preloadAllowedUserPublicKeys(); + void requestUserPublicKey(const QString& username); + + int parseNodeDataFromByteArray(QDataStream& packetStream, + NodeType_t& nodeType, + HifiSockAddr& publicSockAddr, + HifiSockAddr& localSockAddr, + const HifiSockAddr& senderSockAddr); NodeSet nodeInterestListFromPacket(const QByteArray& packet, int numPreceedingBytes); void sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr& senderSockAddr, const NodeSet& nodeInterestList); @@ -131,13 +141,11 @@ private: QString _oauthClientID; QString _oauthClientSecret; QString _hostname; - QMap _networkReplyUUIDMap; - QHash _sessionAuthenticationHash; QSet _webAuthenticationStateSet; QHash _cookieSessionHash; - HifiSockAddr _localSockAddr; + QHash _userPublicKeys; QHash _connectingICEPeers; QHash _connectedICEPeers; 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/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 8674dd484a..3db3cec7cd 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" "GVERB") +set(OPTIONAL_EXTERNALS "Faceshift" "LibOVR" "PrioVR" "Sixense" "Visage" "LeapMotion" "RtMidi" "Qxmpp" "SDL2" "GVERB") foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) string(TOUPPER ${EXTERNAL} ${EXTERNAL}_UPPERCASE) if (NOT ${${EXTERNAL}_UPPERCASE}_ROOT_DIR) @@ -108,7 +108,6 @@ link_hifi_libraries(shared octree voxels fbx metavoxels networking particles ent # find any optional and required libraries find_package(ZLIB REQUIRED) -find_package(OpenSSL REQUIRED) # perform standard include and linking for found externals foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) @@ -178,10 +177,9 @@ endif (GVERB_FOUND) # include headers for interface and InterfaceConfig. include_directories("${PROJECT_SOURCE_DIR}/src" "${PROJECT_BINARY_DIR}/includes") -include_directories("${OPENSSL_INCLUDE_DIR}") target_link_libraries( - ${TARGET_NAME} ${ZLIB_LIBRARIES} ${OPENSSL_LIBRARIES} + ${TARGET_NAME} ${ZLIB_LIBRARIES} Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::OpenGL Qt5::Script Qt5::Svg Qt5::WebKitWidgets ) 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 d6d0af0732..b85e1e4440 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -72,7 +72,6 @@ #include "InterfaceVersion.h" #include "Menu.h" #include "ModelUploader.h" -#include "PaymentManager.h" #include "Util.h" #include "devices/MIDIManager.h" #include "devices/OculusManager.h" @@ -90,7 +89,6 @@ #include "scripting/WindowScriptingInterface.h" #include "ui/InfoView.h" -#include "ui/OAuthWebViewHandler.h" #include "ui/Snapshot.h" #include "ui/Stats.h" #include "ui/TextRenderer.h" @@ -220,10 +218,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : listenPort = atoi(portStr); } - // call the OAuthWebviewHandler static getter so that its instance lives in our thread - // make sure it is ready before the NodeList might need it - OAuthWebViewHandler::getInstance(); - // start the nodeThread so its event loop is running _nodeThread->start(); @@ -256,11 +250,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(&domainHandler, &DomainHandler::settingsReceived, this, &Application::domainSettingsReceived); connect(&domainHandler, &DomainHandler::hostnameChanged, Menu::getInstance(), &Menu::clearLoginDialogDisplayedFlag); - // hookup VoxelEditSender to PaymentManager so we can pay for octree edits - const PaymentManager& paymentManager = PaymentManager::getInstance(); - connect(&_voxelEditSender, &VoxelEditPacketSender::octreePaymentRequired, - &paymentManager, &PaymentManager::sendSignedPayment); - // update our location every 5 seconds in the data-server, assuming that we are authenticated with one const qint64 DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * 1000; @@ -1557,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 { @@ -1847,7 +1835,6 @@ void Application::init() { // initialize our face trackers after loading the menu settings _faceshift.init(); - _faceplus.init(); _visage.init(); Leapmotion::init(); @@ -3391,7 +3378,6 @@ void Application::resetSensors() { _mouseX = _glWidget->width() / 2; _mouseY = _glWidget->height() / 2; - _faceplus.reset(); _faceshift.reset(); _visage.reset(); _dde.reset(); @@ -3486,7 +3472,7 @@ void Application::updateLocationInServer() { rootObject.insert(LOCATION_KEY_IN_ROOT, locationObject); - accountManager.authenticatedRequest("/api/v1/users/location", QNetworkAccessManager::PutOperation, + accountManager.authenticatedRequest("/api/v1/user/location", QNetworkAccessManager::PutOperation, JSONCallbackParameters(), QJsonDocument(rootObject).toJson()); } } @@ -3529,20 +3515,18 @@ void Application::domainChanged(const QString& domainHostname) { // reset the voxels renderer _voxels.killLocalVoxels(); - - // reset the auth URL for OAuth web view handler - OAuthWebViewHandler::getInstance().clearLastAuthorizationURL(); } void Application::connectedToDomain(const QString& hostname) { AccountManager& accountManager = AccountManager::getInstance(); + const QUuid& domainID = NodeList::getInstance()->getDomainHandler().getUUID(); + + if (accountManager.isLoggedIn() && !domainID.isNull()) { + // update our data-server with the domain-server we're logged in with - if (accountManager.isLoggedIn()) { - // update our domain-server with the data-server we're logged in with + QString domainPutJsonString = "{\"location\":{\"domain_id\":\"" + uuidStringWithoutCurlyBraces(domainID) + "\"}}"; - QString domainPutJsonString = "{\"address\":{\"domain\":\"" + hostname + "\"}}"; - - accountManager.authenticatedRequest("/api/v1/users/address", QNetworkAccessManager::PutOperation, + accountManager.authenticatedRequest("/api/v1/user/location", QNetworkAccessManager::PutOperation, JSONCallbackParameters(), domainPutJsonString.toUtf8()); } } diff --git a/interface/src/Application.h b/interface/src/Application.h index aab051505a..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; } @@ -406,7 +404,6 @@ private: // Various helper functions called during update() void updateLOD(); void updateMouseRay(); - void updateFaceplus(); void updateFaceshift(); void updateVisage(); void updateDDE(); @@ -507,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; diff --git a/interface/src/Audio.h b/interface/src/Audio.h index 548ae19143..7390a3c28c 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 0889007c76..3334219dbd 100644 --- a/interface/src/DatagramProcessor.cpp +++ b/interface/src/DatagramProcessor.cpp @@ -11,11 +11,11 @@ #include +#include #include #include "Application.h" #include "Menu.h" -#include "ui/OAuthWebViewHandler.h" #include "DatagramProcessor.h" @@ -136,16 +136,12 @@ void DatagramProcessor::processDatagrams() { application->_bandwidthMeter.inputStream(BandwidthMeter::AVATARS).updateValue(incomingPacket.size()); break; } - case PacketTypeDomainOAuthRequest: { - QDataStream readStream(incomingPacket); - readStream.skipRawData(numBytesForPacketHeader(incomingPacket)); - - QUrl authorizationURL; - readStream >> authorizationURL; - - QMetaObject::invokeMethod(&OAuthWebViewHandler::getInstance(), "displayWebviewForAuthorizationURL", - Q_ARG(const QUrl&, authorizationURL)); - + case PacketTypeDomainConnectionDenied: { + // 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; } case PacketTypeMuteEnvironment: { 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..036d137cd3 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.04f; +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 10b99c4948..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(); @@ -407,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())); @@ -426,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); @@ -1550,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; } @@ -1572,6 +1574,7 @@ void Menu::autoAdjustLOD(float currentFPS) { } if (changed) { + _shouldRenderTableNeedsRebuilding = true; if (_lodToolsDialog) { _lodToolsDialog->reloadSliders(); } @@ -1586,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 5775e003a6..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 @@ -310,6 +312,9 @@ private: QString _snapshotsLocation; QString _scriptsLocation; QByteArray _walletPrivateKey; + + bool _shouldRenderTableNeedsRebuilding; + QMap _shouldRenderTable; }; @@ -367,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"; @@ -391,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"; diff --git a/interface/src/ModelUploader.cpp b/interface/src/ModelUploader.cpp index f11d035ebe..7077c44287 100644 --- a/interface/src/ModelUploader.cpp +++ b/interface/src/ModelUploader.cpp @@ -364,7 +364,9 @@ void ModelUploader::send() { _progressBar = NULL; } -void ModelUploader::checkJSON(const QJsonObject& jsonResponse) { +void ModelUploader::checkJSON(QNetworkReply& requestReply) { + QJsonObject jsonResponse = QJsonDocument::fromJson(requestReply.readAll()).object(); + if (jsonResponse.contains("status") && jsonResponse.value("status").toString() == "success") { qDebug() << "status : success"; JSONCallbackParameters callbackParams; @@ -426,7 +428,7 @@ void ModelUploader::uploadUpdate(qint64 bytesSent, qint64 bytesTotal) { } } -void ModelUploader::uploadSuccess(const QJsonObject& jsonResponse) { +void ModelUploader::uploadSuccess(QNetworkReply& requestReply) { if (_progressDialog) { _progressDialog->accept(); } diff --git a/interface/src/ModelUploader.h b/interface/src/ModelUploader.h index a1c7a27393..7d8ad2b526 100644 --- a/interface/src/ModelUploader.h +++ b/interface/src/ModelUploader.h @@ -40,9 +40,9 @@ public slots: void send(); private slots: - void checkJSON(const QJsonObject& jsonResponse); + void checkJSON(QNetworkReply& requestReply); void uploadUpdate(qint64 bytesSent, qint64 bytesTotal); - void uploadSuccess(const QJsonObject& jsonResponse); + void uploadSuccess(QNetworkReply& requestReply); void uploadFailed(QNetworkReply& errorReply); void checkS3(); void processCheck(); diff --git a/interface/src/PaymentManager.cpp b/interface/src/PaymentManager.cpp deleted file mode 100644 index a0c05f34b3..0000000000 --- a/interface/src/PaymentManager.cpp +++ /dev/null @@ -1,64 +0,0 @@ -// -// PaymentManager.cpp -// interface/src -// -// Created by Stephen Birarda on 2014-07-30. -// 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 -#include -#include - -#include -#include -#include - -#include "SignedWalletTransaction.h" - -#include "PaymentManager.h" - -PaymentManager& PaymentManager::getInstance() { - static PaymentManager sharedInstance; - return sharedInstance; -} - -void PaymentManager::sendSignedPayment(qint64 satoshiAmount, const QUuid& nodeUUID, const QUuid& destinationWalletUUID) { - - QByteArray walletPrivateKeyByteArray = Menu::getInstance()->getWalletPrivateKey(); - - if (!walletPrivateKeyByteArray.isEmpty()) { - // setup a signed wallet transaction - const qint64 DEFAULT_TRANSACTION_EXPIRY_SECONDS = 60; - qint64 currentTimestamp = QDateTime::currentDateTimeUtc().toTime_t(); - SignedWalletTransaction newTransaction(destinationWalletUUID, satoshiAmount, - currentTimestamp, DEFAULT_TRANSACTION_EXPIRY_SECONDS); - - // send the signed transaction to the redeeming node - QByteArray transactionByteArray = byteArrayWithPopulatedHeader(PacketTypeSignedTransactionPayment); - - // append the binary message and the signed message digest - transactionByteArray.append(newTransaction.binaryMessage()); - QByteArray signedMessageDigest = newTransaction.signedMessageDigest(); - - if (!signedMessageDigest.isEmpty()) { - transactionByteArray.append(signedMessageDigest); - - qDebug() << "Paying" << satoshiAmount << "satoshis to" << destinationWalletUUID << "via" << nodeUUID; - - // use the NodeList to send that to the right node - NodeList* nodeList = NodeList::getInstance(); - nodeList->writeDatagram(transactionByteArray, nodeList->nodeWithUUID(nodeUUID)); - } else { - qDebug() << "Payment of" << satoshiAmount << "satoshis to" << destinationWalletUUID << "via" << nodeUUID << - "cannot be sent because there was an error signing the transaction."; - } - - } else { - qDebug() << "Payment of" << satoshiAmount << "satoshis to" << destinationWalletUUID << "via" << nodeUUID << - "cannot be sent because there is no stored wallet private key."; - } -} \ No newline at end of file diff --git a/interface/src/PaymentManager.h b/interface/src/PaymentManager.h deleted file mode 100644 index 67419a39a4..0000000000 --- a/interface/src/PaymentManager.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// PaymentManager.h -// interface/src -// -// Created by Stephen Birarda on 2014-07-30. -// 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_PaymentManager_h -#define hifi_PaymentManager_h - -#include - -class PaymentManager : public QObject { - Q_OBJECT -public: - static PaymentManager& getInstance(); -public slots: - void sendSignedPayment(qint64 satoshiAmount, const QUuid& nodeUUID, const QUuid& destinationWalletUUID); -}; - -#endif // hifi_PaymentManager_h \ No newline at end of file diff --git a/interface/src/SignedWalletTransaction.cpp b/interface/src/SignedWalletTransaction.cpp deleted file mode 100644 index 8a0d4e6fdb..0000000000 --- a/interface/src/SignedWalletTransaction.cpp +++ /dev/null @@ -1,86 +0,0 @@ -// -// SignedWalletTransaction.cpp -// interface/src -// -// Created by Stephen Birarda on 2014-07-11. -// 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 -#include -#include - -#include -#include -#include - -#include - -#include "Menu.h" - -#include "SignedWalletTransaction.h" - -SignedWalletTransaction::SignedWalletTransaction(const QUuid& destinationUUID, qint64 amount, - qint64 messageTimestamp, qint64 expiryDelta) : - WalletTransaction(destinationUUID, amount), - _messageTimestamp(messageTimestamp), - _expiryDelta(expiryDelta) -{ - -} - -QByteArray SignedWalletTransaction::binaryMessage() { - // build the message using the components of this transaction - - // UUID, source UUID, destination UUID, message timestamp, expiry delta, amount - QByteArray messageBinary; - - messageBinary.append(_uuid.toRfc4122()); - - messageBinary.append(reinterpret_cast(&_messageTimestamp), sizeof(_messageTimestamp)); - messageBinary.append(reinterpret_cast(&_expiryDelta), sizeof(_expiryDelta)); - - messageBinary.append(AccountManager::getInstance().getAccountInfo().getWalletID().toRfc4122()); - - messageBinary.append(_destinationUUID.toRfc4122()); - - messageBinary.append(reinterpret_cast(&_amount), sizeof(_amount)); - - return messageBinary; -} - -QByteArray SignedWalletTransaction::hexMessage() { - return binaryMessage().toHex(); -} - -QByteArray SignedWalletTransaction::messageDigest() { - return QCryptographicHash::hash(hexMessage(), QCryptographicHash::Sha256).toHex(); -} - -QByteArray SignedWalletTransaction::signedMessageDigest() { - // pull the current private key from menu into RSA structure in memory - QByteArray privateKeyByteArray = Menu::getInstance()->getWalletPrivateKey(); - - BIO* privateKeyBIO = NULL; - RSA* rsaPrivateKey = NULL; - - privateKeyBIO = BIO_new_mem_buf(privateKeyByteArray.data(), privateKeyByteArray.size()); - PEM_read_bio_RSAPrivateKey(privateKeyBIO, &rsaPrivateKey, NULL, NULL); - - QByteArray digestToEncrypt = messageDigest(); - QByteArray encryptedDigest(RSA_size(rsaPrivateKey), 0); - - int encryptReturn = RSA_private_encrypt(digestToEncrypt.size(), - reinterpret_cast(digestToEncrypt.constData()), - reinterpret_cast(encryptedDigest.data()), - rsaPrivateKey, RSA_PKCS1_PADDING); - - // free the two structures used - BIO_free(privateKeyBIO); - RSA_free(rsaPrivateKey); - - return (encryptReturn != -1) ? encryptedDigest : QByteArray(); -} \ No newline at end of file diff --git a/interface/src/SignedWalletTransaction.h b/interface/src/SignedWalletTransaction.h deleted file mode 100644 index 6bc66a535e..0000000000 --- a/interface/src/SignedWalletTransaction.h +++ /dev/null @@ -1,32 +0,0 @@ -// -// SignedWalletTransaction.h -// interfac/src -// -// Created by Stephen Birarda on 2014-07-11. -// 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_SignedWalletTransaction_h -#define hifi_SignedWalletTransaction_h - -#include - -class SignedWalletTransaction : public WalletTransaction { - Q_OBJECT -public: - SignedWalletTransaction(const QUuid& destinationUUID, qint64 amount, qint64 messageTimestamp, qint64 expiryDelta); - - QByteArray binaryMessage(); - QByteArray hexMessage(); - QByteArray messageDigest(); - QByteArray signedMessageDigest(); - -private: - qint64 _messageTimestamp; - qint64 _expiryDelta; -}; - -#endif // hifi_SignedWalletTransaction_h \ No newline at end of file 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 cd4a3939f3..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); } } 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 e110027e1a..25b8bc142d 100644 --- a/interface/src/devices/Joystick.cpp +++ b/interface/src/devices/Joystick.cpp @@ -15,15 +15,15 @@ #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) : - _instanceId(instanceId), - _name(name), _sdlGameController(sdlGameController), _sdlJoystick(SDL_GameControllerGetJoystick(_sdlGameController)), + _instanceId(instanceId), + _name(name), _axes(QVector(SDL_JoystickNumAxes(_sdlJoystick))), _buttons(QVector(SDL_JoystickNumButtons(_sdlJoystick))) { 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/Model.cpp b/interface/src/renderer/Model.cpp index 55eefb0bde..1228a458b2 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,353 @@ 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; + + 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 +1634,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 +1678,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 +1726,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 +1823,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/ui/OAuthWebViewHandler.cpp b/interface/src/ui/OAuthWebViewHandler.cpp deleted file mode 100644 index 86db54afb4..0000000000 --- a/interface/src/ui/OAuthWebViewHandler.cpp +++ /dev/null @@ -1,168 +0,0 @@ -// -// OAuthWebViewHandler.cpp -// interface/src/ui -// -// Created by Stephen Birarda on 2014-05-01. -// 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 -#include - -#include - -#include "Application.h" - -#include "OAuthWebViewHandler.h" - -OAuthWebViewHandler& OAuthWebViewHandler::getInstance() { - static OAuthWebViewHandler sharedInstance; - return sharedInstance; -} - -OAuthWebViewHandler::OAuthWebViewHandler() : - _activeWebView(NULL), - _webViewRedisplayTimer(), - _lastAuthorizationURL() -{ - addHighFidelityRootCAToSSLConfig(); -} - -const char HIGH_FIDELITY_CA[] = "-----BEGIN CERTIFICATE-----\n" - "MIID6TCCA1KgAwIBAgIJANlfRkRD9A8bMA0GCSqGSIb3DQEBBQUAMIGqMQswCQYD\n" - "VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5j\n" - "aXNjbzEbMBkGA1UEChMSSGlnaCBGaWRlbGl0eSwgSW5jMRMwEQYDVQQLEwpPcGVy\n" - "YXRpb25zMRgwFgYDVQQDEw9oaWdoZmlkZWxpdHkuaW8xIjAgBgkqhkiG9w0BCQEW\n" - "E29wc0BoaWdoZmlkZWxpdHkuaW8wHhcNMTQwMzI4MjIzMzM1WhcNMjQwMzI1MjIz\n" - "MzM1WjCBqjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNV\n" - "BAcTDVNhbiBGcmFuY2lzY28xGzAZBgNVBAoTEkhpZ2ggRmlkZWxpdHksIEluYzET\n" - "MBEGA1UECxMKT3BlcmF0aW9uczEYMBYGA1UEAxMPaGlnaGZpZGVsaXR5LmlvMSIw\n" - "IAYJKoZIhvcNAQkBFhNvcHNAaGlnaGZpZGVsaXR5LmlvMIGfMA0GCSqGSIb3DQEB\n" - "AQUAA4GNADCBiQKBgQDyo1euYiPPEdnvDZnIjWrrP230qUKMSj8SWoIkbTJF2hE8\n" - "2eP3YOgbgSGBzZ8EJBxIOuNmj9g9Eg6691hIKFqy5W0BXO38P04Gg+pVBvpHFGBi\n" - "wpqGbfsjaUDuYmBeJRcMO0XYkLCRQG+lAQNHoFDdItWAJfC3FwtP3OCDnz8cNwID\n" - "AQABo4IBEzCCAQ8wHQYDVR0OBBYEFCSv2kmiGg6VFMnxXzLDNP304cPAMIHfBgNV\n" - "HSMEgdcwgdSAFCSv2kmiGg6VFMnxXzLDNP304cPAoYGwpIGtMIGqMQswCQYDVQQG\n" - "EwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNj\n" - "bzEbMBkGA1UEChMSSGlnaCBGaWRlbGl0eSwgSW5jMRMwEQYDVQQLEwpPcGVyYXRp\n" - "b25zMRgwFgYDVQQDEw9oaWdoZmlkZWxpdHkuaW8xIjAgBgkqhkiG9w0BCQEWE29w\n" - "c0BoaWdoZmlkZWxpdHkuaW+CCQDZX0ZEQ/QPGzAMBgNVHRMEBTADAQH/MA0GCSqG\n" - "SIb3DQEBBQUAA4GBAEkQl3p+lH5vuoCNgyfa67nL0MsBEt+5RSBOgjwCjjASjzou\n" - "FTv5w0he2OypgMQb8i/BYtS1lJSFqjPJcSM1Salzrm3xDOK5pOXJ7h6SQLPDVEyf\n" - "Hy2/9d/to+99+SOUlvfzfgycgjOc+s/AV7Y+GBd7uzGxUdrN4egCZW1F6/mH\n" - "-----END CERTIFICATE-----\n"; - -void OAuthWebViewHandler::addHighFidelityRootCAToSSLConfig() { - QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration(); - - // add the High Fidelity root CA to the list of trusted CA certificates - QByteArray highFidelityCACertificate(HIGH_FIDELITY_CA, sizeof(HIGH_FIDELITY_CA)); - sslConfig.setCaCertificates(sslConfig.caCertificates() + QSslCertificate::fromData(highFidelityCACertificate)); - - // set the modified configuration - QSslConfiguration::setDefaultConfiguration(sslConfig); -} - -const int WEB_VIEW_REDISPLAY_ELAPSED_MSECS = 5 * 1000; - -void OAuthWebViewHandler::displayWebviewForAuthorizationURL(const QUrl& authorizationURL) { - if (!_activeWebView) { - - if (!_lastAuthorizationURL.isEmpty()) { - if (_lastAuthorizationURL.host() == authorizationURL.host() - && _webViewRedisplayTimer.elapsed() < WEB_VIEW_REDISPLAY_ELAPSED_MSECS) { - // this would be re-displaying an OAuth dialog for the same auth URL inside of the redisplay ms - // so return instead - return; - } - } - - _lastAuthorizationURL = authorizationURL; - - _activeWebView = new QWebView; - - // keep the window on top and delete it when it closes - _activeWebView->setWindowFlags(Qt::Sheet); - _activeWebView->setAttribute(Qt::WA_DeleteOnClose); - - qDebug() << "Displaying QWebView for OAuth authorization at" << authorizationURL.toString(); - - AccountManager& accountManager = AccountManager::getInstance(); - - QUrl codedAuthorizationURL = authorizationURL; - - // check if we have an access token for this host - if so we can bypass login by adding it to the URL - if (accountManager.getAuthURL().host() == authorizationURL.host() - && accountManager.hasValidAccessToken()) { - - const QString ACCESS_TOKEN_QUERY_STRING_KEY = "access_token"; - - QUrlQuery authQuery(codedAuthorizationURL); - authQuery.addQueryItem(ACCESS_TOKEN_QUERY_STRING_KEY, accountManager.getAccountInfo().getAccessToken().token); - - codedAuthorizationURL.setQuery(authQuery); - } - - connect(_activeWebView.data(), &QWebView::urlChanged, this, &OAuthWebViewHandler::handleURLChanged); - - _activeWebView->load(codedAuthorizationURL); - - connect(_activeWebView->page()->networkAccessManager(), &QNetworkAccessManager::sslErrors, - this, &OAuthWebViewHandler::handleSSLErrors); - connect(_activeWebView->page()->networkAccessManager(), &QNetworkAccessManager::finished, - this, &OAuthWebViewHandler::handleReplyFinished); - connect(_activeWebView.data(), &QWebView::loadFinished, this, &OAuthWebViewHandler::handleLoadFinished); - - // connect to the destroyed signal so after the web view closes we can start a timer - connect(_activeWebView.data(), &QWebView::destroyed, this, &OAuthWebViewHandler::handleWebViewDestroyed); - } -} - -void OAuthWebViewHandler::handleSSLErrors(QNetworkReply* networkReply, const QList& errorList) { - qDebug() << "SSL Errors:" << errorList; -} - -void OAuthWebViewHandler::handleLoadFinished(bool success) { - if (success && _activeWebView->url().host() == NodeList::getInstance()->getDomainHandler().getHostname()) { - qDebug() << "OAuth authorization code passed successfully to domain-server."; - - // grab the UUID that is set as the state parameter in the auth URL - // since that is our new session UUID - QUrlQuery authQuery(_activeWebView->url()); - - const QString AUTH_STATE_QUERY_KEY = "state"; - NodeList::getInstance()->setSessionUUID(QUuid(authQuery.queryItemValue(AUTH_STATE_QUERY_KEY))); - - _activeWebView->close(); - _activeWebView = NULL; - } -} - -void OAuthWebViewHandler::handleReplyFinished(QNetworkReply* reply) { - if (_activeWebView && reply->error() != QNetworkReply::NoError) { - qDebug() << "Error loading" << reply->url() << "-" << reply->errorString(); - _activeWebView->close(); - } -} - -void OAuthWebViewHandler::handleWebViewDestroyed(QObject* destroyedObject) { - _webViewRedisplayTimer.restart(); -} - -void OAuthWebViewHandler::handleURLChanged(const QUrl& url) { - // check if this is the authorization screen - if it is then we need to show the OAuthWebViewHandler - const QString ACCESS_TOKEN_URL_REGEX_STRING = "redirect_uri=[\\w:\\/\\.]+&access_token="; - QRegExp accessTokenRegex(ACCESS_TOKEN_URL_REGEX_STRING); - - if (accessTokenRegex.indexIn(url.toString()) != -1) { - _activeWebView->show(); - } else if (url.toString() == DEFAULT_NODE_AUTH_URL.toString() + "/login") { - // this is a login request - we're going to close the webview and signal the AccountManager that we need a login - qDebug() << "data-server replied with login request. Signalling that login is required to proceed with OAuth."; - _activeWebView->close(); - AccountManager::getInstance().checkAndSignalForAccessToken(); - } -} diff --git a/interface/src/ui/OAuthWebViewHandler.h b/interface/src/ui/OAuthWebViewHandler.h deleted file mode 100644 index 1a95f17dfd..0000000000 --- a/interface/src/ui/OAuthWebViewHandler.h +++ /dev/null @@ -1,43 +0,0 @@ -// -// OAuthWebviewHandler.h -// interface/src/ui -// -// Created by Stephen Birarda on 2014-05-01. -// 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_OAuthWebviewHandler_h -#define hifi_OAuthWebviewHandler_h - -#include - -class QWebView; - -class OAuthWebViewHandler : public QObject { - Q_OBJECT -public: - OAuthWebViewHandler(); - static OAuthWebViewHandler& getInstance(); - static void addHighFidelityRootCAToSSLConfig(); - - void clearLastAuthorizationURL() { _lastAuthorizationURL = QUrl(); } - -public slots: - void displayWebviewForAuthorizationURL(const QUrl& authorizationURL); - -private slots: - void handleSSLErrors(QNetworkReply* networkReply, const QList& errorList); - void handleLoadFinished(bool success); - void handleReplyFinished(QNetworkReply* reply); - void handleWebViewDestroyed(QObject* destroyedObject); - void handleURLChanged(const QUrl& url); -private: - QPointer _activeWebView; - QElapsedTimer _webViewRedisplayTimer; - QUrl _lastAuthorizationURL; -}; - -#endif // hifi_OAuthWebviewHandler_h \ No newline at end of file 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/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/networking/CMakeLists.txt b/libraries/networking/CMakeLists.txt index 501437fab2..e4dd37234b 100644 --- a/libraries/networking/CMakeLists.txt +++ b/libraries/networking/CMakeLists.txt @@ -10,5 +10,19 @@ if (WIN32) target_link_libraries(${TARGET_NAME} ws2_32.lib) endif () +# find OpenSSL +find_package(OpenSSL REQUIRED) + +if (APPLE AND ${OPENSSL_INCLUDE_DIR} STREQUAL "/usr/include") + # this is a user on OS X using system OpenSSL, which is going to throw warnings since they're deprecating for their common crypto + message(WARNING "The found version of OpenSSL is the OS X system version. This will produce deprecation warnings." + "\nWe recommend you install a newer version (at least 1.0.1h) in a different directory and set OPENSSL_ROOT_DIR in your env so Cmake can find it.") +endif () + +include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}") + +# append OpenSSL to our list of libraries to link +list(APPEND ${TARGET_NAME}_LIBRARIES_TO_LINK "${OPENSSL_LIBRARIES}") + # call macro to link our dependencies and bubble them up via a property on our target link_shared_dependencies() \ No newline at end of file diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index bb471442ea..650e0e3321 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -19,9 +19,11 @@ #include #include #include +#include #include "NodeList.h" #include "PacketHeaders.h" +#include "RSAKeypairGenerator.h" #include "AccountManager.h" @@ -144,6 +146,12 @@ void AccountManager::setAuthURL(const QUrl& authURL) { } else { requestProfile(); } + + // if we don't have a private key in settings we should generate a new keypair + if (!_accountInfo.hasPrivateKey()) { + qDebug() << "No private key present - generating a new key-pair."; + generateNewKeypair(); + } } } } @@ -280,14 +288,12 @@ void AccountManager::processReply() { } void AccountManager::passSuccessToCallback(QNetworkReply* requestReply) { - QJsonDocument jsonResponse = QJsonDocument::fromJson(requestReply->readAll()); - JSONCallbackParameters callbackParams = _pendingCallbackMap.value(requestReply); if (callbackParams.jsonCallbackReceiver) { // invoke the right method on the callback receiver QMetaObject::invokeMethod(callbackParams.jsonCallbackReceiver, qPrintable(callbackParams.jsonCallbackMethod), - Q_ARG(const QJsonObject&, jsonResponse.object())); + Q_ARG(QNetworkReply&, *requestReply)); // remove the related reply-callback group from the map _pendingCallbackMap.remove(requestReply); @@ -295,7 +301,7 @@ void AccountManager::passSuccessToCallback(QNetworkReply* requestReply) { } else { if (VERBOSE_HTTP_REQUEST_DEBUGGING) { qDebug() << "Received JSON response from data-server that has no matching callback."; - qDebug() << jsonResponse; + qDebug() << QJsonDocument::fromJson(requestReply->readAll()); } } } @@ -319,6 +325,16 @@ void AccountManager::passErrorToCallback(QNetworkReply* requestReply) { } } +void AccountManager::persistAccountToSettings() { + if (_shouldPersistToSettingsFile) { + // store this access token into the local settings + QSettings localSettings; + localSettings.beginGroup(ACCOUNTS_GROUP); + localSettings.setValue(_authURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE), + QVariant::fromValue(_accountInfo)); + } +} + bool AccountManager::hasValidAccessToken() { if (_accountInfo.getAccessToken().token.isEmpty() || _accountInfo.getAccessToken().isExpired()) { @@ -408,15 +424,10 @@ void AccountManager::requestAccessTokenFinished() { emit loginComplete(rootURL); - if (_shouldPersistToSettingsFile) { - // store this access token into the local settings - QSettings localSettings; - localSettings.beginGroup(ACCOUNTS_GROUP); - localSettings.setValue(rootURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE), - QVariant::fromValue(_accountInfo)); - } + persistAccountToSettings(); requestProfile(); + generateNewKeypair(); } } else { // TODO: error handling @@ -434,7 +445,7 @@ void AccountManager::requestProfile() { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QUrl profileURL = _authURL; - profileURL.setPath("/api/v1/users/profile"); + profileURL.setPath("/api/v1/user/profile"); QNetworkRequest profileRequest(profileURL); profileRequest.setRawHeader(ACCESS_TOKEN_AUTHORIZATION_HEADER, _accountInfo.getAccessToken().authorizationHeaderValue()); @@ -458,15 +469,8 @@ void AccountManager::requestProfileFinished() { // the username has changed to whatever came back emit usernameChanged(_accountInfo.getUsername()); - if (_shouldPersistToSettingsFile) { - // store the whole profile into the local settings - QUrl rootURL = profileReply->url(); - rootURL.setPath(""); - QSettings localSettings; - localSettings.beginGroup(ACCOUNTS_GROUP); - localSettings.setValue(rootURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE), - QVariant::fromValue(_accountInfo)); - } + // store the whole profile into the local settings + persistAccountToSettings(); } else { // TODO: error handling @@ -478,3 +482,57 @@ void AccountManager::requestProfileError(QNetworkReply::NetworkError error) { // TODO: error handling qDebug() << "AccountManager requestProfileError - " << error; } + +void AccountManager::generateNewKeypair() { + // setup a new QThread to generate the keypair on, in case it takes a while + QThread* generateThread = new QThread(this); + + // setup a keypair generator + RSAKeypairGenerator* keypairGenerator = new RSAKeypairGenerator(); + + connect(generateThread, &QThread::started, keypairGenerator, &RSAKeypairGenerator::generateKeypair); + connect(keypairGenerator, &RSAKeypairGenerator::generatedKeypair, this, &AccountManager::processGeneratedKeypair); + connect(keypairGenerator, &RSAKeypairGenerator::errorGeneratingKeypair, + this, &AccountManager::handleKeypairGenerationError); + connect(keypairGenerator, &QObject::destroyed, generateThread, &QThread::quit); + connect(generateThread, &QThread::finished, generateThread, &QThread::deleteLater); + + keypairGenerator->moveToThread(generateThread); + + qDebug() << "Starting worker thread to generate 2048-bit RSA key-pair."; + generateThread->start(); +} + +void AccountManager::processGeneratedKeypair(const QByteArray& publicKey, const QByteArray& privateKey) { + + qDebug() << "Generated 2048-bit RSA key-pair. Storing private key and uploading public key."; + + // set the private key on our data-server account info + _accountInfo.setPrivateKey(privateKey); + persistAccountToSettings(); + + // upload the public key so data-web has an up-to-date key + const QString PUBLIC_KEY_UPDATE_PATH = "api/v1/user/public_key"; + + // setup a multipart upload to send up the public key + QHttpMultiPart* requestMultiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + + QHttpPart keyPart; + keyPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream")); + keyPart.setHeader(QNetworkRequest::ContentDispositionHeader, + QVariant("form-data; name=\"public_key\"; filename=\"public_key\"")); + keyPart.setBody(publicKey); + + requestMultiPart->append(keyPart); + + authenticatedRequest(PUBLIC_KEY_UPDATE_PATH, QNetworkAccessManager::PutOperation, + JSONCallbackParameters(), QByteArray(), requestMultiPart); + + // get rid of the keypair generator now that we don't need it anymore + sender()->deleteLater(); +} + +void AccountManager::handleKeypairGenerationError() { + // for now there isn't anything we do with this except get the worker thread to clean up + sender()->deleteLater(); +} diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h index 3628b5a865..7b5ea8de8b 100644 --- a/libraries/networking/src/AccountManager.h +++ b/libraries/networking/src/AccountManager.h @@ -70,7 +70,7 @@ public: void requestAccessToken(const QString& login, const QString& password); void requestProfile(); - const DataServerAccountInfo& getAccountInfo() const { return _accountInfo; } + DataServerAccountInfo& getAccountInfo() { return _accountInfo; } public slots: void requestAccessTokenFinished(); @@ -91,13 +91,19 @@ signals: void balanceChanged(qint64 newBalance); private slots: void processReply(); + void handleKeypairGenerationError(); + void processGeneratedKeypair(const QByteArray& publicKey, const QByteArray& privateKey); private: AccountManager(); AccountManager(AccountManager const& other); // not implemented void operator=(AccountManager const& other); // not implemented + + void persistAccountToSettings(); void passSuccessToCallback(QNetworkReply* reply); void passErrorToCallback(QNetworkReply* reply); + + void generateNewKeypair(); Q_INVOKABLE void invokedRequest(const QString& path, bool requiresAuthentication, diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 3d7617e17b..1a7f6d3dbc 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -10,6 +10,7 @@ // #include +#include #include #include @@ -134,8 +135,9 @@ void AddressManager::handleLookupString(const QString& lookupString) { } } -void AddressManager::handleAPIResponse(const QJsonObject &jsonObject) { - QJsonObject dataObject = jsonObject["data"].toObject(); +void AddressManager::handleAPIResponse(QNetworkReply& requestReply) { + QJsonObject responseObject = QJsonDocument::fromJson(requestReply.readAll()).object(); + QJsonObject dataObject = responseObject["data"].toObject(); const QString ADDRESS_API_DOMAIN_KEY = "domain"; const QString ADDRESS_API_ONLINE_KEY = "online"; @@ -190,7 +192,7 @@ void AddressManager::handleAPIResponse(const QJsonObject &jsonObject) { } else { qDebug() << "Received an address manager API response with no domain key. Cannot parse."; - qDebug() << jsonObject; + qDebug() << responseObject; } } else { // we've been told that this result exists but is offline, emit our signal so the application can handle diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index fe6219b3f7..bc0f2716dd 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -42,7 +42,7 @@ public: public slots: void handleLookupString(const QString& lookupString); - void handleAPIResponse(const QJsonObject& jsonObject); + void handleAPIResponse(QNetworkReply& requestReply); void handleAPIError(QNetworkReply& errorReply); void goToUser(const QString& username); signals: diff --git a/libraries/networking/src/DataServerAccountInfo.cpp b/libraries/networking/src/DataServerAccountInfo.cpp index 9bdb012f70..600eb57166 100644 --- a/libraries/networking/src/DataServerAccountInfo.cpp +++ b/libraries/networking/src/DataServerAccountInfo.cpp @@ -9,6 +9,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + +#include #include #include "DataServerAccountInfo.h" @@ -20,7 +23,9 @@ DataServerAccountInfo::DataServerAccountInfo() : _discourseApiKey(), _walletID(), _balance(0), - _hasBalance(false) + _hasBalance(false), + _privateKey(), + _usernameSignature() { } @@ -33,6 +38,7 @@ DataServerAccountInfo::DataServerAccountInfo(const DataServerAccountInfo& otherI _walletID = otherInfo._walletID; _balance = otherInfo._balance; _hasBalance = otherInfo._hasBalance; + _privateKey = otherInfo._privateKey; } DataServerAccountInfo& DataServerAccountInfo::operator=(const DataServerAccountInfo& otherInfo) { @@ -51,6 +57,7 @@ void DataServerAccountInfo::swap(DataServerAccountInfo& otherInfo) { swap(_walletID, otherInfo._walletID); swap(_balance, otherInfo._balance); swap(_hasBalance, otherInfo._hasBalance); + swap(_privateKey, otherInfo._privateKey); } void DataServerAccountInfo::setAccessTokenFromJSON(const QJsonObject& jsonObject) { @@ -61,6 +68,9 @@ void DataServerAccountInfo::setUsername(const QString& username) { if (_username != username) { _username = username; + // clear our username signature so it has to be re-created + _usernameSignature = QByteArray(); + qDebug() << "Username changed to" << username; } } @@ -92,7 +102,8 @@ void DataServerAccountInfo::setBalance(qint64 balance) { } } -void DataServerAccountInfo::setBalanceFromJSON(const QJsonObject& jsonObject) { +void DataServerAccountInfo::setBalanceFromJSON(QNetworkReply& requestReply) { + QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object(); if (jsonObject["status"].toString() == "success") { qint64 balanceInSatoshis = jsonObject["data"].toObject()["wallet"].toObject()["balance"].toDouble(); setBalance(balanceInSatoshis); @@ -111,12 +122,55 @@ void DataServerAccountInfo::setProfileInfoFromJSON(const QJsonObject& jsonObject setWalletID(QUuid(user["wallet_id"].toString())); } +const QByteArray& DataServerAccountInfo::getUsernameSignature() { + if (_usernameSignature.isEmpty()) { + if (!_privateKey.isEmpty()) { + const char* privateKeyData = _privateKey.constData(); + RSA* rsaPrivateKey = d2i_RSAPrivateKey(NULL, + reinterpret_cast(&privateKeyData), + _privateKey.size()); + if (rsaPrivateKey) { + QByteArray usernameByteArray = _username.toUtf8(); + _usernameSignature.resize(RSA_size(rsaPrivateKey)); + + int encryptReturn = RSA_private_encrypt(usernameByteArray.size(), + reinterpret_cast(usernameByteArray.constData()), + reinterpret_cast(_usernameSignature.data()), + rsaPrivateKey, RSA_PKCS1_PADDING); + + if (encryptReturn == -1) { + qDebug() << "Error encrypting username signature."; + qDebug() << "Will re-attempt on next domain-server check in."; + _usernameSignature = QByteArray(); + } + + // free the private key RSA struct now that we are done with it + RSA_free(rsaPrivateKey); + } else { + qDebug() << "Could not create RSA struct from QByteArray private key."; + qDebug() << "Will re-attempt on next domain-server check in."; + } + } + } + + return _usernameSignature; +} + +void DataServerAccountInfo::setPrivateKey(const QByteArray& privateKey) { + _privateKey = privateKey; + + // clear our username signature so it has to be re-created + _usernameSignature = QByteArray(); +} + QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info) { - out << info._accessToken << info._username << info._xmppPassword << info._discourseApiKey << info._walletID; + out << info._accessToken << info._username << info._xmppPassword << info._discourseApiKey + << info._walletID << info._privateKey; return out; } QDataStream& operator>>(QDataStream &in, DataServerAccountInfo& info) { - in >> info._accessToken >> info._username >> info._xmppPassword >> info._discourseApiKey >> info._walletID; + in >> info._accessToken >> info._username >> info._xmppPassword >> info._discourseApiKey + >> info._walletID >> info._privateKey; return in; } diff --git a/libraries/networking/src/DataServerAccountInfo.h b/libraries/networking/src/DataServerAccountInfo.h index 91675594ad..9804bd755c 100644 --- a/libraries/networking/src/DataServerAccountInfo.h +++ b/libraries/networking/src/DataServerAccountInfo.h @@ -13,6 +13,7 @@ #define hifi_DataServerAccountInfo_h #include +#include #include #include "OAuthAccessToken.h" @@ -41,13 +42,17 @@ public: const QUuid& getWalletID() const { return _walletID; } void setWalletID(const QUuid& walletID); + + const QByteArray& getUsernameSignature(); + bool hasPrivateKey() const { return !_privateKey.isEmpty(); } + void setPrivateKey(const QByteArray& privateKey); qint64 getBalance() const { return _balance; } float getBalanceInSatoshis() const { return _balance / SATOSHIS_PER_CREDIT; } void setBalance(qint64 balance); bool hasBalance() const { return _hasBalance; } void setHasBalance(bool hasBalance) { _hasBalance = hasBalance; } - Q_INVOKABLE void setBalanceFromJSON(const QJsonObject& jsonObject); + Q_INVOKABLE void setBalanceFromJSON(QNetworkReply& requestReply); bool hasProfile() const; @@ -67,6 +72,8 @@ private: QUuid _walletID; qint64 _balance; bool _hasBalance; + QByteArray _privateKey; + QByteArray _usernameSignature; }; #endif // hifi_DataServerAccountInfo_h diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index dd33c96d74..919dc75c23 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -679,7 +679,6 @@ void LimitedNodeList::updateLocalSockAddr() { qDebug() << "Local socket has changed from" << _localSockAddr << "to" << newSockAddr; } - _localSockAddr = newSockAddr; emit localSockAddrChanged(_localSockAddr); diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index c416773201..73381d01a5 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -101,6 +101,8 @@ public: const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket); SharedNodePointer updateSocketsForNode(const QUuid& uuid, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket); + + const HifiSockAddr& getLocalSockAddr() const { return _localSockAddr; } void processNodeData(const HifiSockAddr& senderSockAddr, const QByteArray& packet); void processKillNode(const QByteArray& datagram); diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 25531861ca..f8da7cdbfa 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -302,11 +302,20 @@ void NodeList::sendDomainServerCheckIn() { QDataStream packetStream(&domainServerPacket, QIODevice::Append); // pack our data to send to the domain-server - packetStream << _ownerType << _publicSockAddr << _localSockAddr << (quint8) _nodeTypesOfInterest.size(); + packetStream << _ownerType << _publicSockAddr << _localSockAddr << _nodeTypesOfInterest.toList(); - // copy over the bytes for node types of interest, if required - foreach (NodeType_t nodeTypeOfInterest, _nodeTypesOfInterest) { - packetStream << nodeTypeOfInterest; + + // if this is a connect request, and we can present a username signature, send it along + if (!_domainHandler.isConnected()) { + DataServerAccountInfo& accountInfo = AccountManager::getInstance().getAccountInfo(); + packetStream << accountInfo.getUsername(); + + const QByteArray& usernameSignature = AccountManager::getInstance().getAccountInfo().getUsernameSignature(); + + if (!usernameSignature.isEmpty()) { + qDebug() << "Including username signature in domain connect request."; + packetStream << usernameSignature; + } } if (!isUsingDTLS) { diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index e3bfbb8f84..be4fb68bfb 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -114,7 +114,7 @@ QString nameForPacketType(PacketType type) { PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainListRequest); PACKET_TYPE_NAME_LOOKUP(PacketTypeRequestAssignment); PACKET_TYPE_NAME_LOOKUP(PacketTypeCreateAssignment); - PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainOAuthRequest); + PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainConnectionDenied); PACKET_TYPE_NAME_LOOKUP(PacketTypeMuteEnvironment); PACKET_TYPE_NAME_LOOKUP(PacketTypeAudioStreamStats); PACKET_TYPE_NAME_LOOKUP(PacketTypeDataServerConfirm); diff --git a/libraries/networking/src/PacketHeaders.h b/libraries/networking/src/PacketHeaders.h index 2d298d253d..ab596673f5 100644 --- a/libraries/networking/src/PacketHeaders.h +++ b/libraries/networking/src/PacketHeaders.h @@ -38,7 +38,7 @@ enum PacketType { PacketTypeDomainListRequest, PacketTypeRequestAssignment, PacketTypeCreateAssignment, - PacketTypeDomainOAuthRequest, + PacketTypeDomainConnectionDenied, PacketTypeMuteEnvironment, PacketTypeAudioStreamStats, PacketTypeDataServerConfirm, @@ -81,7 +81,7 @@ typedef char PacketVersion; const QSet NON_VERIFIED_PACKETS = QSet() << PacketTypeDomainServerRequireDTLS << PacketTypeDomainConnectRequest - << PacketTypeDomainList << PacketTypeDomainListRequest << PacketTypeDomainOAuthRequest + << PacketTypeDomainList << PacketTypeDomainListRequest << PacketTypeDomainConnectionDenied << PacketTypeCreateAssignment << PacketTypeRequestAssignment << PacketTypeStunResponse << PacketTypeNodeJsonStats << PacketTypeVoxelQuery << PacketTypeParticleQuery << PacketTypeEntityQuery << PacketTypeOctreeDataNack << PacketTypeVoxelEditNack << PacketTypeParticleEditNack << PacketTypeEntityEditNack diff --git a/libraries/networking/src/RSAKeypairGenerator.cpp b/libraries/networking/src/RSAKeypairGenerator.cpp new file mode 100644 index 0000000000..a51a17c0ca --- /dev/null +++ b/libraries/networking/src/RSAKeypairGenerator.cpp @@ -0,0 +1,91 @@ +// +// RSAKeypairGenerator.cpp +// libraries/networking/src +// +// Created by Stephen Birarda on 2014-10-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 +#include +#include + +#include + +#include "RSAKeypairGenerator.h" + +RSAKeypairGenerator::RSAKeypairGenerator(QObject* parent) : + QObject(parent) +{ + +} + +void RSAKeypairGenerator::generateKeypair() { + + RSA* keyPair = RSA_new(); + BIGNUM* exponent = BN_new(); + + const unsigned long RSA_KEY_EXPONENT = 65537; + BN_set_word(exponent, RSA_KEY_EXPONENT); + + // seed the random number generator before we call RSA_generate_key_ex + srand(time(NULL)); + + const int RSA_KEY_BITS = 2048; + + if (!RSA_generate_key_ex(keyPair, RSA_KEY_BITS, exponent, NULL)) { + qDebug() << "Error generating 2048-bit RSA Keypair -" << ERR_get_error(); + + emit errorGeneratingKeypair(); + + // we're going to bust out of here but first we cleanup the BIGNUM + BN_free(exponent); + return; + } + + // we don't need the BIGNUM anymore so clean that up + BN_free(exponent); + + // grab the public key and private key from the file + unsigned char* publicKeyDER = NULL; + int publicKeyLength = i2d_RSAPublicKey(keyPair, &publicKeyDER); + + unsigned char* privateKeyDER = NULL; + int privateKeyLength = i2d_RSAPrivateKey(keyPair, &privateKeyDER); + + if (publicKeyLength <= 0 || privateKeyLength <= 0) { + qDebug() << "Error getting DER public or private key from RSA struct -" << ERR_get_error(); + + emit errorGeneratingKeypair(); + + // cleanup the RSA struct + RSA_free(keyPair); + + // cleanup the public and private key DER data, if required + if (publicKeyLength > 0) { + OPENSSL_free(publicKeyDER); + } + + if (privateKeyLength > 0) { + OPENSSL_free(privateKeyDER); + } + + return; + } + + // we have the public key and private key in memory + // we can cleanup the RSA struct before we continue on + RSA_free(keyPair); + + QByteArray publicKeyArray(reinterpret_cast(publicKeyDER), publicKeyLength); + QByteArray privateKeyArray(reinterpret_cast(privateKeyDER), privateKeyLength); + + // cleanup the publicKeyDER and publicKeyDER data + OPENSSL_free(publicKeyDER); + OPENSSL_free(privateKeyDER); + + emit generatedKeypair(publicKeyArray, privateKeyArray); +} \ No newline at end of file diff --git a/libraries/networking/src/RSAKeypairGenerator.h b/libraries/networking/src/RSAKeypairGenerator.h new file mode 100644 index 0000000000..dd90313625 --- /dev/null +++ b/libraries/networking/src/RSAKeypairGenerator.h @@ -0,0 +1,28 @@ +// +// RSAKeypairGenerator.h +// libraries/networking/src +// +// Created by Stephen Birarda on 2014-10-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_RSAKeypairGenerator_h +#define hifi_RSAKeypairGenerator_h + +#include + +class RSAKeypairGenerator : public QObject { + Q_OBJECT +public: + RSAKeypairGenerator(QObject* parent = 0); +public slots: + void generateKeypair(); +signals: + void errorGeneratingKeypair(); + void generatedKeypair(const QByteArray& publicKey, const QByteArray& privateKey); +}; + +#endif // hifi_RSAKeypairGenerator_h \ No newline at end of file diff --git a/libraries/networking/src/UserActivityLogger.cpp b/libraries/networking/src/UserActivityLogger.cpp index 549f02ae3c..4fc6905e3a 100644 --- a/libraries/networking/src/UserActivityLogger.cpp +++ b/libraries/networking/src/UserActivityLogger.cpp @@ -69,7 +69,7 @@ void UserActivityLogger::logAction(QString action, QJsonObject details, JSONCall multipart); } -void UserActivityLogger::requestFinished(const QJsonObject& object) { +void UserActivityLogger::requestFinished(QNetworkReply& requestReply) { // qDebug() << object; } diff --git a/libraries/networking/src/UserActivityLogger.h b/libraries/networking/src/UserActivityLogger.h index 1bd966d632..9b100461ee 100644 --- a/libraries/networking/src/UserActivityLogger.h +++ b/libraries/networking/src/UserActivityLogger.h @@ -39,7 +39,7 @@ public slots: void wentTo(QString destinationType, QString destinationName); private slots: - void requestFinished(const QJsonObject& object); + void requestFinished(QNetworkReply& requestReply); void requestError(QNetworkReply& errorReply); private: 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)); + } } } }
+/-
" if (isArray) { - colIsArray = _.isArray(row) - colValue = colIsArray ? row : row[col.name] + rowIsObject = setting.columns.length > 1 + colValue = rowIsObject ? row[col.name] : row html += colValue // for arrays we add a hidden input to this td so that values can be posted appropriately html += "" + + (rowIsObject ? "." + col.name : "") + "' value='" + colValue + "'/>" } else if (row.hasOwnProperty(col.name)) { html += row[col.name] } @@ -293,7 +317,13 @@ function makeTable(setting, setting_name, setting_value) { html += "